Project import generated by Copybara.

NOKEYCHECK=True
GitOrigin-RevId: 85ee5a08dff955736188429c7f4e614472914288
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..2415029
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,4 @@
+.gitignore		export-ignore
+.gitattributes		export-ignore
+
+update_version		export-ignore
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..b2cb2d3
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,5 @@
+Gregory Maxwell (greg@xiph.org)
+Jean-Marc Valin - Original opusdec implementation / Resampler
+Thorvald Natvig - Resampler
+Michael Smith   - Ogginfo (basis of opusinfo)
+John Edwards    - Windows audio output
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..cf01f8f
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,371 @@
+
+Opus-tools, with the exception of opusinfo.[ch] is available under
+the following two clause BSD-style license:
+
+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 COPYRIGHT OWNER
+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.
+
+
+Opusinfo is a fork of ogginfo from the vorbis-tools package
+(http://www.xiph.org). It is available under the GPL:
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..afe1ba8
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,3 @@
+2012-05-16  Gregory Maxwell  <greg@xiph.org>
+
+        * Initial public version, 0.1.0
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..2f1ac76
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,110 @@
+ACLOCAL_AMFLAGS = -I m4
+
+#AUTOMAKE_OPTIONS = subdir-objects 1.6 dist-zip
+AUTOMAKE_OPTIONS = subdir-objects 1.11 dist-zip dist-xz
+
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/win32 -D_FORTIFY_SOURCE=2
+AM_CFLAGS = $(OPUS_CFLAGS) $(OGG_CFLAGS)
+
+bin_PROGRAMS = opusenc opusdec opusinfo
+noinst_PROGRAMS = opusrtp
+
+noinst_HEADERS = src/arch.h \
+                 src/diag_range.h \
+                 src/flac.h \
+                 src/info_opus.h \
+                 src/lpc.h \
+                 src/opusenc.h \
+                 src/opus_header.h \
+                 src/opusinfo.h \
+                 src/os_support.h \
+                 src/picture.h \
+                 src/resample_sse.h \
+                 src/speex_resampler.h \
+                 src/stack_alloc.h \
+                 win32/unicode_support.h \
+                 src/cpusupport.h \
+                 src/wave_out.h \
+                 src/wav_io.h
+
+EXTRA_DIST = Makefile.unix \
+             man/opusrtp.1 \
+             opus-tools.sln \
+             src/opusdec.vcxproj.filters \
+             src/opusinfo.vcxproj.filters \
+             src/opusenc.vcxproj.filters \
+             src/opusinfo.vcxproj \
+             src/opusenc.vcxproj \
+             src/opusdec.vcxproj \
+             include/getopt.h \
+             share/getopt.c \
+             share/getopt1.c \
+             win32/genversion.bat \
+             win32/config.h \
+             win32/unicode_support.h \
+             win32/unicode_support.c \
+             win32/version.h
+
+dist_man_MANS = man/opusenc.1 man/opusdec.1 man/opusinfo.1
+
+resampler_CPPFLAGS = -DSPX_RESAMPLE_EXPORT= -DRANDOM_PREFIX=opustools -DOUTSIDE_SPEEX -DFLOATING_POINT
+
+opusenc_SOURCES = src/opus_header.c src/opusenc.c src/picture.c src/resample.c src/audio-in.c src/diag_range.c src/flac.c src/lpc.c win32/unicode_support.c
+opusenc_CPPFLAGS = $(AM_CPPFLAGS) $(resampler_CPPFLAGS)
+opusenc_CFLAGS = $(AM_CFLAGS) $(FLAC_CFLAGS)
+opusenc_LDADD = $(OPUS_LIBS) $(FLAC_LIBS) $(OGG_LIBS) $(LIBM)
+opusenc_MANS = man/opusenc.1
+
+opusdec_SOURCES = src/opus_header.c src/wav_io.c src/wave_out.c src/opusdec.c src/resample.c src/diag_range.c win32/unicode_support.c
+opusdec_CPPFLAGS = $(AM_CPPFLAGS) $(resampler_CPPFLAGS)
+opusdec_LDADD = $(OPUS_LIBS) $(OGG_LIBS) $(LIBM)
+opusdec_MANS = man/opusdec.1
+
+opusinfo_SOURCES = src/opus_header.c src/opusinfo.c src/info_opus.c src/picture.c win32/unicode_support.c
+opusinfo_CPPFLAGS = $(AM_CPPFLAGS) -DOPUSTOOLS
+opusinfo_LDADD = $(OGG_LIBS)
+opusinfo_MANS = man/opusinfo.1
+
+opusrtp_SOURCES = src/opusrtp.c
+opusrtp_LDADD = $(OPUS_LIBS) $(OGG_LIBS) @LIBPCAP@
+
+#TESTS = FIXME
+
+
+# We check this every time make is run, with configure.ac being touched to
+# trigger an update of the build system files if update_version changes the
+# current PACKAGE_VERSION (or if package_version was modified manually by a
+# user with either AUTO_UPDATE=no or no update_version script present - the
+# latter being the normal case for tarball releases).
+#
+# We can't just add the package_version file to CONFIGURE_DEPENDENCIES since
+# simply running autoconf will not actually regenerate configure for us when
+# the content of that file changes (due to autoconf dependency checking not
+# knowing about that without us creating yet another file for it to include).
+#
+# The MAKECMDGOALS check is a gnu-make'ism, but will degrade 'gracefully' for
+# makes that don't support it.  The only loss of functionality is not forcing
+# an update of package_version for `make dist` if AUTO_UPDATE=no, but that is
+# unlikely to be a real problem for any real user.
+$(top_srcdir)/configure.ac: force
+	@case "$(MAKECMDGOALS)" in \
+	    dist-hook)                             exit 0       ;; \
+	    dist-* | dist | distcheck | distclean) _arg=release ;; \
+	esac; \
+	if ! $(top_srcdir)/update_version $$_arg 2> /dev/null; then \
+	    if [ ! -e $(top_srcdir)/package_version ]; then \
+		echo 'PACKAGE_VERSION="unknown"' > $(top_srcdir)/package_version; \
+	    fi; \
+	    . $(top_srcdir)/package_version || exit 1; \
+	    [ "$(PACKAGE_VERSION)" != "$$PACKAGE_VERSION" ] || exit 0; \
+	fi; \
+	touch $@
+
+force:
+
+# Create a minimal package_version file when make dist is run.
+dist-hook:
+	echo 'PACKAGE_VERSION="$(PACKAGE_VERSION)"' > $(top_distdir)/package_version
+
diff --git a/Makefile.unix b/Makefile.unix
new file mode 100644
index 0000000..3472da7
--- /dev/null
+++ b/Makefile.unix
@@ -0,0 +1,64 @@
+#Opus-tools should be built with autotools, not this makefile.
+#Run ./configure to run autotools/autoconf.
+#This makefile exists as a fallback where autotools isn't working.
+
+-include package_version
+
+#CC=gcc
+CFLAGS := -O2 -g -c -Wall -Wextra -DHAVE_LIBFLAC $(CFLAGS)
+INCLUDES := -I../opus/include -I/usr/include/FLAC
+
+ifneq (,$(findstring mingw,$(CC)))
+  INCLUDES += -Iwin32
+  COMMON_OBJS += win32/unicode_support.o
+  CFLAGS += -DHAVE_WINMM
+  LIBS += -lwinmm
+endif
+
+PROGS := opusenc opusdec opusinfo
+all: $(PROGS)
+
+clean:
+	rm -f src/*.o win32/*.o $(PROGS) opusrtp
+
+.PHONY: all clean
+
+
+VERSIONED_OBJS = src/opusenc.o src/opusdec.o src/opusinfo.o src/opusrtp.o \
+  src/wave_out.o
+
+$(VERSIONED_OBJS): CFLAGS += -DPACKAGE_NAME='"opus-tools"' -DPACKAGE_VERSION='$(PACKAGE_VERSION)'
+$(VERSIONED_OBJS): package_version
+
+RESAMPLER_CPPFLAGS = -DSPX_RESAMPLE_EXPORT= -DRANDOM_PREFIX=opustools -DOUTSIDE_SPEEX -DFLOATING_POINT
+
+src/opusdec.o src/resample.o src/audio-in.o: CFLAGS += $(RESAMPLER_CPPFLAGS)
+
+src/info_opus.o: CFLAGS += -DOPUSTOOLS
+
+
+.c.o:
+	$(CC) $(CFLAGS) $(INCLUDES) $< -o $@
+
+opusenc: src/opus_header.o src/opusenc.o src/resample.o src/audio-in.o src/diag_range.o src/lpc.o src/flac.o src/picture.o $(COMMON_OBJS)
+	$(CC) $(LDFLAGS) $^ -o $@ ../opus/.libs/libopus.a -lm -logg -lFLAC $(LIBS)
+
+opusdec: src/opus_header.o src/wav_io.o src/wave_out.o src/opusdec.o src/resample.o src/diag_range.o $(COMMON_OBJS)
+	$(CC) $(LDFLAGS) $^ -o $@ ../opus/.libs/libopus.a -lm -logg $(LIBS)
+
+opusinfo: src/opus_header.o src/opusinfo.o src/info_opus.o src/picture.o $(COMMON_OBJS)
+	$(CC) $(LDFLAGS) $^ -o $@ -logg $(LIBS)
+
+opusrtp: src/opusrtp.o
+	$(CC) $(LDFLAGS) $^ -o $@ ../opus/.libs/libopus.a -logg -lm
+
+
+package_version: force
+	@if [ -x ./update_version ]; then \
+		./update_version || true; \
+	elif [ ! -e ./package_version ]; then \
+		echo 'PACKAGE_VERSION="unknown"' > ./package_version; \
+	fi
+
+force:
+
diff --git a/README b/README
new file mode 100644
index 0000000..6d566c1
--- /dev/null
+++ b/README
@@ -0,0 +1,7 @@
+== Opus Audio Tools ==
+
+This is opus-tools, a set of tools to encode, inspect, and decode
+audio in the Opus format.
+
+For more information on Opus see http://www.opus-codec.org/
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..942ba09
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Run this to set up the build system: configure, makefiles, etc.
+set -e
+
+srcdir=`dirname $0`
+test -n "$srcdir" && cd "$srcdir"
+
+echo "Updating build configuration files, please wait...."
+
+ACLOCAL_FLAGS="-I m4"
+autoreconf -isf
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..edff53f
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,323 @@
+dnl Process this file with autoconf to produce a configure script. -*-m4-*-
+
+dnl The package_version file will be automatically synced to the git revision
+dnl by the update_version script when configured in the repository, but will
+dnl remain constant in tarball releases unless it is manually edited.
+m4_define([CURRENT_VERSION],
+          m4_esyscmd([ ./update_version 2>/dev/null || true
+                       if test -e package_version; then
+                           . ./package_version
+                           printf "$PACKAGE_VERSION"
+                       else
+                           printf "unknown"
+                       fi ]))
+
+AC_INIT([opus-tools],[CURRENT_VERSION],[opus@xiph.org])
+AC_CONFIG_SRCDIR([src/opusenc.c])
+AC_CONFIG_MACRO_DIR([m4])
+
+dnl enable silent rules on automake 1.11 and later
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AM_INIT_AUTOMAKE([1.11 foreign no-define])
+AM_MAINTAINER_MODE([enable])
+
+AC_CANONICAL_HOST
+AM_PROG_CC_C_O
+
+AC_PROG_CC_C99
+AC_C_BIGENDIAN
+AC_C_CONST
+AC_C_INLINE
+
+#Use a hacked up version of autoconf's AC_C_RESTRICT because it's not
+#strong enough a test to detect old buggy versions of GCC (e.g. 2.95.3)
+#Note: Both this and the test for variable-size arrays below are also
+#      done by AC_PROG_CC_C99, but not thoroughly enough apparently.
+AC_CACHE_CHECK([for C/C++ restrict keyword], ac_cv_c_restrict,
+  [ac_cv_c_restrict=no
+   # The order here caters to the fact that C++ does not require restrict.
+   for ac_kw in __restrict __restrict__ _Restrict restrict; do
+     AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+      [[typedef int * int_ptr;
+	int foo (int_ptr $ac_kw ip, int * $ac_kw baz[]) {
+	return ip[0];
+       }]],
+      [[int s[1];
+	int * $ac_kw t = s;
+	t[0] = 0;
+	return foo(t, (void *)0)]])],
+      [ac_cv_c_restrict=$ac_kw])
+     test "$ac_cv_c_restrict" != no && break
+   done
+  ])
+
+AH_VERBATIM([restrict],
+[/* Define to the equivalent of the C99 'restrict' keyword, or to
+   nothing if this is not supported.  Do not define if restrict is
+   supported directly.  */
+#undef restrict
+/* Work around a bug in Sun C++: it does not support _Restrict or
+   __restrict__, even though the corresponding Sun C compiler ends up with
+   "#define restrict _Restrict" or "#define restrict __restrict__" in the
+   previous line.  Perhaps some future version of Sun C++ will work with
+   restrict; if so, hopefully it defines __RESTRICT like Sun C does.  */
+#if defined __SUNPRO_CC && !defined __RESTRICT
+# define _Restrict
+# define __restrict__
+#endif])
+
+case $ac_cv_c_restrict in
+   restrict) ;;
+   no) AC_DEFINE([restrict], []) ;;
+   *)  AC_DEFINE_UNQUOTED([restrict], [$ac_cv_c_restrict]) ;;
+esac
+
+AC_MSG_CHECKING(for C99 variable-size arrays)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+                   [[static int x; char a[++x]; a[sizeof a - 1] = 0; int N; return a[0];]])],
+    [ has_var_arrays=yes
+      AC_DEFINE([VAR_ARRAYS], [1], [Use C99 variable-size arrays])
+    ],[
+      has_var_arrays=no
+    ])
+AC_MSG_RESULT([$has_var_arrays])
+
+AC_ARG_ENABLE([assertions],
+    [AS_HELP_STRING([--enable-assertions],[enable additional software error checking])],,
+    [enable_assertions=no])
+
+AS_IF([test "$enable_assertions" = "yes"], [
+  AC_DEFINE([ENABLE_ASSERTIONS], [1], [Assertions])
+])
+
+if test "$CFLAGS" = "-g -O2"; then
+    saved_CFLAGS="$CFLAGS"
+    CFLAGS="-O3 -ffast-math"
+    AC_MSG_CHECKING([if ${CC} supports -O3 -g -ffast-math])
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
+        [ AC_MSG_RESULT([yes])
+          saved_CFLAGS="-O3 -g -ffast-math"
+        ],[
+          AC_MSG_RESULT([no])
+        ])
+    CFLAGS="$saved_CFLAGS"
+fi
+
+LT_LIB_M
+AC_CHECK_LIB([winmm], [main])
+
+dnl check for pkg-config itself so we don't try the m4 macro without pkg-config
+AC_CHECK_PROG(HAVE_PKG_CONFIG, pkg-config, yes)
+
+dnl check for Ogg
+AS_IF([test "$HAVE_PKG_CONFIG" = "yes"],
+  dnl first try via pkg-config
+ [PKG_CHECK_MODULES([OGG],[ogg >= 1.3])],
+ [
+  dnl fall back to the old school test
+  XIPH_PATH_OGG(, AC_MSG_ERROR([
+    libogg is required to build this package!
+    please see http://www.xiph.org/ for how to
+    obtain a copy.
+  ]))
+  saved_CFLAGS="$CFLAGS"
+  saved_LIBS="$LIBS"
+  CFLAGS="$CFLAGS $OGG_CFLAGS"
+  LIBS="$LIBS $OGG_LIBS"
+  AC_CHECK_FUNC([ogg_stream_flush_fill],,[
+    AC_MSG_ERROR([newer libogg version (1.3 or later) required])
+  ])
+  CFLAGS="$saved_CFLAGS"
+  LIBS="$saved_LIBS"
+ ])
+
+dnl check for Opus
+AS_IF([test "$HAVE_PKG_CONFIG" = "yes"],
+ [PKG_CHECK_MODULES([OPUS],[opus >= 1.0.3])],
+ [
+  dnl fall back to the old school test
+  XIPH_PATH_OPUS(, AC_MSG_ERROR([
+    Opus is required to build this package!
+    please see http://opus-codec.org/ for how to
+    obtain a copy.
+  ]))
+ ])
+
+dnl check for OSS
+HAVE_OSS=no
+AC_CHECK_HEADERS([sys/soundcard.h soundcard.h machine/soundcard.h],[
+  HAVE_OSS=yes
+  break
+])
+
+dnl check for sndio
+AC_CHECK_LIB([sndio], [sio_open])
+
+if test "$HAVE_OSS" != "yes" && test "$ac_cv_lib_sndio_sio_open" != "yes"; then
+  AC_MSG_WARN([Audio support not found -- no direct audio output in opusdec])
+fi
+
+dnl check for flac
+AC_ARG_WITH([flac],
+    [AS_HELP_STRING([--without-flac],[disable FLAC support])],,
+    [with_flac=yes])
+
+AS_IF([test "$with_flac" = "yes"],
+ [
+  AS_IF([test "$HAVE_PKG_CONFIG" = "yes"],
+   [PKG_CHECK_MODULES([FLAC],[flac >= 1.1.3])],
+   [
+    dnl fall back to AC_CHECK_LIB
+    AC_CHECK_LIB([FLAC],[FLAC__stream_decoder_init_ogg_stream],
+      [
+        FLAC_LIBS="-lFLAC"
+      ],
+      [
+        AC_MSG_ERROR([
+          FLAC 1.1.3 or later is required to build this package!
+          Please install it or configure with --disable-flac.
+        ])
+      ]
+    )
+    AC_CHECK_HEADER([FLAC/stream_decoder.h],,
+      [
+        AC_MSG_ERROR([
+          FLAC headers are required to build this package!
+          Please install the development version of FLAC
+          or configure with --disable-flac.
+        ])
+      ]
+    )
+   ])
+
+  AC_DEFINE([HAVE_LIBFLAC],[1],[FLAC])
+ ])
+
+dnl check for pcap
+AC_CHECK_LIB([pcap], [pcap_open_live], [
+  AC_DEFINE([HAVE_PCAP], 1, [Define if building with libpcap support])
+  LIBPCAP="-lpcap"
+])
+AC_SUBST(LIBPCAP)
+
+on_x86=no
+case "$host_cpu" in
+i[[3456]]86 | x86_64)
+  on_x86=yes
+  ;;
+esac
+
+dnl check for sse
+AC_ARG_ENABLE([sse],
+    [AS_HELP_STRING([--enable-sse],[Build binaries that require SSE])],,
+    [enable_sse=no])
+
+AS_IF([test "$on_x86" = "yes" && test "$enable_sse" = "yes"],
+ [
+  saved_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -msse"
+  AC_MSG_CHECKING([if ${CC} supports -msse])
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([],[[char foo;]])],
+    [ AC_MSG_RESULT([yes]) ],
+    [
+      AC_MSG_RESULT([no])
+      enable_sse=no
+      CFLAGS="$saved_CFLAGS"
+    ])
+ ])
+
+dnl Enable stack-protector-all only on x86 where it's well supported.
+dnl on some platforms it causes crashes. Hopefully the OS's default's
+dnl include this on platforms that work but have been missed here.
+AC_ARG_ENABLE([stack-protector],
+    [AS_HELP_STRING([--disable-stack-protector],[Disable compiler stack hardening])],,
+    [
+      AS_IF([test "$ac_cv_c_compiler_gnu" = "yes" && test "$on_x86" = "yes"],
+            [enable_stack_protector=yes],[enable_stack_protector=no])
+    ])
+
+AS_IF([test "$enable_stack_protector" = "yes"],
+ [
+  saved_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -fstack-protector-all"
+  AC_MSG_CHECKING([if ${CC} supports -fstack-protector-all])
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([],[[char foo;]])],
+    [ AC_MSG_RESULT([yes]) ],
+    [
+      AC_MSG_RESULT([no])
+      enable_stack_protector=no
+      CFLAGS="$saved_CFLAGS"
+    ])
+ ])
+
+
+AC_ARG_ENABLE([pie],
+    [AS_HELP_STRING([--disable-pie],[Disable PIE/RELRO hardening])],,
+    [enable_pie=yes])
+
+AS_IF([test "$enable_pie" = "yes"],
+ [
+  saved_CFLAGS="$CFLAGS"
+  saved_LDFLAGS="$LDFLAGS"
+  saved_LIBS="$LIBS"
+  CFLAGS="$CFLAGS -fPIE"
+  LDFLAGS="$LDFLAGS -pie -Wl,-z,relro -Wl,-z,now"
+  LIBS="$LIBS $OPUS_LIBS"
+  AC_MSG_CHECKING([for PIE support])
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <opus/opus.h>]],
+                                  [[OpusDecoder *dec = opus_decoder_create(48000, 2, 0L)]])],
+    [ AC_MSG_RESULT([yes]) ],
+    [ AC_MSG_RESULT([no])
+      enable_pie=no
+      CFLAGS="$saved_CFLAGS"
+      LDFLAGS="$saved_LDFLAGS"
+    ])
+  LIBS="$saved_LIBS"
+ ])
+
+CFLAGS="$CFLAGS -W"
+
+saved_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes"
+AC_MSG_CHECKING([if ${CC} supports -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
+    [ AC_MSG_RESULT([yes]) ],
+    [ AC_MSG_RESULT([no])
+      CFLAGS="$saved_CFLAGS"
+    ])
+
+AC_FUNC_FSEEKO
+
+saved_LIBS="$LIBS"
+LIBS="$LIBS $LIBM"
+AC_CHECK_FUNCS([lrintf])
+AC_CHECK_FUNCS([fminf])
+AC_CHECK_FUNCS([fmaxf])
+LIBS="$saved_LIBS"
+
+AC_CONFIG_FILES([Makefile])
+AC_CONFIG_HEADERS([config.h])
+AC_OUTPUT
+
+AC_MSG_NOTICE([
+------------------------------------------------------------------------
+  $PACKAGE_NAME $PACKAGE_VERSION:  Automatic configuration OK.
+
+    Compiler support:
+
+      C99 var arrays: ................ ${has_var_arrays}
+      C99 lrintf: .................... ${ac_cv_func_lrintf}
+      Stack protector: ............... ${enable_stack_protector}
+      PIE: ........................... ${enable_pie}
+
+    General configuration:
+
+      Assertion checking: ............ ${enable_assertions}
+      FLAC input: .................... ${with_flac}
+
+------------------------------------------------------------------------
+
+ Type "make; make install" to compile and install
+])
+
diff --git a/include/getopt.h b/include/getopt.h
new file mode 100644
index 0000000..b0147e9
--- /dev/null
+++ b/include/getopt.h
@@ -0,0 +1,169 @@
+/* Declarations for getopt.
+   Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _GETOPT_H
+
+#ifndef __need_getopt
+# define _GETOPT_H 1
+#endif
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+#ifndef __need_getopt
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument		(or 0) if the option does not take an argument,
+   required_argument	(or 1) if the option requires an argument,
+   optional_argument 	(or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+# if defined __STDC__ && __STDC__
+  const char *name;
+# else
+  char *name;
+# endif
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+# define no_argument		0
+# define required_argument	1
+# define optional_argument	2
+#endif	/* need getopt */
+
+
+/* Get definitions and prototypes for functions to process the
+   arguments in ARGV (ARGC of them, minus the program name) for
+   options given in OPTS.
+
+   Return the option character from OPTS just read.  Return -1 when
+   there are no more options.  For unrecognized options, or options
+   missing arguments, `optopt' is set to the option letter, and '?' is
+   returned.
+
+   The OPTS string is a list of characters which are recognized option
+   letters, optionally followed by colons, specifying that that letter
+   takes an argument, to be placed in `optarg'.
+
+   If a letter in OPTS is followed by two colons, its argument is
+   optional.  This behavior is specific to the GNU `getopt'.
+
+   The argument `--' causes premature termination of argument
+   scanning, explicitly telling `getopt' that there are no more
+   options.
+
+   If OPTS begins with `--', then non-option arguments are treated as
+   arguments to the option '\0'.  This behavior is specific to the GNU
+   `getopt'.  */
+
+#if defined __STDC__ && __STDC__
+# ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+extern int getopt (int __argc, char *const *__argv, const char *__shortopts);
+# else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+# endif /* __GNU_LIBRARY__ */
+
+# ifndef __need_getopt
+extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts,
+		        const struct option *__longopts, int *__longind);
+extern int getopt_long_only (int __argc, char *const *__argv,
+			     const char *__shortopts,
+		             const struct option *__longopts, int *__longind);
+
+/* Internal only.  Users should not call this directly.  */
+extern int _getopt_internal (int __argc, char *const *__argv,
+			     const char *__shortopts,
+		             const struct option *__longopts, int *__longind,
+			     int __long_only);
+# endif
+#else /* not __STDC__ */
+extern int getopt ();
+# ifndef __need_getopt
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+# endif
+#endif /* __STDC__ */
+
+#ifdef	__cplusplus
+}
+#endif
+
+/* Make sure we later can get all the definitions and declarations.  */
+#undef __need_getopt
+
+#endif /* getopt.h */
diff --git a/m4/ogg.m4 b/m4/ogg.m4
new file mode 100644
index 0000000..77d663b
--- /dev/null
+++ b/m4/ogg.m4
@@ -0,0 +1,116 @@
+# Configure paths for libogg
+# Jack Moffitt <jack@icecast.org> 10-21-2000
+# Shamelessly stolen from Owen Taylor and Manish Singh
+
+dnl XIPH_PATH_OGG([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Test for libogg, and define OGG_CFLAGS and OGG_LIBS
+dnl
+AC_DEFUN([XIPH_PATH_OGG],
+[dnl
+dnl Get the cflags and libraries
+dnl
+AC_ARG_WITH(ogg,AC_HELP_STRING([--with-ogg=PFX],[Prefix where libogg is installed (optional)]), ogg_prefix="$withval", ogg_prefix="")
+AC_ARG_WITH(ogg-libraries,AC_HELP_STRING([--with-ogg-libraries=DIR],[Directory where libogg library is installed (optional)]), ogg_libraries="$withval", ogg_libraries="")
+AC_ARG_WITH(ogg-includes,AC_HELP_STRING([--with-ogg-includes=DIR],[Directory where libogg header files are installed (optional)]), ogg_includes="$withval", ogg_includes="")
+AC_ARG_ENABLE(oggtest,AC_HELP_STRING([--disable-oggtest],[Do not try to compile and run a test Ogg program]),, enable_oggtest=yes)
+
+  if test "x$ogg_libraries" != "x" ; then
+    OGG_LIBS="-L$ogg_libraries"
+  elif test "x$ogg_prefix" = "xno" || test "x$ogg_prefix" = "xyes" ; then
+    OGG_LIBS=""
+  elif test "x$ogg_prefix" != "x" ; then
+    OGG_LIBS="-L$ogg_prefix/lib"
+  elif test "x$prefix" != "xNONE" ; then
+    OGG_LIBS="-L$prefix/lib"
+  fi
+
+  if test "x$ogg_prefix" != "xno" ; then
+    OGG_LIBS="$OGG_LIBS -logg"
+  fi
+
+  if test "x$ogg_includes" != "x" ; then
+    OGG_CFLAGS="-I$ogg_includes"
+  elif test "x$ogg_prefix" = "xno" || test "x$ogg_prefix" = "xyes" ; then
+    OGG_CFLAGS=""
+  elif test "x$ogg_prefix" != "x" ; then
+    OGG_CFLAGS="-I$ogg_prefix/include"
+  elif test "x$prefix" != "xNONE"; then
+    OGG_CFLAGS="-I$prefix/include"
+  fi
+
+  AC_MSG_CHECKING(for Ogg)
+  if test "x$ogg_prefix" = "xno" ; then
+    no_ogg="disabled"
+    enable_oggtest="no"
+  else
+    no_ogg=""
+  fi
+
+
+  if test "x$enable_oggtest" = "xyes" ; then
+    ac_save_CFLAGS="$CFLAGS"
+    ac_save_LIBS="$LIBS"
+    CFLAGS="$CFLAGS $OGG_CFLAGS"
+    LIBS="$LIBS $OGG_LIBS"
+dnl
+dnl Now check if the installed Ogg is sufficiently new.
+dnl
+      rm -f conf.oggtest
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+
+int main ()
+{
+  system("touch conf.oggtest");
+  return 0;
+}
+
+],, no_ogg=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+  fi
+
+  if test "x$no_ogg" = "xdisabled" ; then
+     AC_MSG_RESULT(no)
+     ifelse([$2], , :, [$2])
+  elif test "x$no_ogg" = "x" ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$1], , :, [$1])
+  else
+     AC_MSG_RESULT(no)
+     if test -f conf.oggtest ; then
+       :
+     else
+       echo "*** Could not run Ogg test program, checking why..."
+       CFLAGS="$CFLAGS $OGG_CFLAGS"
+       LIBS="$LIBS $OGG_LIBS"
+       AC_TRY_LINK([
+#include <stdio.h>
+#include <ogg/ogg.h>
+],     [ return 0; ],
+       [ echo "*** The test program compiled, but did not run. This usually means"
+       echo "*** that the run-time linker is not finding Ogg or finding the wrong"
+       echo "*** version of Ogg. If it is not finding Ogg, you'll need to set your"
+       echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+       echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+       echo "*** is required on your system"
+       echo "***"
+       echo "*** If you have an old version installed, it is best to remove it, although"
+       echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+       [ echo "*** The test program failed to compile or link. See the file config.log for the"
+       echo "*** exact error that occured. This usually means Ogg was incorrectly installed"
+       echo "*** or that you have moved Ogg since it was installed." ])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+     OGG_CFLAGS=""
+     OGG_LIBS=""
+     ifelse([$2], , :, [$2])
+  fi
+  AC_SUBST(OGG_CFLAGS)
+  AC_SUBST(OGG_LIBS)
+  rm -f conf.oggtest
+])
diff --git a/m4/opus.m4 b/m4/opus.m4
new file mode 100644
index 0000000..20f166b
--- /dev/null
+++ b/m4/opus.m4
@@ -0,0 +1,117 @@
+# Configure paths for libopus
+# Gregory Maxwell <greg@xiph.org> 08-30-2012
+# Shamelessly stolen from Jack Moffitt (libogg) who
+# Shamelessly stole from Owen Taylor and Manish Singh
+
+dnl XIPH_PATH_OPUS([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Test for libopus, and define OPUS_CFLAGS and OPUS_LIBS
+dnl
+AC_DEFUN([XIPH_PATH_OPUS],
+[dnl
+dnl Get the cflags and libraries
+dnl
+AC_ARG_WITH(opus,AC_HELP_STRING([--with-opus=PFX],[Prefix where opus is installed (optional)]), opus_prefix="$withval", opus_prefix="")
+AC_ARG_WITH(opus-libraries,AC_HELP_STRING([--with-opus-libraries=DIR],[Directory where the opus library is installed (optional)]), opus_libraries="$withval", opus_libraries="")
+AC_ARG_WITH(opus-includes,AC_HELP_STRING([--with-opus-includes=DIR],[Directory where the opus header files are installed (optional)]), opus_includes="$withval", opus_includes="")
+AC_ARG_ENABLE(opustest,AC_HELP_STRING([--disable-opustest],[Do not try to compile and run a test opus program]),, enable_opustest=yes)
+
+  if test "x$opus_libraries" != "x" ; then
+    OPUS_LIBS="-L$opus_libraries"
+  elif test "x$opus_prefix" = "xno" || test "x$opus_prefix" = "xyes" ; then
+    OPUS_LIBS=""
+  elif test "x$opus_prefix" != "x" ; then
+    OPUS_LIBS="-L$opus_prefix/lib"
+  elif test "x$prefix" != "xNONE" ; then
+    OPUS_LIBS="-L$prefix/lib"
+  fi
+
+  if test "x$opus_prefix" != "xno" ; then
+    OPUS_LIBS="$OPUS_LIBS -lopus"
+  fi
+
+  if test "x$opus_includes" != "x" ; then
+    OPUS_CFLAGS="-I$opus_includes"
+  elif test "x$opus_prefix" = "xno" || test "x$opus_prefix" = "xyes" ; then
+    OPUS_CFLAGS=""
+  elif test "x$opus_prefix" != "x" ; then
+    OPUS_CFLAGS="-I$opus_prefix/include"
+  elif test "x$prefix" != "xNONE"; then
+    OPUS_CFLAGS="-I$prefix/include"
+  fi
+
+  AC_MSG_CHECKING(for Opus)
+  if test "x$opus_prefix" = "xno" ; then
+    no_opus="disabled"
+    enable_opustest="no"
+  else
+    no_opus=""
+  fi
+
+
+  if test "x$enable_opustest" = "xyes" ; then
+    ac_save_CFLAGS="$CFLAGS"
+    ac_save_LIBS="$LIBS"
+    CFLAGS="$CFLAGS $OPUS_CFLAGS"
+    LIBS="$LIBS $OPUS_LIBS"
+dnl
+dnl Now check if the installed Opus is sufficiently new.
+dnl
+      rm -f conf.opustest
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <opus.h>
+
+int main ()
+{
+  system("touch conf.opustest");
+  return 0;
+}
+
+],, no_opus=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+  fi
+
+  if test "x$no_opus" = "xdisabled" ; then
+     AC_MSG_RESULT(no)
+     ifelse([$2], , :, [$2])
+  elif test "x$no_opus" = "x" ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$1], , :, [$1])
+  else
+     AC_MSG_RESULT(no)
+     if test -f conf.opustest ; then
+       :
+     else
+       echo "*** Could not run Opus test program, checking why..."
+       CFLAGS="$CFLAGS $OPUS_CFLAGS"
+       LIBS="$LIBS $OPUS_LIBS"
+       AC_TRY_LINK([
+#include <stdio.h>
+#include <opus.h>
+],     [ return 0; ],
+       [ echo "*** The test program compiled, but did not run. This usually means"
+       echo "*** that the run-time linker is not finding Opus or finding the wrong"
+       echo "*** version of Opus. If it is not finding Opus, you'll need to set your"
+       echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+       echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+       echo "*** is required on your system"
+       echo "***"
+       echo "*** If you have an old version installed, it is best to remove it, although"
+       echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+       [ echo "*** The test program failed to compile or link. See the file config.log for the"
+       echo "*** exact error that occured. This usually means Opus was incorrectly installed"
+       echo "*** or that you have moved Opus since it was installed." ])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+     OPUS_CFLAGS=""
+     OPUS_LIBS=""
+     ifelse([$2], , :, [$2])
+  fi
+  AC_SUBST(OPUS_CFLAGS)
+  AC_SUBST(OPUS_LIBS)
+  rm -f conf.opustest
+])
diff --git a/m4/pkg.m4 b/m4/pkg.m4
new file mode 100644
index 0000000..73973f7
--- /dev/null
+++ b/m4/pkg.m4
@@ -0,0 +1,157 @@
+# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
+# serial 1 (pkg-config-0.24)
+# 
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=m4_default([$1], [0.9.0])
+	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		AC_MSG_RESULT([yes])
+	else
+		AC_MSG_RESULT([no])
+		PKG_CONFIG=""
+	fi
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists.  Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+# only at the first occurence in configure.ac, so if the first place
+# it's called might be skipped (such as if it is within an "if", you
+# have to call PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_default([$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+    pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+    PKG_CHECK_EXISTS([$3],
+                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+		     [pkg_failed=yes])
+ else
+    pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+   	AC_MSG_RESULT([no])
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1`
+        else 
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+	m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])
+        ])
+elif test $pkg_failed = untried; then
+     	AC_MSG_RESULT([no])
+	m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])
+        ])
+else
+	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+	$3
+fi[]dnl
+])# PKG_CHECK_MODULES
diff --git a/man/opusdec.1 b/man/opusdec.1
new file mode 100644
index 0000000..f45bff8
--- /dev/null
+++ b/man/opusdec.1
@@ -0,0 +1,122 @@
+.\" Process this file with
+.\" groff -man -Tascii opusdec.1
+.\"
+.TH opusdec 1 2012-08-31 "Xiph.Org Foundation" "opus-tools"
+
+.SH NAME
+opusdec \- decode audio from Opus format to WAV (or simple audio output)
+
+.SH SYNOPSIS
+.B opusdec
+[
+.B -hV
+] [
+.B --rate Hz
+] [
+.B --gain dB
+] [
+.B --no-dither
+] [
+.B --float
+] [
+.B --force-wav
+] [
+.B --packet-loss pct
+] [
+.B --save-range file
+]
+.B input.opus
+[
+.B output.wav
+]
+
+.SH DESCRIPTION
+
+.B opusdec
+decodes Opus files into PCM Wave (uncompressed) files.
+
+If the input file is specified as
+.B "-"
+, then
+.B opusdec
+will read from stdin. Likewise, an output filename of
+.B "-"
+will cause output to be to stdout.
+
+If no output is specified
+.B opusdec
+will attempt to play the audio in realtime if it supports
+audio playback on your system.
+
+.SH "OPTIONS"
+.IP "-h, --help"
+Print help message
+.IP "-V, --version"
+Display version information
+.IP "--quiet"
+Suppresses program output
+.IP "--rate"
+.br
+Force decoding at sampling rate n Hz
+.IP "--gain"
+.br
+Adjust the output volume n dB, negative values make the signal quieter.
+.IP "--no-dither"
+Do not dither 16-bit output
+.IP "--float"
+32-bit floating-point files instead of 16-bit files
+.IP "--force-wav"
+Force including a wav header on output (e.g. for non-wav extensions and stdout)
+.IP "--packet-loss"
+Simulate n % random Opus packet loss
+.IP "--save-range"
+Saves check values for every frame to a file
+
+.SH EXAMPLES
+Decode a file
+.B input.opus
+to
+.B output.wav
+.RS
+opusdec input.opus output.wav
+.RE
+
+Play a file
+.B input.opus
+and force output at 48000 regardless of
+the original sampling rate
+.br
+.I (48kHz output may be faster, due to avoiding resampling and some sound hardware produces higher quality output when run at 48kHz)
+.RS
+opusdec --rate 48000 input.opus
+.RE
+
+Re-encode a high bitrate Opus file to a lower rate
+.RS
+opusdec --force-wav input.opus - | opusenc --bitrate 64 - output.opus
+.RE
+
+Play an http stream
+.B http://icecast.somwhere.org:8000/stream.opus
+with the help of curl on a system with pulseaudio
+.br
+.I (press ctrl-c to quit)
+.RS
+curl http://icecast.somwhere.org:8000/stream.opus | padsp opusdec -
+.RE
+
+.SH AUTHORS
+.br
+Jean-Marc Valin <jmvalin@jmvalin.ca>
+.br
+Gregory Maxwell <greg@xiph.org>
+
+.SH BUGS
+
+Opusdec does not currently reject all invalid files which it should reject.
+It also doesn't provide very helpful output for the corrupted files it
+does reject. Use \fBopusinfo\fR(1) for somewhat better diagnostics.
+
+.SH SEE ALSO
+.BR opusenc (1),
+.BR opusinfo (1)
diff --git a/man/opusenc.1 b/man/opusenc.1
new file mode 100644
index 0000000..6ad4206
--- /dev/null
+++ b/man/opusenc.1
@@ -0,0 +1,360 @@
+.\" Process this file with
+.\" groff -man -Tascii opusenc.1
+.\"
+.TH opusenc 1 2012-08-31 "Xiph.Org Foundation" "opus-tools"
+
+.SH NAME
+opusenc \- encode audio into the Opus format
+
+.SH SYNOPSIS
+.B opusenc
+[
+.B -h
+] [
+.B -V
+] [
+.B --bitrate
+.I kbit/sec
+] [
+.B --vbr
+] [
+.B --cvbr
+] [
+.B --hard-cbr
+] [
+.B --comp
+.I complexity
+] [
+.B --framesize
+.I 2.5, 5, 10, 20, 40, 60
+] [
+.B --expect-loss
+.I pct
+] [
+.B --downmix-mono
+] [
+.B --downmix-stereo
+] [
+.B --max-delay
+.I ms
+] [
+.B --serial
+.I serial number
+] [
+.B --save-range
+.I file
+] [
+.B --set-ctl-int
+.I ctl=value
+] [
+.B --comment
+.I tag=value
+] [
+.B --artist
+.I author
+] [
+.B --title
+.I 'track title'
+] [
+.B --album
+.I 'album title'
+] [
+.B --date
+.I YYYY-MM-DD
+] [
+.B --genre
+.I genre
+] [
+.B --picture
+.IB filename | specification
+] [
+.B --padding
+.I n
+] [
+.B --discard-comments
+] [
+.B --discard-pictures
+] [
+.B --raw
+] [
+.B --raw-bits
+.I bits/sample
+] [
+.B --raw-rate
+.I Hz
+] [
+.B --raw-chan
+.I N
+] [
+.B --raw-endianness
+.I flag
+] [
+.B --ignorelength
+]
+.I input.wav
+.I output.opus
+
+.SH DESCRIPTION
+.B opusenc
+reads audio data in either raw, Wave, or AIFF format and encodes it into an
+Opus stream. If the input file is "-" audio data is read from stdin.
+Likewise, if the output file is "-" opus data is written to stdout.
+
+Unless quieted
+.B opusenc
+displays fancy statistics about the encoding progress.
+
+.SH OPTIONS
+.IP "-h, --help"
+Show command help
+.IP "-V, --version"
+Show the version number
+.IP "--bitrate N.nnn"
+Target bitrate in kbit/sec (6-256 per channel)
+
+In VBR mode this specifies the average rate for a large and diverse
+collection of audio. In CVBR and Hard-CBR mode it specifies the specific
+output bitrate.
+
+Default for >=44.1kHz input is 64kbps per mono stream, 96kbps per coupled pair.
+
+.IP "--vbr"
+Use variable bitrate encoding (default)
+
+In VBR mode the bitrate may go up and down freely depending on the content
+to achieve more consistent quality.
+
+.IP "--cvbr"
+Use constrained variable bitrate encoding.
+
+Outputs to a specific bitrate. This mode is analogous to CBR in AAC/MP3
+encoders and managed mode in vorbis coders. This delivers less consistent
+quality than VBR mode but consistent bitrate.
+.IP "--hard-cbr"
+Use hard constant bitrate encoding.
+
+With hard-cbr every frame will be exactly the same size, similar to how
+speech codecs work. This delivers lower overall quality but is useful
+ where bitrate changes might leak data in encrypted channels or on
+synchronous transports.
+.IP "--comp N"
+Encoding computational complexity (0-10, default: 10). Zero gives the
+fastest encodes but lower quality, while 10 gives the highest quality
+but slower encoding.
+.IP "--framesize N"
+Maximum frame size in milliseconds (2.5, 5, 10, 20, 40, 60, default: 20)
+.br
+Smaller framesizes achieve lower latency but less quality at a given
+bitrate.
+.br
+Sizes greater than 20ms are only interesting at fairly low
+bitrates.
+.IP "--expect-loss N"
+Percentage packet loss to expect (default: 0)
+.IP "--downmix-mono"
+Downmix to mono
+.IP "--downmix-stereo"
+Downmix to stereo (if >2 channels input)
+.IP "--max-delay N"
+Maximum container delay in milliseconds (0-1000, default: 1000)
+.IP "--serial n"
+Forces the stream serial number to a specified value (instead of being random).
+This is used to make the encoder deterministic for testing and is not generally recommended.
+.IP "--save-range file"
+Saves check values for every frame to a file
+.IP "--set-ctl-int x=y"
+Pass the encoder control x with value y (advanced)
+Preface with s: to direct the ctl to multistream s
+.br
+This may be used multiple times
+
+.IP "--comment tag=value"
+Add an extra comment.  This may be used multiple times, and all
+instances will be added to each of the input files specified. The argument
+should be in the form "tag=value".
+See the vorbis-comment specification for well known tag names:
+http://www.xiph.org/vorbis/doc/v-comment.html
+.IP "--artist artist"
+Set the artist comment field to
+.I artist.
+This may be used multiple times to list contributing artists individually.
+Note that some playback software does not display multiple artists gracefully.
+.IP "--title title"
+Set the track title comment field to
+.I title
+.IP "--album album"
+Set the album or collection title field to
+.I album
+.IP "--date YYYY-MM-DD"
+Set the date comment field to
+.I YYYY-MM-DD
+.IP "--genre genre"
+Set the genre comment field to
+.I genre.
+This option may be specified multiple times to tag a track with
+multiple overlapping genres.
+.IP "--picture filename|specification"
+Attach album art for the track.
+
+Either a
+.I filename
+for the artwork or a more complete
+.I specification
+form can be used.
+The picture is added to a
+.B METADATA_BLOCK_PICTURE
+comment field similar to what is used in
+.SM FLAC.
+The
+.I specification
+is a string whose parts are separated by | (pipe) characters.
+Some parts may be left empty to invoke default values.
+Passing a plain filename is just shorthand for the "||||filename"
+specification.
+
+The format of
+.I specification
+is [\fBtype\fR]|[\fBmime-type\fR]|[\fBdescription\fR]|[\fBwidth\fRx\fBheight\fRx\fBdepth\fR[/\fBcolors\fR]]|\fBfilename\fR
+
+.I type
+is an optional number describing the nature of the picture.
+Defined values are from one of:
+
+  0: Other
+.br
+  1: 32x32 pixel 'file icon' (PNG only)
+.br
+  2: Other file icon
+.br
+  3: Cover (front)
+.br
+  4: Cover (back)
+.br
+  5: Leaflet page
+.br
+  6: Media (e.g., label side of a CD)
+.br
+  7: Lead artist/lead performer/soloist
+.br
+  8: Artist/performer
+.br
+  9: Conductor
+.br
+ 10: Band/Orchestra
+.br
+ 11: Composer
+.br
+ 12: Lyricist/text writer
+.br
+ 13: Recording location
+.br
+ 14: During recording
+.br
+ 15: During performance
+.br
+ 16: Movie/video screen capture
+.br
+ 17: A bright colored fish
+.br
+ 18: Illustration
+.br
+ 19: Band/artist logotype
+.br
+ 20: Publisher/studio logotype
+
+The default is 3 (front cover).
+More than one --picture option can be specified to attach multiple pictures.
+There may only be one picture each of type 1 and 2 in a file.
+
+.I mime-type
+is optional. If left blank, it will be detected from the file. For
+best compatibility with players, use pictures with a
+.I mime-type
+of image/jpeg or image/png. The
+.I mime-type
+can also be "-->" to mean that
+.I filename
+is actually a URL to an image, though this use is discouraged.
+The file at the URL will not be fetched.
+The URL itself is stored in the comment field.
+
+.I description
+is optional. The default is an empty string.
+
+The next part specifies the resolution and color information. If the
+.I mime-type
+is image/jpeg, image/png, or image/gif, you can usually leave this empty and
+they can be detected from the file. Otherwise, you must specify the width in
+pixels, height in pixels, and color depth in bits-per-pixel. If the image has
+indexed colors you should also specify the number of colors used. If possible,
+these are checked against the file for accuracy.
+
+.I filename
+is the path to the picture file to be imported, or the URL if the
+.I mime-type
+is -->.
+.IP "--padding n"
+Reserve
+.I n
+extra bytes for metadata tags. This can make later tag editing more
+efficient. Defaults to 512.
+.IP "--discard-comments"
+Don't propagate metadata tags from the input file.
+.IP "--discard-pictures"
+Don't propagate pictures or art from the input file.
+
+.IP "--raw"
+Raw (headerless) PCM input
+.IP "--raw-bits N"
+Set bits/sample for raw input (default: 16)
+.IP "--raw-rate N"
+Set sampling rate for raw input (default: 48000)
+.IP "--raw-chan N"
+Set number of channels for raw input (default: 2)
+.IP "--raw-endianness [0/1]"
+Set the endianness for raw input: 1 for bigendian, 0 for little (defaults to 0)
+.IP "--ignorelength"
+Always ignore the datalength in Wave headers. Opusenc automatically ignores
+the length when its implausible (very small or very large) but some STDIN
+usage may still need this option to avoid truncation.
+
+.SH EXAMPLES
+
+Simplest usage. Take input as input.wav and produce output as output.opus:
+.RS
+opusenc input.wav output.opus
+.RE
+.PP
+
+Produce a very high quality encode with a target rate of 160kbps:
+.RS
+opusenc --bitrate 160 input.wav output.opus
+.RE
+.PP
+
+Record and send a live stream to an Icecast HTTP streaming server using oggfwd:
+.RS
+arecord -c 2 -r 48000 -twav - | opusenc --bitrate 96 -  - | oggfwd icecast.somewhere.org 8000 password /stream.opus
+.RE
+.PP
+
+.SH NOTES
+
+While it is possible to use opusenc for low latency streaming (e.g. with --max-delay set to 0
+and netcat instead of Icecast) it's not really designed for this, and the Ogg container
+and TCP transport aren't the best tools for that application. Shell
+pipelines themselves will often have high buffering. The ability to set
+framesizes as low as 2.5 ms in opusenc mostly exists to try out the quality
+of the format with low latency settings, but not really for actual low
+latency usage.
+.br
+Interactive usage should use UDP/RTP directly.
+
+.SH AUTHORS
+.br
+Gregory Maxwell <greg@xiph.org>
+
+.SH SEE ALSO
+.BR opusdec (1),
+.BR opusinfo (1),
+.BR oggfwd (1)
diff --git a/man/opusinfo.1 b/man/opusinfo.1
new file mode 100644
index 0000000..193f54e
--- /dev/null
+++ b/man/opusinfo.1
@@ -0,0 +1,72 @@
+.\" Process this file with
+.\" groff -man -Tascii opusinfo.1
+.\"
+.TH opusinfo 1 2012-08-31 "Xiph.Org Foundation" "opus-tools"
+
+.SH NAME
+opusinfo \- gives information about Opus files and does extensive validity checking
+
+.SH SYNOPSIS
+.B opusinfo
+[
+.B -q
+|
+.B -v
+] [
+.B -h
+] [
+.B -V
+]
+.I file1.opus
+.B ...
+.I fileN.opus
+
+.SH DESCRIPTION
+.B opusinfo
+reads one or more Opus files and prints information about stream contents
+(including chained and/or multiplexed streams) to standard output. It will
+detect (but not correct) a wide range of common defects, with many
+additional checks specifically for Opus streams.
+
+For all stream types
+.B opusinfo
+will print the filename being processed, the stream serial numbers, and various
+common error conditions.
+
+For
+.B Opus
+streams, information including the version used for encoding, number of channels
+and other header information, the bitrate and playback length, the contents of the
+comment header, and general statistics about the stream are printed.
+
+Opusinfo is a fork of \fBogginfo\fR(1) with the non-opus parts largely removed.
+
+.SH OPTIONS
+.IP -h
+Show a help and usage message.
+.IP -q
+Quiet mode. This may be specified multiple times. Doing so once will remove
+the detailed informative messages; twice will remove warnings as well.
+.IP -v
+Verbose mode. At the current time, this does not do anything.
+.IP -V
+Show program version info and exit.
+
+.SH NOTES
+
+There are many kinds of errored, invalid, non-normative, or otherwise
+unwise stream constructions which opusinfo will not produce warnings
+on. Passing opusinfo with flying colors is not certification of the
+correctness of a stream. Future versions may detect more error
+conditions.
+
+.SH AUTHORS
+.br
+Michael Smith <msmith@xiph.org>
+.br
+Gregory Maxwell <greg@xiph.org>
+
+.SH SEE ALSO
+
+.BR opusdec (1),
+.BR opusenc (1)
diff --git a/man/opusrtp.1 b/man/opusrtp.1
new file mode 100644
index 0000000..e4297b0
--- /dev/null
+++ b/man/opusrtp.1
@@ -0,0 +1,58 @@
+.\" Process this file with
+.\" groff -man -Tascii opusrtp.1
+.\"
+.TH opusrtp 1 2012-08-31 "Xiph.Org Foundation" "opus-tools"
+
+.SH NAME
+opusrtp \- encapsulate Opus audio in RTP
+
+.SH SYNOPSIS
+.B opusrtp
+[
+.B -hV
+]
+[
+.B --sniff
+]
+.B file.opus
+[
+.B file2.opus ...
+]
+
+.SH DESCRIPTION
+
+.B opusrtp
+Demonstration tool for sending and receiving Opus audio data in RTP,
+used for interactive applications on the internet.
+
+By default, Opus audio from each given file is sent as an RTP stream.
+
+.SH "OPTIONS"
+.IP "-h, --help"
+Print help message
+.IP "-V, --version"
+Display version information
+.IP "--quiet"
+Suppresses program output
+.IP "--sniff"
+Sniff the network for active RTP sessions and save them to .opus
+files. This can be useful for debugging other Opus RTP implementations.
+For this function to work, the program must be run with superuser
+privileges.
+
+.SH AUTHORS
+.br
+Ralph Giles <giles@thaumas.net>
+
+.SH BUGS
+
+Only the sniff mode is implemented; the tool should do normal unicast
+and multicast send/receive.
+
+The sniff mode should allow specifying device/host/port/payload type
+to limit capture. All that is hard-coded.
+
+.SH SEE ALSO
+.BR opusdec (1),
+.BR opusenc (1),
+.BR opusinfo (1)
diff --git a/opus-tools.sln b/opus-tools.sln
new file mode 100644
index 0000000..cee0e35
--- /dev/null
+++ b/opus-tools.sln
@@ -0,0 +1,32 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opusdec", "src\opusdec.vcxproj", "{26A9EEB4-030D-52F5-8999-7479F39B0420}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opusenc", "src\opusenc.vcxproj", "{0A40D595-3CCF-C117-31DC-2AC7A03419AF}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opusinfo", "src\opusinfo.vcxproj", "{63B119F8-86A2-2F2A-FBA5-E10744E703CB}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{26A9EEB4-030D-52F5-8999-7479F39B0420}.Debug|Win32.ActiveCfg = Debug|Win32
+		{26A9EEB4-030D-52F5-8999-7479F39B0420}.Debug|Win32.Build.0 = Debug|Win32
+		{26A9EEB4-030D-52F5-8999-7479F39B0420}.Release|Win32.ActiveCfg = Release|Win32
+		{26A9EEB4-030D-52F5-8999-7479F39B0420}.Release|Win32.Build.0 = Release|Win32
+		{0A40D595-3CCF-C117-31DC-2AC7A03419AF}.Debug|Win32.ActiveCfg = Debug|Win32
+		{0A40D595-3CCF-C117-31DC-2AC7A03419AF}.Debug|Win32.Build.0 = Debug|Win32
+		{0A40D595-3CCF-C117-31DC-2AC7A03419AF}.Release|Win32.ActiveCfg = Release|Win32
+		{0A40D595-3CCF-C117-31DC-2AC7A03419AF}.Release|Win32.Build.0 = Release|Win32
+		{63B119F8-86A2-2F2A-FBA5-E10744E703CB}.Debug|Win32.ActiveCfg = Debug|Win32
+		{63B119F8-86A2-2F2A-FBA5-E10744E703CB}.Debug|Win32.Build.0 = Debug|Win32
+		{63B119F8-86A2-2F2A-FBA5-E10744E703CB}.Release|Win32.ActiveCfg = Release|Win32
+		{63B119F8-86A2-2F2A-FBA5-E10744E703CB}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/share/getopt.c b/share/getopt.c
new file mode 100644
index 0000000..a11cf22
--- /dev/null
+++ b/share/getopt.c
@@ -0,0 +1,1047 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to drepper@gnu.org
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99
+   	Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+# ifndef const
+#  define const
+# endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+# include <gnu-versions.h>
+# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#  define ELIDE_CODE
+# endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef	__GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+# include <stdlib.h>
+# include <unistd.h>
+#endif	/* GNU C library.  */
+
+#ifdef VMS
+# include <unixlib.h>
+# if HAVE_STRING_H - 0
+#  include <string.h>
+# endif
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+   When compiling libc, the _ macro is predefined.  */
+# ifdef HAVE_LIBINTL_H
+#  include <libintl.h>
+#  define _(msgid)	gettext (msgid)
+# else
+#  define _(msgid)	(msgid)
+# endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* 1003.2 says this must be 1 before any call.  */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+   causes problems with re-calling getopt as programs generally don't
+   know that. */
+
+int __getopt_initialized;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+
+#ifdef	__GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+# include <string.h>
+# define my_index	strchr
+#else
+
+#include <string.h>
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+#ifndef getenv
+extern char *getenv ();
+#endif
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+	return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+# if (!defined __STDC__ || !__STDC__) && !defined strlen
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen (const char *);
+# endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+   indicating ARGV elements that should not be considered arguments.  */
+
+/* Defined in getopt_init.c  */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+   is valid for the getopt call we must make sure that the ARGV passed
+   to getopt is that one passed to the process.  */
+static void
+__attribute__ ((unused))
+store_args_and_env (int argc, char *const *argv)
+{
+  /* XXX This is no good solution.  We should rather copy the args so
+     that we can compare them later.  But we must not use malloc(3).  */
+  original_argc = argc;
+  original_argv = argv;
+}
+# ifdef text_set_element
+text_set_element (__libc_subinit, store_args_and_env);
+# endif /* text_set_element */
+
+# define SWAP_FLAGS(ch1, ch2) \
+  if (nonoption_flags_len > 0)						      \
+    {									      \
+      char __tmp = __getopt_nonoption_flags[ch1];			      \
+      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];	      \
+      __getopt_nonoption_flags[ch2] = __tmp;				      \
+    }
+#else	/* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif	/* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+#if defined __STDC__ && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+#ifdef _LIBC
+  /* First make sure the handling of the `__getopt_nonoption_flags'
+     string can work normally.  Our top argument must be in the range
+     of the string.  */
+  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+    {
+      /* We must extend the array.  The user plays games with us and
+	 presents new arguments.  */
+      char *new_str = malloc (top + 1);
+      if (new_str == NULL)
+	nonoption_flags_len = nonoption_flags_max_len = 0;
+      else
+	{
+	  memset (__mempcpy (new_str, __getopt_nonoption_flags,
+			     nonoption_flags_max_len),
+		  '\0', top + 1 - nonoption_flags_max_len);
+	  nonoption_flags_max_len = top + 1;
+	  __getopt_nonoption_flags = new_str;
+	}
+    }
+#endif
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+	{
+	  /* Bottom segment is the short one.  */
+	  int len = middle - bottom;
+	  register int i;
+
+	  /* Swap it with the top part of the top segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[top - (middle - bottom) + i];
+	      argv[top - (middle - bottom) + i] = tem;
+	      SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+	    }
+	  /* Exclude the moved bottom segment from further swapping.  */
+	  top -= len;
+	}
+      else
+	{
+	  /* Top segment is the short one.  */
+	  int len = top - middle;
+	  register int i;
+
+	  /* Swap it with the bottom part of the bottom segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[middle + i];
+	      argv[middle + i] = tem;
+	      SWAP_FLAGS (bottom + i, middle + i);
+	    }
+	  /* Exclude the moved top segment from further swapping.  */
+	  bottom += len;
+	}
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+#if defined __STDC__ && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  first_nonopt = last_nonopt = optind;
+
+  nextchar = NULL;
+
+  posixly_correct = getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (posixly_correct != NULL)
+    ordering = REQUIRE_ORDER;
+  else
+    ordering = PERMUTE;
+
+#ifdef _LIBC
+  if (posixly_correct == NULL
+      && argc == original_argc && argv == original_argv)
+    {
+      if (nonoption_flags_max_len == 0)
+	{
+	  if (__getopt_nonoption_flags == NULL
+	      || __getopt_nonoption_flags[0] == '\0')
+	    nonoption_flags_max_len = -1;
+	  else
+	    {
+	      const char *orig_str = __getopt_nonoption_flags;
+	      int len = nonoption_flags_max_len = strlen (orig_str);
+	      if (nonoption_flags_max_len < argc)
+		nonoption_flags_max_len = argc;
+	      __getopt_nonoption_flags =
+		(char *) malloc (nonoption_flags_max_len);
+	      if (__getopt_nonoption_flags == NULL)
+		nonoption_flags_max_len = -1;
+	      else
+		memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+			'\0', nonoption_flags_max_len - len);
+	    }
+	}
+      nonoption_flags_len = nonoption_flags_max_len;
+    }
+  else
+    nonoption_flags_len = 0;
+#endif
+
+  return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns -1.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  optarg = NULL;
+
+  if (optind == 0 || !__getopt_initialized)
+    {
+      if (optind == 0)
+	optind = 1;	/* Don't scan ARGV[0], the program name.  */
+      optstring = _getopt_initialize (argc, argv, optstring);
+      __getopt_initialized = 1;
+    }
+
+  /* Test whether ARGV[optind] points to a non-option argument.
+     Either it does not have option syntax, or there is an environment flag
+     from the shell indicating it is not an option.  The later information
+     is only used when the used in the GNU libc.  */
+#ifdef _LIBC
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'	      \
+		      || (optind < nonoption_flags_len			      \
+			  && __getopt_nonoption_flags[optind] == '1'))
+#else
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+	 moved back by the user (who may also have changed the arguments).  */
+      if (last_nonopt > optind)
+	last_nonopt = optind;
+      if (first_nonopt > optind)
+	first_nonopt = optind;
+
+      if (ordering == PERMUTE)
+	{
+	  /* If we have just processed some options following some non-options,
+	     exchange them so that the options come first.  */
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (last_nonopt != optind)
+	    first_nonopt = optind;
+
+	  /* Skip any additional non-options
+	     and extend the range of non-options previously skipped.  */
+
+	  while (optind < argc && NONOPTION_P)
+	    optind++;
+	  last_nonopt = optind;
+	}
+
+      /* The special ARGV-element `--' means premature end of options.
+	 Skip it like a null option,
+	 then exchange with previous non-options as if it were an option,
+	 then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+	{
+	  optind++;
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (first_nonopt == last_nonopt)
+	    first_nonopt = optind;
+	  last_nonopt = argc;
+
+	  optind = argc;
+	}
+
+      /* If we have done all the ARGV-elements, stop the scan
+	 and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+	{
+	  /* Set the next-arg-index to point at the non-options
+	     that we previously skipped, so the caller will digest them.  */
+	  if (first_nonopt != last_nonopt)
+	    optind = first_nonopt;
+	  return -1;
+	}
+
+      /* If we have come to a non-option and did not permute it,
+	 either stop the scan or describe it to the caller and pass it by.  */
+
+      if (NONOPTION_P)
+	{
+	  if (ordering == REQUIRE_ORDER)
+	    return -1;
+	  optarg = argv[optind++];
+	  return 1;
+	}
+
+      /* We have found another option-ARGV-element.
+	 Skip the initial punctuation.  */
+
+      nextchar = (argv[optind] + 1
+		  + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[optind][1] == '-'
+	  || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound = -1;
+      int option_index;
+
+      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+	/* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+	 or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+	if (!strncmp (p->name, nextchar, nameend - nextchar))
+	  {
+	    if ((unsigned int) (nameend - nextchar)
+		== (unsigned int) strlen (p->name))
+	      {
+		/* Exact match found.  */
+		pfound = p;
+		indfound = option_index;
+		exact = 1;
+		break;
+	      }
+	    else if (pfound == NULL)
+	      {
+		/* First nonexact match found.  */
+		pfound = p;
+		indfound = option_index;
+	      }
+	    else
+	      /* Second or later nonexact match found.  */
+	      ambig = 1;
+	  }
+
+      if (ambig && !exact)
+	{
+	  if (opterr)
+	    fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+		     argv[0], argv[optind]);
+	  nextchar += strlen (nextchar);
+	  optind++;
+	  optopt = 0;
+	  return '?';
+	}
+
+      if (pfound != NULL)
+	{
+	  option_index = indfound;
+	  optind++;
+	  if (*nameend)
+	    {
+	      /* Don't test has_arg with >, because some C compilers don't
+		 allow it to be used on enums.  */
+	      if (pfound->has_arg)
+		optarg = nameend + 1;
+	      else
+		{
+		  if (opterr)
+		    {
+		      if (argv[optind - 1][1] == '-')
+			/* --option */
+			fprintf (stderr,
+				 _("%s: option `--%s' doesn't allow an argument\n"),
+				 argv[0], pfound->name);
+		      else
+			/* +option or -option */
+			fprintf (stderr,
+				 _("%s: option `%c%s' doesn't allow an argument\n"),
+				 argv[0], argv[optind - 1][0], pfound->name);
+		    }
+
+		  nextchar += strlen (nextchar);
+
+		  optopt = pfound->val;
+		  return '?';
+		}
+	    }
+	  else if (pfound->has_arg == 1)
+	    {
+	      if (optind < argc)
+		optarg = argv[optind++];
+	      else
+		{
+		  if (opterr)
+		    fprintf (stderr,
+			   _("%s: option `%s' requires an argument\n"),
+			   argv[0], argv[optind - 1]);
+		  nextchar += strlen (nextchar);
+		  optopt = pfound->val;
+		  return optstring[0] == ':' ? ':' : '?';
+		}
+	    }
+	  nextchar += strlen (nextchar);
+	  if (longind != NULL)
+	    *longind = option_index;
+	  if (pfound->flag)
+	    {
+	      *(pfound->flag) = pfound->val;
+	      return 0;
+	    }
+	  return pfound->val;
+	}
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+	 or the option starts with '--' or is not a valid short
+	 option, then it's an error.
+	 Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+	  || my_index (optstring, *nextchar) == NULL)
+	{
+	  if (opterr)
+	    {
+	      if (argv[optind][1] == '-')
+		/* --option */
+		fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+			 argv[0], nextchar);
+	      else
+		/* +option or -option */
+		fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+			 argv[0], argv[optind][0], nextchar);
+	    }
+	  nextchar = (char *) "";
+	  optind++;
+	  optopt = 0;
+	  return '?';
+	}
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+	if (opterr)
+	  {
+	    if (posixly_correct)
+	      /* 1003.2 specifies the format of this message.  */
+	      fprintf (stderr, _("%s: illegal option -- %c\n"),
+		       argv[0], c);
+	    else
+	      fprintf (stderr, _("%s: invalid option -- %c\n"),
+		       argv[0], c);
+	  }
+	optopt = c;
+	return '?';
+      }
+    /* Convenience. Treat POSIX -W foo same as long option --foo */
+    if (temp[0] == 'W' && temp[1] == ';')
+      {
+	char *nameend;
+	const struct option *p;
+	const struct option *pfound = NULL;
+	int exact = 0;
+	int ambig = 0;
+	int indfound = 0;
+	int option_index;
+
+	/* This is an option that requires an argument.  */
+	if (*nextchar != '\0')
+	  {
+	    optarg = nextchar;
+	    /* If we end this ARGV-element by taking the rest as an arg,
+	       we must advance to the next element now.  */
+	    optind++;
+	  }
+	else if (optind == argc)
+	  {
+	    if (opterr)
+	      {
+		/* 1003.2 specifies the format of this message.  */
+		fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+			 argv[0], c);
+	      }
+	    optopt = c;
+	    if (optstring[0] == ':')
+	      c = ':';
+	    else
+	      c = '?';
+	    return c;
+	  }
+	else
+	  /* We already incremented `optind' once;
+	     increment it again when taking next ARGV-elt as argument.  */
+	  optarg = argv[optind++];
+
+	/* optarg is now the argument, see if it's in the
+	   table of longopts.  */
+
+	for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+	  /* Do nothing.  */ ;
+
+	/* Test all long options for either exact match
+	   or abbreviated matches.  */
+	for (p = longopts, option_index = 0; p->name; p++, option_index++)
+	  if (!strncmp (p->name, nextchar, nameend - nextchar))
+	    {
+	      if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+		{
+		  /* Exact match found.  */
+		  pfound = p;
+		  indfound = option_index;
+		  exact = 1;
+		  break;
+		}
+	      else if (pfound == NULL)
+		{
+		  /* First nonexact match found.  */
+		  pfound = p;
+		  indfound = option_index;
+		}
+	      else
+		/* Second or later nonexact match found.  */
+		ambig = 1;
+	    }
+	if (ambig && !exact)
+	  {
+	    if (opterr)
+	      fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+		       argv[0], argv[optind]);
+	    nextchar += strlen (nextchar);
+	    optind++;
+	    return '?';
+	  }
+	if (pfound != NULL)
+	  {
+	    option_index = indfound;
+	    if (*nameend)
+	      {
+		/* Don't test has_arg with >, because some C compilers don't
+		   allow it to be used on enums.  */
+		if (pfound->has_arg)
+		  optarg = nameend + 1;
+		else
+		  {
+		    if (opterr)
+		      fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+			       argv[0], pfound->name);
+
+		    nextchar += strlen (nextchar);
+		    return '?';
+		  }
+	      }
+	    else if (pfound->has_arg == 1)
+	      {
+		if (optind < argc)
+		  optarg = argv[optind++];
+		else
+		  {
+		    if (opterr)
+		      fprintf (stderr,
+			       _("%s: option `%s' requires an argument\n"),
+			       argv[0], argv[optind - 1]);
+		    nextchar += strlen (nextchar);
+		    return optstring[0] == ':' ? ':' : '?';
+		  }
+	      }
+	    nextchar += strlen (nextchar);
+	    if (longind != NULL)
+	      *longind = option_index;
+	    if (pfound->flag)
+	      {
+		*(pfound->flag) = pfound->val;
+		return 0;
+	      }
+	    return pfound->val;
+	  }
+	  nextchar = NULL;
+	  return 'W';	/* Let the application handle it.   */
+      }
+    if (temp[1] == ':')
+      {
+	if (temp[2] == ':')
+	  {
+	    /* This is an option that accepts an argument optionally.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		optind++;
+	      }
+	    else
+	      optarg = NULL;
+	    nextchar = NULL;
+	  }
+	else
+	  {
+	    /* This is an option that requires an argument.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		/* If we end this ARGV-element by taking the rest as an arg,
+		   we must advance to the next element now.  */
+		optind++;
+	      }
+	    else if (optind == argc)
+	      {
+		if (opterr)
+		  {
+		    /* 1003.2 specifies the format of this message.  */
+		    fprintf (stderr,
+			   _("%s: option requires an argument -- %c\n"),
+			   argv[0], c);
+		  }
+		optopt = c;
+		if (optstring[0] == ':')
+		  c = ':';
+		else
+		  c = '?';
+	      }
+	    else
+	      /* We already incremented `optind' once;
+		 increment it again when taking next ARGV-elt as argument.  */
+	      optarg = argv[optind++];
+	    nextchar = NULL;
+	  }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+			   (const struct option *) 0,
+			   (int *) 0,
+			   0);
+}
+
+#endif	/* Not ELIDE_CODE.  */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == -1)
+	break;
+
+      switch (c)
+	{
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/share/getopt1.c b/share/getopt1.c
new file mode 100644
index 0000000..3d264f2
--- /dev/null
+++ b/share/getopt1.c
@@ -0,0 +1,188 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
+     Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef	NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif	/* Not ELIDE_CODE.  */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+	{"add", 1, 0, 0},
+	{"append", 0, 0, 0},
+	{"delete", 1, 0, 0},
+	{"verbose", 0, 0, 0},
+	{"create", 0, 0, 0},
+	{"file", 1, 0, 0},
+	{0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+		       long_options, &option_index);
+      if (c == -1)
+	break;
+
+      switch (c)
+	{
+	case 0:
+	  printf ("option %s", long_options[option_index].name);
+	  if (optarg)
+	    printf (" with arg %s", optarg);
+	  printf ("\n");
+	  break;
+
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case 'd':
+	  printf ("option d with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/src/arch.h b/src/arch.h
new file mode 100644
index 0000000..3b47ed9
--- /dev/null
+++ b/src/arch.h
@@ -0,0 +1,239 @@
+/* Copyright (C) 2003 Jean-Marc Valin */
+/**
+   @file arch.h
+   @brief Various architecture definitions Speex
+*/
+/*
+   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 ARCH_H
+#define ARCH_H
+
+#ifndef SPEEX_VERSION
+#define SPEEX_MAJOR_VERSION 1         /**< Major Speex version. */
+#define SPEEX_MINOR_VERSION 1         /**< Minor Speex version. */
+#define SPEEX_MICRO_VERSION 15        /**< Micro Speex version. */
+#define SPEEX_EXTRA_VERSION ""        /**< Extra Speex version. */
+#define SPEEX_VERSION "speex-1.2beta3"  /**< Speex version string. */
+#endif
+
+/* A couple test to catch stupid option combinations */
+#ifdef FIXED_POINT
+
+#ifdef FLOATING_POINT
+#error You cannot compile as floating point and fixed point at the same time
+#endif
+#ifdef _USE_SSE
+#error SSE is only for floating-point
+#endif
+#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
+#error Make up your mind. What CPU do you have?
+#endif
+#ifdef VORBIS_PSYCHO
+#error Vorbis-psy model currently not implemented in fixed-point
+#endif
+
+#else
+
+#ifndef FLOATING_POINT
+#error You now need to define either FIXED_POINT or FLOATING_POINT
+#endif
+#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
+#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
+#endif
+#ifdef FIXED_POINT_DEBUG
+#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
+#endif
+
+
+#endif
+
+#ifndef OUTSIDE_SPEEX
+#include "../include/speex/speex_types.h"
+#endif
+
+#define ABS(x) ((x) < 0 ? (-(x)) : (x))      /**< Absolute integer value. */
+#define ABS16(x) ((x) < 0 ? (-(x)) : (x))    /**< Absolute 16-bit value.  */
+#define MIN16(a,b) ((a) < (b) ? (a) : (b))   /**< Maximum 16-bit value.   */
+#define MAX16(a,b) ((a) > (b) ? (a) : (b))   /**< Maximum 16-bit value.   */
+#define ABS32(x) ((x) < 0 ? (-(x)) : (x))    /**< Absolute 32-bit value.  */
+#define MIN32(a,b) ((a) < (b) ? (a) : (b))   /**< Maximum 32-bit value.   */
+#define MAX32(a,b) ((a) > (b) ? (a) : (b))   /**< Maximum 32-bit value.   */
+
+#ifdef FIXED_POINT
+
+typedef spx_int16_t spx_word16_t;
+typedef spx_int32_t spx_word32_t;
+typedef spx_word32_t spx_mem_t;
+typedef spx_word16_t spx_coef_t;
+typedef spx_word16_t spx_lsp_t;
+typedef spx_word32_t spx_sig_t;
+
+#define Q15ONE 32767
+
+#define LPC_SCALING  8192
+#define SIG_SCALING  16384
+#define LSP_SCALING  8192.
+#define GAMMA_SCALING 32768.
+#define GAIN_SCALING 64
+#define GAIN_SCALING_1 0.015625
+
+#define LPC_SHIFT    13
+#define LSP_SHIFT    13
+#define SIG_SHIFT    14
+#define GAIN_SHIFT   6
+
+#define VERY_SMALL 0
+#define VERY_LARGE32 ((spx_word32_t)2147483647)
+#define VERY_LARGE16 ((spx_word16_t)32767)
+#define Q15_ONE ((spx_word16_t)32767)
+
+
+#ifdef FIXED_DEBUG
+#include "fixed_debug.h"
+#else
+
+#include "fixed_generic.h"
+
+#ifdef ARM5E_ASM
+#include "fixed_arm5e.h"
+#elif defined (ARM4_ASM)
+#include "fixed_arm4.h"
+#elif defined (BFIN_ASM)
+#include "fixed_bfin.h"
+#endif
+
+#endif
+
+
+#else
+
+typedef float spx_mem_t;
+typedef float spx_coef_t;
+typedef float spx_lsp_t;
+typedef float spx_sig_t;
+typedef float spx_word16_t;
+typedef float spx_word32_t;
+
+#define Q15ONE 1.0f
+#define LPC_SCALING  1.f
+#define SIG_SCALING  1.f
+#define LSP_SCALING  1.f
+#define GAMMA_SCALING 1.f
+#define GAIN_SCALING 1.f
+#define GAIN_SCALING_1 1.f
+
+
+#define VERY_SMALL 1e-15f
+#define VERY_LARGE32 1e15f
+#define VERY_LARGE16 1e15f
+#define Q15_ONE ((spx_word16_t)1.f)
+
+#define QCONST16(x,bits) (x)
+#define QCONST32(x,bits) (x)
+
+#define NEG16(x) (-(x))
+#define NEG32(x) (-(x))
+#define EXTRACT16(x) (x)
+#define EXTEND32(x) (x)
+#define SHR16(a,shift) (a)
+#define SHL16(a,shift) (a)
+#define SHR32(a,shift) (a)
+#define SHL32(a,shift) (a)
+#define PSHR16(a,shift) (a)
+#define PSHR32(a,shift) (a)
+#define VSHR32(a,shift) (a)
+#define SATURATE16(x,a) (x)
+#define SATURATE32(x,a) (x)
+
+#define PSHR(a,shift)       (a)
+#define SHR(a,shift)       (a)
+#define SHL(a,shift)       (a)
+#define SATURATE(x,a) (x)
+
+#define ADD16(a,b) ((a)+(b))
+#define SUB16(a,b) ((a)-(b))
+#define ADD32(a,b) ((a)+(b))
+#define SUB32(a,b) ((a)-(b))
+#define MULT16_16_16(a,b)     ((a)*(b))
+#define MULT16_16(a,b)     ((spx_word32_t)(a)*(spx_word32_t)(b))
+#define MAC16_16(c,a,b)     ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
+
+#define MULT16_32_Q11(a,b)     ((a)*(b))
+#define MULT16_32_Q13(a,b)     ((a)*(b))
+#define MULT16_32_Q14(a,b)     ((a)*(b))
+#define MULT16_32_Q15(a,b)     ((a)*(b))
+#define MULT16_32_P15(a,b)     ((a)*(b))
+
+#define MAC16_32_Q11(c,a,b)     ((c)+(a)*(b))
+#define MAC16_32_Q15(c,a,b)     ((c)+(a)*(b))
+
+#define MAC16_16_Q11(c,a,b)     ((c)+(a)*(b))
+#define MAC16_16_Q13(c,a,b)     ((c)+(a)*(b))
+#define MAC16_16_P13(c,a,b)     ((c)+(a)*(b))
+#define MULT16_16_Q11_32(a,b)     ((a)*(b))
+#define MULT16_16_Q13(a,b)     ((a)*(b))
+#define MULT16_16_Q14(a,b)     ((a)*(b))
+#define MULT16_16_Q15(a,b)     ((a)*(b))
+#define MULT16_16_P15(a,b)     ((a)*(b))
+#define MULT16_16_P13(a,b)     ((a)*(b))
+#define MULT16_16_P14(a,b)     ((a)*(b))
+
+#define DIV32_16(a,b)     (((spx_word32_t)(a))/(spx_word16_t)(b))
+#define PDIV32_16(a,b)     (((spx_word32_t)(a))/(spx_word16_t)(b))
+#define DIV32(a,b)     (((spx_word32_t)(a))/(spx_word32_t)(b))
+#define PDIV32(a,b)     (((spx_word32_t)(a))/(spx_word32_t)(b))
+
+
+#endif
+
+
+#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
+
+/* 2 on TI C5x DSP */
+#define BYTES_PER_CHAR 2 
+#define BITS_PER_CHAR 16
+#define LOG2_BITS_PER_CHAR 4
+
+#else 
+
+#define BYTES_PER_CHAR 1
+#define BITS_PER_CHAR 8
+#define LOG2_BITS_PER_CHAR 3
+
+#endif
+
+
+
+#ifdef FIXED_DEBUG
+extern long long spx_mips;
+#endif
+
+
+#endif
diff --git a/src/audio-in.c b/src/audio-in.c
new file mode 100644
index 0000000..8ed037b
--- /dev/null
+++ b/src/audio-in.c
@@ -0,0 +1,1094 @@
+/* Copyright 2000-2002, Michael Smith <msmith@xiph.org>
+             2010, Monty <monty@xiph.org>
+   AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty <xiphmont@xiph.org>
+   (From GPL code in oggenc relicensed by permission from Monty and Msmith)
+   File: audio-in.c
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if !defined(_LARGEFILE_SOURCE)
+# define _LARGEFILE_SOURCE
+#endif
+#if !defined(_LARGEFILE64_SOURCE)
+# define _LARGEFILE64_SOURCE
+#endif
+#if !defined(_FILE_OFFSET_BITS)
+# define _FILE_OFFSET_BITS 64
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <math.h>
+
+#include "stack_alloc.h"
+
+#ifdef WIN32
+# include <windows.h> /*GetFileType()*/
+# include <io.h>      /*_get_osfhandle()*/
+#endif
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(X) gettext(X)
+#else
+#define _(X) (X)
+#define textdomain(X)
+#define bindtextdomain(X, Y)
+#endif
+#ifdef gettext_noop
+#define N_(X) gettext_noop(X)
+#else
+#define N_(X) (X)
+#endif
+
+#include <ogg/ogg.h>
+#include "opusenc.h"
+#include "speex_resampler.h"
+#include "lpc.h"
+#include "opus_header.h"
+#include "flac.h"
+
+/* Macros to read header data */
+#define READ_U32_LE(buf) \
+    (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U16_LE(buf) \
+    (((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U32_BE(buf) \
+    (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
+
+#define READ_U16_BE(buf) \
+    (((buf)[0]<<8)|((buf)[1]&0xff))
+
+/* Define the supported formats here */
+input_format formats[] = {
+    {wav_id, 12, wav_open, wav_close, "wav", N_("WAV file reader")},
+    {aiff_id, 12, aiff_open, wav_close, "aiff", N_("AIFF/AIFC file reader")},
+    {flac_id,     4, flac_open, flac_close, "flac", N_("FLAC file reader")},
+    {oggflac_id, 33, flac_open, flac_close, "ogg", N_("Ogg FLAC file reader")},
+    {NULL, 0, NULL, NULL, NULL, NULL}
+};
+
+input_format *open_audio_file(FILE *in, oe_enc_opt *opt)
+{
+    int j=0;
+    unsigned char *buf=NULL;
+    int buf_size=0, buf_filled=0;
+    int size,ret;
+
+    while(formats[j].id_func)
+    {
+        size = formats[j].id_data_len;
+        if(size >= buf_size)
+        {
+            buf = realloc(buf, size);
+            buf_size = size;
+        }
+
+        if(size > buf_filled)
+        {
+            ret = fread(buf+buf_filled, 1, buf_size-buf_filled, in);
+            buf_filled += ret;
+
+            if(buf_filled < size)
+            { /* File truncated */
+                j++;
+                continue;
+            }
+        }
+
+        if(formats[j].id_func(buf, buf_filled))
+        {
+            /* ok, we now have something that can handle the file */
+            if(formats[j].open_func(in, opt, buf, buf_filled)) {
+                free(buf);
+                return &formats[j];
+            }
+        }
+        j++;
+    }
+
+    free(buf);
+
+    return NULL;
+}
+
+static int seek_forward(FILE *in, unsigned int length)
+{
+    if(fseek(in, length, SEEK_CUR))
+    {
+        /* Failed. Do it the hard way. */
+        unsigned char buf[1024];
+        unsigned int seek_needed = length;
+        int seeked;
+        while(seek_needed > 0)
+        {
+            seeked = fread(buf, 1, seek_needed>1024?1024:seek_needed, in);
+            if(!seeked)
+                return 0; /* Couldn't read more, can't read file */
+            else
+                seek_needed -= seeked;
+        }
+    }
+    return 1;
+}
+
+static int find_wav_chunk(FILE *in, char *type, unsigned int *len)
+{
+    unsigned char buf[8];
+
+    while(1)
+    {
+        if(fread(buf,1,8,in) < 8) /* Suck down a chunk specifier */
+        {
+            fprintf(stderr, _("Warning: Unexpected EOF reading WAV header\n"));
+            return 0; /* EOF before reaching the appropriate chunk */
+        }
+
+        if(memcmp(buf, type, 4))
+        {
+            *len = READ_U32_LE(buf+4);
+            if(!seek_forward(in, *len))
+                return 0;
+
+            buf[4] = 0;
+            fprintf(stderr, _("Skipping chunk of type \"%s\", length %d\n"), buf, *len);
+        }
+        else
+        {
+            *len = READ_U32_LE(buf+4);
+            return 1;
+        }
+    }
+}
+
+static int find_aiff_chunk(FILE *in, char *type, unsigned int *len)
+{
+    unsigned char buf[8];
+    int restarted = 0;
+
+    while(1)
+    {
+        if(fread(buf,1,8,in) <8)
+        {
+            if(!restarted) {
+                /* Handle out of order chunks by seeking back to the start
+                 * to retry */
+                restarted = 1;
+                fseek(in, 12, SEEK_SET);
+                continue;
+            }
+            fprintf(stderr, _("Warning: Unexpected EOF in AIFF chunk\n"));
+            return 0;
+        }
+
+        *len = READ_U32_BE(buf+4);
+
+        if(memcmp(buf,type,4))
+        {
+            if((*len) & 0x1)
+                (*len)++;
+
+            if(!seek_forward(in, *len))
+                return 0;
+        }
+        else
+            return 1;
+    }
+}
+
+double read_IEEE80(unsigned char *buf)
+{
+    int s=buf[0]&0xff;
+    int e=((buf[0]&0x7f)<<8)|(buf[1]&0xff);
+    double f=((unsigned long)(buf[2]&0xff)<<24)|
+        ((buf[3]&0xff)<<16)|
+        ((buf[4]&0xff)<<8) |
+         (buf[5]&0xff);
+
+    if(e==32767)
+    {
+        if(buf[2]&0x80)
+            return HUGE_VAL; /* Really NaN, but this won't happen in reality */
+        else
+        {
+            if(s)
+                return -HUGE_VAL;
+            else
+                return HUGE_VAL;
+        }
+    }
+
+    f=ldexp(f,32);
+    f+= ((buf[6]&0xff)<<24)|
+        ((buf[7]&0xff)<<16)|
+        ((buf[8]&0xff)<<8) |
+         (buf[9]&0xff);
+
+    return ldexp(f, e-16446);
+}
+
+/* AIFF/AIFC support adapted from the old OggSQUISH application */
+int aiff_id(unsigned char *buf, int len)
+{
+    if(len<12) return 0; /* Truncated file, probably */
+
+    if(memcmp(buf, "FORM", 4))
+        return 0;
+
+    if(memcmp(buf+8, "AIF",3))
+        return 0;
+
+    if(buf[11]!='C' && buf[11]!='F')
+        return 0;
+
+    return 1;
+}
+
+static int aiff_permute_matrix[6][6] =
+{
+  {0},              /* 1.0 mono   */
+  {0,1},            /* 2.0 stereo */
+  {0,2,1},          /* 3.0 channel ('wide') stereo */
+  {0,1,2,3},        /* 4.0 discrete quadraphonic (WARN) */
+  {0,2,1,3,4},      /* 5.0 surround (WARN) */
+  {0,1,2,3,4,5},    /* 5.1 surround (WARN)*/
+};
+
+int aiff_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen)
+{
+    int aifc; /* AIFC or AIFF? */
+    unsigned int len;
+    unsigned char *buffer;
+    unsigned char buf2[8];
+    int bigendian = 1;
+    aiff_fmt format;
+    aifffile *aiff;
+    int i;
+    (void)buflen;/*unused*/
+
+    if(buf[11]=='C')
+        aifc=1;
+    else
+        aifc=0;
+
+    if(!find_aiff_chunk(in, "COMM", &len))
+    {
+        fprintf(stderr, _("Warning: No common chunk found in AIFF file\n"));
+        return 0; /* EOF before COMM chunk */
+    }
+
+    if(len < 18)
+    {
+        fprintf(stderr, _("Warning: Truncated common chunk in AIFF header\n"));
+        return 0; /* Weird common chunk */
+    }
+
+    buffer = alloca(len);
+
+    if(fread(buffer,1,len,in) < len)
+    {
+        fprintf(stderr, _("Warning: Unexpected EOF reading AIFF header\n"));
+        return 0;
+    }
+
+    format.channels = READ_U16_BE(buffer);
+    format.totalframes = READ_U32_BE(buffer+2);
+    format.samplesize = READ_U16_BE(buffer+6);
+    format.rate = (int)read_IEEE80(buffer+8);
+
+    if(aifc)
+    {
+        if(len < 22)
+        {
+            fprintf(stderr, _("Warning: AIFF-C header truncated.\n"));
+            return 0;
+        }
+
+        if(!memcmp(buffer+18, "NONE", 4))
+        {
+            bigendian = 1;
+        }
+        else if(!memcmp(buffer+18, "sowt", 4))
+        {
+            bigendian = 0;
+        }
+        else
+        {
+            fprintf(stderr, _("Warning: Can't handle compressed AIFF-C (%c%c%c%c)\n"), *(buffer+18), *(buffer+19), *(buffer+20), *(buffer+21));
+            return 0; /* Compressed. Can't handle */
+        }
+    }
+
+    if(!find_aiff_chunk(in, "SSND", &len))
+    {
+        fprintf(stderr, _("Warning: No SSND chunk found in AIFF file\n"));
+        return 0; /* No SSND chunk -> no actual audio */
+    }
+
+    if(len < 8)
+    {
+        fprintf(stderr, _("Warning: Corrupted SSND chunk in AIFF header\n"));
+        return 0;
+    }
+
+    if(fread(buf2,1,8, in) < 8)
+    {
+        fprintf(stderr, _("Warning: Unexpected EOF reading AIFF header\n"));
+        return 0;
+    }
+
+    format.offset = READ_U32_BE(buf2);
+    format.blocksize = READ_U32_BE(buf2+4);
+
+    if( format.blocksize == 0 &&
+        (format.samplesize == 16 || format.samplesize == 8))
+    {
+        /* From here on, this is very similar to the wav code. Oh well. */
+
+        opt->rate = format.rate;
+        opt->channels = format.channels;
+        opt->samplesize = format.samplesize;
+        opt->read_samples = wav_read; /* Similar enough, so we use the same */
+        opt->total_samples_per_channel = format.totalframes;
+
+        aiff = malloc(sizeof(aifffile));
+        aiff->f = in;
+        aiff->samplesread = 0;
+        aiff->channels = format.channels;
+        aiff->samplesize = format.samplesize;
+        aiff->totalsamples = format.totalframes;
+        aiff->bigendian = bigendian;
+        aiff->unsigned8bit = 0;
+
+        if(aiff->channels>3)
+          fprintf(stderr, _("WARNING: AIFF[-C] files with more than three channels use\n"
+                  "speaker locations incompatible with Vorbis surround definitions.\n"
+                  "Not performing channel location mapping.\n"));
+
+        opt->readdata = (void *)aiff;
+
+        aiff->channel_permute = malloc(aiff->channels * sizeof(int));
+        if (aiff->channels <= 6)
+            /* Where we know the mappings, use them. */
+            memcpy(aiff->channel_permute, aiff_permute_matrix[aiff->channels-1],
+                    sizeof(int) * aiff->channels);
+        else
+            /* Use a default 1-1 mapping */
+            for (i=0; i < aiff->channels; i++)
+                aiff->channel_permute[i] = i;
+
+        seek_forward(in, format.offset); /* Swallow some data */
+        return 1;
+    }
+    else
+    {
+        fprintf(stderr, _("ERROR: Unsupported AIFF/AIFC file.\n"
+                "Must be 8 or 16 bit PCM.\n"));
+        return 0;
+    }
+}
+
+int wav_id(unsigned char *buf, int len)
+{
+    if(len<12) return 0; /* Something screwed up */
+
+    if(memcmp(buf, "RIFF", 4))
+        return 0; /* Not wave */
+
+    /*flen = READ_U32_LE(buf+4);*/ /* We don't use this */
+
+    if(memcmp(buf+8, "WAVE",4))
+        return 0; /* RIFF, but not wave */
+
+    return 1;
+}
+
+int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *oldbuf, int buflen)
+{
+    unsigned char buf[40];
+    unsigned int len;
+    int samplesize;
+    int validbits;
+    wav_fmt format;
+    wavfile *wav;
+    int i;
+    (void)buflen;/*unused*/
+    (void)oldbuf;/*unused*/
+
+    /* Ok. At this point, we know we have a WAV file. Now we have to detect
+     * whether we support the subtype, and we have to find the actual data
+     * We don't (for the wav reader) need to use the buffer we used to id this
+     * as a wav file (oldbuf)
+     */
+
+    if(!find_wav_chunk(in, "fmt ", &len))
+        return 0; /* EOF */
+
+    if(len < 16)
+    {
+        fprintf(stderr, _("Warning: Unrecognised format chunk in WAV header\n"));
+        return 0; /* Weird format chunk */
+    }
+
+    /* A common error is to have a format chunk that is not 16, 18 or
+     * 40 bytes in size.  This is incorrect, but not fatal, so we only
+     * warn about it instead of refusing to work with the file.
+     * Please, if you have a program that's creating format chunks of
+     * sizes other than 16 or 18 bytes in size, report a bug to the
+     * author.
+     */
+    if(len!=16 && len!=18 && len!=40)
+        fprintf(stderr,
+                _("Warning: INVALID format chunk in wav header.\n"
+                " Trying to read anyway (may not work)...\n"));
+
+    if(len>40)len=40;
+
+    if(fread(buf,1,len,in) < len)
+    {
+        fprintf(stderr, _("Warning: Unexpected EOF reading WAV header\n"));
+        return 0;
+    }
+
+    format.format =      READ_U16_LE(buf);
+    format.channels =    READ_U16_LE(buf+2);
+    format.samplerate =  READ_U32_LE(buf+4);
+    format.bytespersec = READ_U32_LE(buf+8);
+    format.align =       READ_U16_LE(buf+12);
+    format.samplesize =  READ_U16_LE(buf+14);
+
+    if(format.format == -2) /* WAVE_FORMAT_EXTENSIBLE */
+    {
+      if(len<40)
+      {
+        fprintf(stderr, _("ERROR: Extended WAV format header invalid (too small)\n"));
+        return 0;
+      }
+
+      validbits = READ_U16_LE(buf+18);
+      if(validbits < 1 || validbits > format.samplesize)
+        validbits = format.samplesize;
+
+      format.mask = READ_U32_LE(buf+20);
+      /* warn the user if the format mask is not a supported/expected type */
+      switch(format.mask){
+      case 1539: /* 4.0 using side surround instead of back */
+        fprintf(stderr, _("WARNING: WAV file uses side surround instead of rear for quadraphonic;\n"
+                "remapping side speakers to rear in encoding.\n"));
+        break;
+      case 1551: /* 5.1 using side instead of rear */
+        fprintf(stderr, _("WARNING: WAV file uses side surround instead of rear for 5.1;\n"
+                "remapping side speakers to rear in encoding.\n"));
+        break;
+      case 319:  /* 6.1 using rear instead of side */
+        fprintf(stderr, _("WARNING: WAV file uses rear surround instead of side for 6.1;\n"
+                "remapping rear speakers to side in encoding.\n"));
+        break;
+      case 255:  /* 7.1 'Widescreen' */
+        fprintf(stderr, _("WARNING: WAV file is a 7.1 'Widescreen' channel mapping;\n"
+                "remapping speakers to Vorbis 7.1 format.\n"));
+        break;
+      case 0:    /* default/undeclared */
+      case 1:    /* mono */
+      case 3:    /* stereo */
+      case 51:   /* quad */
+      case 55:   /* 5.0 */
+      case 63:   /* 5.1 */
+      case 1807: /* 6.1 */
+      case 1599: /* 7.1 */
+        break;
+      default:
+        fprintf(stderr, _("WARNING: Unknown WAV surround channel mask: %d\n"
+                "Blindly mapping speakers using default SMPTE/ITU ordering.\n"),
+                format.mask);
+        break;
+      }
+      format.format = READ_U16_LE(buf+24);
+    }
+    else
+    {
+      validbits = format.samplesize;
+    }
+
+    if(!find_wav_chunk(in, "data", &len))
+        return 0; /* EOF */
+
+    if(format.format == 1)
+    {
+        samplesize = format.samplesize/8;
+        opt->read_samples = wav_read;
+    }
+    else if(format.format == 3)
+    {
+        validbits = 24;
+        samplesize = 4;
+        opt->read_samples = wav_ieee_read;
+    }
+    else
+    {
+        fprintf(stderr, _("ERROR: Unsupported WAV file type.\n"
+                "Must be standard PCM or type 3 floating point PCM.\n"));
+        return 0;
+    }
+
+    if(format.align != format.channels * samplesize) {
+        /* This is incorrect according to the spec. Warn loudly, then ignore
+         * this value.
+         */
+        fprintf(stderr, _("Warning: WAV 'block alignment' value is incorrect, "
+                    "ignoring.\n"
+                    "The software that created this file is incorrect.\n"));
+    }
+
+    if(format.samplesize == samplesize*8 &&
+            (format.samplesize == 24 || format.samplesize == 16 ||
+             format.samplesize == 8 ||
+         (format.samplesize == 32 && format.format == 3)))
+    {
+        /* OK, good - we have the one supported format,
+           now we want to find the size of the file */
+        opt->rate = format.samplerate;
+        opt->channels = format.channels;
+        opt->samplesize = validbits;
+
+        wav = malloc(sizeof(wavfile));
+        wav->f = in;
+        wav->samplesread = 0;
+        wav->bigendian = 0;
+        wav->unsigned8bit = format.samplesize == 8;
+        wav->channels = format.channels; /* This is in several places. The price
+                                            of trying to abstract stuff. */
+        wav->samplesize = format.samplesize;
+
+        if(len>(format.channels*samplesize*4U) && len<((1U<<31)-65536) && opt->ignorelength!=1) /*Length provided is plausible.*/
+        {
+            opt->total_samples_per_channel = len/(format.channels*samplesize);
+        }
+#ifdef WIN32
+        /*On Mingw/Win32 fseek() returns zero on pipes.*/
+        else if (opt->ignorelength==1 || ((GetFileType((HANDLE)_get_osfhandle(fileno(in)))&~FILE_TYPE_REMOTE)!=FILE_TYPE_DISK))
+#else
+        else if (opt->ignorelength==1)
+#endif
+        {
+           opt->total_samples_per_channel = 0;
+        }
+        else
+        {
+            opus_int64 pos;
+            pos = ftell(in);
+            if(fseek(in, 0, SEEK_END) == -1)
+            {
+                opt->total_samples_per_channel = 0; /* Give up */
+            }
+            else
+            {
+#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
+                opt->total_samples_per_channel = _ftelli64(in);
+#elif defined HAVE_FSEEKO
+                opt->total_samples_per_channel = ftello(in);
+#else
+                opt->total_samples_per_channel = ftell(in);
+#endif
+                if(opt->total_samples_per_channel>pos)
+                {
+                   opt->total_samples_per_channel = (opt->total_samples_per_channel-pos)/(format.channels*samplesize);
+                }
+                else
+                {
+                   opt->total_samples_per_channel=0;
+                }
+                fseek(in,pos, SEEK_SET);
+            }
+        }
+        wav->totalsamples = opt->total_samples_per_channel;
+
+        opt->readdata = (void *)wav;
+
+        wav->channel_permute = malloc(wav->channels * sizeof(int));
+        if (wav->channels <= 8)
+            /* Where we know the mappings, use them. */
+            memcpy(wav->channel_permute, wav_permute_matrix[wav->channels-1],
+                    sizeof(int) * wav->channels);
+        else
+            /* Use a default 1-1 mapping */
+            for (i=0; i < wav->channels; i++)
+                wav->channel_permute[i] = i;
+
+        return 1;
+    }
+    else
+    {
+        fprintf(stderr, _("ERROR: Unsupported WAV sample size.\n"
+                "Must be 8, 16, or 24 bit PCM or 32 bit floating point PCM.\n"));
+        return 0;
+    }
+}
+
+long wav_read(void *in, float *buffer, int samples)
+{
+    wavfile *f = (wavfile *)in;
+    int sampbyte = f->samplesize / 8;
+    signed char *buf = alloca(samples*sampbyte*f->channels);
+    long bytes_read = fread(buf, 1, samples*sampbyte*f->channels, f->f);
+    int i,j;
+    opus_int64 realsamples;
+    int *ch_permute = f->channel_permute;
+
+    if(f->totalsamples && f->samplesread +
+            bytes_read/(sampbyte*f->channels) > f->totalsamples) {
+        bytes_read = sampbyte*f->channels*(f->totalsamples - f->samplesread);
+    }
+
+    realsamples = bytes_read/(sampbyte*f->channels);
+    f->samplesread += realsamples;
+
+    if(f->samplesize==8)
+    {
+        if(f->unsigned8bit)
+        {
+            unsigned char *bufu = (unsigned char *)buf;
+            for(i = 0; i < realsamples; i++)
+            {
+                for(j=0; j < f->channels; j++)
+                {
+                    buffer[i*f->channels+j]=((int)(bufu[i*f->channels + ch_permute[j]])-128)/128.0f;
+                }
+            }
+        }
+        else
+        {
+            for(i = 0; i < realsamples; i++)
+            {
+                for(j=0; j < f->channels; j++)
+                {
+                    buffer[i*f->channels+j]=buf[i*f->channels + ch_permute[j]]/128.0f;
+                }
+            }
+        }
+    }
+    else if(f->samplesize==16)
+    {
+        if(!f->bigendian)
+        {
+            for(i = 0; i < realsamples; i++)
+            {
+                for(j=0; j < f->channels; j++)
+                {
+                    buffer[i*f->channels+j] = ((buf[i*2*f->channels + 2*ch_permute[j] + 1]<<8) |
+                                    (buf[i*2*f->channels + 2*ch_permute[j]] & 0xff))/32768.0f;
+                }
+            }
+        }
+        else
+        {
+            for(i = 0; i < realsamples; i++)
+            {
+                for(j=0; j < f->channels; j++)
+                {
+                    buffer[i*f->channels+j]=((buf[i*2*f->channels + 2*ch_permute[j]]<<8) |
+                                  (buf[i*2*f->channels + 2*ch_permute[j] + 1] & 0xff))/32768.0f;
+                }
+            }
+        }
+    }
+    else if(f->samplesize==24)
+    {
+        if(!f->bigendian) {
+            for(i = 0; i < realsamples; i++)
+            {
+                for(j=0; j < f->channels; j++)
+                {
+                    buffer[i*f->channels+j] = ((buf[i*3*f->channels + 3*ch_permute[j] + 2] << 16) |
+                      (((unsigned char *)buf)[i*3*f->channels + 3*ch_permute[j] + 1] << 8) |
+                      (((unsigned char *)buf)[i*3*f->channels + 3*ch_permute[j]] & 0xff))
+                        / 8388608.0f;
+
+                }
+            }
+        }
+        else {
+            fprintf(stderr, _("Big endian 24 bit PCM data is not currently "
+                              "supported, aborting.\n"));
+            return 0;
+        }
+    }
+    else {
+        fprintf(stderr, _("Internal error: attempt to read unsupported "
+                          "bitdepth %d\n"), f->samplesize);
+        return 0;
+    }
+
+    return realsamples;
+}
+
+long wav_ieee_read(void *in, float *buffer, int samples)
+{
+    wavfile *f = (wavfile *)in;
+    float *buf = alloca(samples*4*f->channels); /* de-interleave buffer */
+    long bytes_read = fread(buf,1,samples*4*f->channels, f->f);
+    int i,j;
+    opus_int64 realsamples;
+
+    if(f->totalsamples && f->samplesread +
+            bytes_read/(4*f->channels) > f->totalsamples)
+        bytes_read = 4*f->channels*(f->totalsamples - f->samplesread);
+
+    realsamples = bytes_read/(4*f->channels);
+    f->samplesread += realsamples;
+
+    for(i=0; i < realsamples; i++)
+        for(j=0; j < f->channels; j++)
+            buffer[i*f->channels+j] = buf[i*f->channels + f->channel_permute[j]];
+
+    return realsamples;
+}
+
+void wav_close(void *info)
+{
+    wavfile *f = (wavfile *)info;
+    free(f->channel_permute);
+
+    free(f);
+}
+
+int raw_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen)
+{
+    wav_fmt format; /* fake wave header ;) */
+    wavfile *wav = malloc(sizeof(wavfile));
+    int i;
+    (void)buf;/*unused*/
+    (void)buflen;/*unused*/
+
+    /* construct fake wav header ;) */
+    format.format =      2;
+    format.channels =    opt->channels;
+    format.samplerate =  opt->rate;
+    format.samplesize =  opt->samplesize;
+    format.bytespersec = opt->channels * opt->rate * opt->samplesize / 8;
+    format.align =       format.bytespersec;
+    wav->f =             in;
+    wav->samplesread =   0;
+    wav->bigendian =     opt->endianness;
+    wav->unsigned8bit =  format.samplesize == 8;
+    wav->channels =      format.channels;
+    wav->samplesize =    opt->samplesize;
+    wav->totalsamples =  0;
+    wav->channel_permute = malloc(wav->channels * sizeof(int));
+    for (i=0; i < wav->channels; i++)
+      wav->channel_permute[i] = i;
+
+    opt->read_samples = wav_read;
+    opt->readdata = (void *)wav;
+    opt->total_samples_per_channel = 0; /* raw mode, don't bother */
+    return 1;
+}
+
+typedef struct {
+    audio_read_func real_reader;
+    void *real_readdata;
+    int channels;
+    float scale_factor;
+} scaler;
+
+static long read_scaler(void *data, float *buffer, int samples) {
+    scaler *d = data;
+    long in_samples = d->real_reader(d->real_readdata, buffer, samples);
+    int i;
+
+    for(i=0; i < d->channels*in_samples; i++) {
+       buffer[i] *= d->scale_factor;
+    }
+
+    return in_samples;
+}
+
+void setup_scaler(oe_enc_opt *opt, float scale) {
+    scaler *d = calloc(1, sizeof(scaler));
+
+    d->real_reader = opt->read_samples;
+    d->real_readdata = opt->readdata;
+
+    opt->read_samples = read_scaler;
+    opt->readdata = d;
+    d->channels = opt->channels;
+    d->scale_factor = scale;
+}
+
+typedef struct {
+    audio_read_func real_reader;
+    void *real_readdata;
+    ogg_int64_t *original_samples;
+    int channels;
+    int lpc_ptr;
+    int *extra_samples;
+    float *lpc_out;
+} padder;
+
+/* Read audio data, appending padding to make up any gap
+ * between the available and requested number of samples
+ * with LPC-predicted data to minimize the pertubation of
+ * the valid data that falls in the same frame.
+ */
+static long read_padder(void *data, float *buffer, int samples) {
+    padder *d = data;
+    long in_samples = d->real_reader(d->real_readdata, buffer, samples);
+    int i, extra=0;
+    const int lpc_order=32;
+
+    if(d->original_samples)*d->original_samples+=in_samples;
+
+    if(in_samples<samples){
+      if(d->lpc_ptr<0){
+        d->lpc_out=calloc(d->channels * *d->extra_samples, sizeof(*d->lpc_out));
+        if(in_samples>lpc_order*2){
+          float *lpc=alloca(lpc_order*sizeof(*lpc));
+          for(i=0;i<d->channels;i++){
+            vorbis_lpc_from_data(buffer+i,lpc,in_samples,lpc_order,d->channels);
+            vorbis_lpc_predict(lpc,buffer+i+(in_samples-lpc_order)*d->channels,
+                               lpc_order,d->lpc_out+i,*d->extra_samples,d->channels);
+          }
+        }
+        d->lpc_ptr=0;
+      }
+      extra=samples-in_samples;
+      if(extra>*d->extra_samples)extra=*d->extra_samples;
+      *d->extra_samples-=extra;
+    }
+    memcpy(buffer+in_samples*d->channels,d->lpc_out+d->lpc_ptr*d->channels,extra*d->channels*sizeof(*buffer));
+    d->lpc_ptr+=extra;
+    return in_samples+extra;
+}
+
+void setup_padder(oe_enc_opt *opt,ogg_int64_t *original_samples) {
+    padder *d = calloc(1, sizeof(padder));
+
+    d->real_reader = opt->read_samples;
+    d->real_readdata = opt->readdata;
+
+    opt->read_samples = read_padder;
+    opt->readdata = d;
+    d->channels = opt->channels;
+    d->extra_samples = &opt->extraout;
+    d->original_samples=original_samples;
+    d->lpc_ptr = -1;
+    d->lpc_out = NULL;
+}
+
+void clear_padder(oe_enc_opt *opt) {
+    padder *d = opt->readdata;
+
+    opt->read_samples = d->real_reader;
+    opt->readdata = d->real_readdata;
+
+    if(d->lpc_out)free(d->lpc_out);
+    free(d);
+}
+
+typedef struct {
+    SpeexResamplerState *resampler;
+    audio_read_func real_reader;
+    void *real_readdata;
+    float *bufs;
+    int channels;
+    int bufpos;
+    int bufsize;
+    int done;
+} resampler;
+
+static long read_resampled(void *d, float *buffer, int samples)
+{
+    resampler *rs = d;
+    int out_samples=0;
+    float *pcmbuf;
+    int *inbuf;
+    pcmbuf=rs->bufs;
+    inbuf=&rs->bufpos;
+    while(out_samples<samples){
+      int i;
+      int reading, ret;
+      unsigned in_len, out_len;
+      out_len=samples-out_samples;
+      reading=rs->bufsize-*inbuf;
+      if(reading>1024)reading=1024;
+      ret=rs->real_reader(rs->real_readdata, pcmbuf+*inbuf*rs->channels, reading);
+      *inbuf+=ret;
+      in_len=*inbuf;
+      speex_resampler_process_interleaved_float(rs->resampler, pcmbuf, &in_len, buffer+out_samples*rs->channels, &out_len);
+      out_samples+=out_len;
+      if(ret==0&&in_len==0){
+        for(i=out_samples*rs->channels;i<samples*rs->channels;i++)buffer[i]=0;
+        return out_samples;
+      }
+      for(i=0;i<rs->channels*(*inbuf-(long int)in_len);i++)pcmbuf[i]=pcmbuf[i+rs->channels*in_len];
+      *inbuf-=in_len;
+    }
+    return out_samples;
+}
+
+int setup_resample(oe_enc_opt *opt, int complexity, long outfreq) {
+    resampler *rs = calloc(1, sizeof(resampler));
+    int err;
+
+    rs->bufsize = 5760*2; /* Have at least two output frames worth, just in case of ugly ratios */
+    rs->bufpos = 0;
+
+    rs->real_reader = opt->read_samples;
+    rs->real_readdata = opt->readdata;
+    rs->channels = opt->channels;
+    rs->done = 0;
+    rs->resampler = speex_resampler_init(rs->channels, opt->rate, outfreq, complexity, &err);
+    if(err!=0)fprintf(stderr, _("resampler error: %s\n"), speex_resampler_strerror(err));
+
+    opt->skip+=speex_resampler_get_output_latency(rs->resampler);
+
+    rs->bufs = malloc(sizeof(float) * rs->bufsize * opt->channels);
+
+    opt->read_samples = read_resampled;
+    opt->readdata = rs;
+    if(opt->total_samples_per_channel)
+        opt->total_samples_per_channel = (int)((float)opt->total_samples_per_channel *
+            ((float)outfreq/(float)opt->rate));
+    opt->rate = outfreq;
+
+    return 0;
+}
+
+void clear_resample(oe_enc_opt *opt) {
+    resampler *rs = opt->readdata;
+
+    opt->read_samples = rs->real_reader;
+    opt->readdata = rs->real_readdata;
+    speex_resampler_destroy(rs->resampler);
+
+    free(rs->bufs);
+
+    free(rs);
+}
+
+typedef struct {
+    audio_read_func real_reader;
+    void *real_readdata;
+    float *bufs;
+    float *matrix;
+    int in_channels;
+    int out_channels;
+} downmix;
+
+static long read_downmix(void *data, float *buffer, int samples)
+{
+    downmix *d = data;
+    long in_samples = d->real_reader(d->real_readdata, d->bufs, samples);
+    int i,j,k,in_ch,out_ch;
+
+    in_ch=d->in_channels;
+    out_ch=d->out_channels;
+
+    for(i=0;i<in_samples;i++){
+      for(j=0;j<out_ch;j++){
+        float *samp;
+        samp=&buffer[i*out_ch+j];
+        *samp=0;
+        for(k=0;k<in_ch;k++){
+          *samp+=d->bufs[i*in_ch+k]*d->matrix[in_ch*j+k];
+        }
+      }
+    }
+    return in_samples;
+}
+
+int setup_downmix(oe_enc_opt *opt, int out_channels) {
+    static const float stupid_matrix[7][8][2]={
+      /*2*/  {{1,0},{0,1}},
+      /*3*/  {{1,0},{0.7071f,0.7071f},{0,1}},
+      /*4*/  {{1,0},{0,1},{0.866f,0.5f},{0.5f,0.866f}},
+      /*5*/  {{1,0},{0.7071f,0.7071f},{0,1},{0.866f,0.5f},{0.5f,0.866f}},
+      /*6*/  {{1,0},{0.7071f,0.7071f},{0,1},{0.866f,0.5f},{0.5f,0.866f},{0.7071f,0.7071f}},
+      /*7*/  {{1,0},{0.7071f,0.7071f},{0,1},{0.866f,0.5f},{0.5f,0.866f},{0.6123f,0.6123f},{0.7071f,0.7071f}},
+      /*8*/  {{1,0},{0.7071f,0.7071f},{0,1},{0.866f,0.5f},{0.5f,0.866f},{0.866f,0.5f},{0.5f,0.866f},{0.7071f,0.7071f}},
+    };
+    float sum;
+    downmix *d;
+    int i,j;
+
+    if(opt->channels<=out_channels || out_channels>2 || opt->channels<=0 || out_channels<=0) {
+        fprintf(stderr, _("Downmix must actually downmix and only knows mono/stereo out.\n"));
+        return 0;
+    }
+
+    if(out_channels==2 && opt->channels>8) {
+        fprintf(stderr, _("Downmix only knows how to mix >8ch to mono.\n"));
+        return 0;
+    }
+
+    d = calloc(1, sizeof(downmix));
+    d->bufs = malloc(sizeof(float)*opt->channels*4096);
+    d->matrix = malloc(sizeof(float)*opt->channels*out_channels);
+    d->real_reader = opt->read_samples;
+    d->real_readdata = opt->readdata;
+    d->in_channels=opt->channels;
+    d->out_channels=out_channels;
+
+    if(out_channels==1&&d->in_channels>8){
+      for(i=0;i<d->in_channels;i++)d->matrix[i]=1.0f/d->in_channels;
+    }else if(out_channels==2){
+      for(j=0;j<d->out_channels;j++)
+        for(i=0;i<d->in_channels;i++)d->matrix[d->in_channels*j+i]=
+          stupid_matrix[opt->channels-2][i][j];
+    }else{
+      for(i=0;i<d->in_channels;i++)d->matrix[i]=
+        (stupid_matrix[opt->channels-2][i][0])+
+        (stupid_matrix[opt->channels-2][i][1]);
+    }
+    sum=0;
+    for(i=0;i<d->in_channels*d->out_channels;i++)sum+=d->matrix[i];
+    sum=(float)out_channels/sum;
+    for(i=0;i<d->in_channels*d->out_channels;i++)d->matrix[i]*=sum;
+    opt->read_samples = read_downmix;
+    opt->readdata = d;
+
+    opt->channels = out_channels;
+    return out_channels;
+}
+
+void clear_downmix(oe_enc_opt *opt) {
+    downmix *d = opt->readdata;
+
+    opt->read_samples = d->real_reader;
+    opt->readdata = d->real_readdata;
+    opt->channels = d->in_channels; /* other things in cleanup rely on this */
+
+    free(d->bufs);
+    free(d->matrix);
+    free(d);
+}
diff --git a/src/cpusupport.h b/src/cpusupport.h
new file mode 100644
index 0000000..d68cf0c
--- /dev/null
+++ b/src/cpusupport.h
@@ -0,0 +1,67 @@
+/**
+   @file cpusupport.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 OPUSTOOLS_CPUSUPPORT_H
+# define OPUSTOOLS_CPUSUPPORT_H
+
+/* We want to warn if we're built with SSE support, but running
+   on a host without those instructions. Therefore we disable
+   the query both if the compiler isn't supporting SSE, and on
+   targets which are guaranteed to have SSE. */
+# if !defined(__SSE__) || defined(_M_X64) || defined(__amd64__)
+#  define query_cpu_support() 0
+# else
+
+#if defined WIN32 || defined _WIN32
+#include <intrin.h>
+static inline int query_cpu_support(void)
+{
+   int buffer[4];
+   __cpuid(buffer, 1);
+   return ((buffer[3] & (1<<25)) == 0) /*SSE*/
+#  ifdef __SSE2__
+        + ((buffer[3] & (1<<26)) == 0) /*SSE2*/
+#  endif
+       ;
+}
+#else
+#include <cpuid.h>
+static inline int query_cpu_support(void)
+{
+   unsigned int eax, ebx, ecx, edx=0;
+   __get_cpuid(1, &eax, &ebx, &ecx, &edx);
+   return ((edx & 1<<25) == 0) /*SSE*/
+#ifdef __SSE2__
+        + ((edx & 1<<26) == 0) /*SSE2*/
+#endif
+       ;
+}
+#endif
+
+# endif
+#endif
diff --git a/src/diag_range.c b/src/diag_range.c
new file mode 100644
index 0000000..2f6fce4
--- /dev/null
+++ b/src/diag_range.c
@@ -0,0 +1,245 @@
+/* Copyright (C)2012 Xiph.Org Foundation
+   Copyright (C)2012 Gregory Maxwell
+   Copyright (C)2012 Jean-Marc Valin
+   File: diag_range.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
+
+#ifdef _WIN32
+#define I64FORMAT "I64d"
+#define I64uFORMAT "I64u"
+#else
+#define I64FORMAT "lld"
+#define I64uFORMAT "llu"
+#endif
+
+#include <stdio.h>
+#include <opus.h>
+#include "diag_range.h"
+
+/*This is some non-exported code copied wholesale from libopus.
+ *Normal programs shouldn't need these functions, but we use them here
+ *to parse deep inside multichannel packets in order to get diagnostic
+ *data for save-range. If you're thinking about copying it and you aren't
+ *making an opus stream diagnostic tool, you're probably doing something
+ *wrong.*/
+static int parse_size(const unsigned char *data, opus_int32 len, short *size)
+{
+   if (len<1)
+   {
+      *size = -1;
+      return -1;
+   } else if (data[0]<252)
+   {
+      *size = data[0];
+      return 1;
+   } else if (len<2)
+   {
+      *size = -1;
+      return -1;
+   } else {
+      *size = 4*data[1] + data[0];
+      return 2;
+   }
+}
+
+static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
+      int self_delimited, unsigned char *out_toc,
+      const unsigned char *frames[48], short size[48], int *payload_offset)
+{
+   int i, bytes;
+   int count;
+   int cbr;
+   unsigned char ch, toc;
+   int framesize;
+   int last_size;
+   const unsigned char *data0 = data;
+
+   if (size==NULL)
+      return OPUS_BAD_ARG;
+
+   framesize = opus_packet_get_samples_per_frame(data, 48000);
+
+   cbr = 0;
+   toc = *data++;
+   len--;
+   last_size = len;
+   switch (toc&0x3)
+   {
+   /* One frame */
+   case 0:
+      count=1;
+      break;
+   /* Two CBR frames */
+   case 1:
+      count=2;
+      cbr = 1;
+      if (!self_delimited)
+      {
+         if (len&0x1)
+            return OPUS_INVALID_PACKET;
+         size[0] = last_size = len/2;
+      }
+      break;
+   /* Two VBR frames */
+   case 2:
+      count = 2;
+      bytes = parse_size(data, len, size);
+      len -= bytes;
+      if (size[0]<0 || size[0] > len)
+         return OPUS_INVALID_PACKET;
+      data += bytes;
+      last_size = len-size[0];
+      break;
+   /* Multiple CBR/VBR frames (from 0 to 120 ms) */
+   case 3:
+      if (len<1)
+         return OPUS_INVALID_PACKET;
+      /* Number of frames encoded in bits 0 to 5 */
+      ch = *data++;
+      count = ch&0x3F;
+      if (count <= 0 || framesize*count > 5760)
+         return OPUS_INVALID_PACKET;
+      len--;
+      /* Padding flag is bit 6 */
+      if (ch&0x40)
+      {
+         int padding=0;
+         int p;
+         do {
+            if (len<=0)
+               return OPUS_INVALID_PACKET;
+            p = *data++;
+            len--;
+            padding += p==255 ? 254: p;
+         } while (p==255);
+         len -= padding;
+      }
+      if (len<0)
+         return OPUS_INVALID_PACKET;
+      /* VBR flag is bit 7 */
+      cbr = !(ch&0x80);
+      if (!cbr)
+      {
+         /* VBR case */
+         last_size = len;
+         for (i=0;i<count-1;i++)
+         {
+            bytes = parse_size(data, len, size+i);
+            len -= bytes;
+            if (size[i]<0 || size[i] > len)
+               return OPUS_INVALID_PACKET;
+            data += bytes;
+            last_size -= bytes+size[i];
+         }
+         if (last_size<0)
+            return OPUS_INVALID_PACKET;
+      } else if (!self_delimited)
+      {
+         /* CBR case */
+         last_size = len/count;
+         if (last_size*count!=len)
+            return OPUS_INVALID_PACKET;
+         for (i=0;i<count-1;i++)
+            size[i] = last_size;
+      }
+      break;
+   }
+   /* Self-delimited framing has an extra size for the last frame. */
+   if (self_delimited)
+   {
+      bytes = parse_size(data, len, size+count-1);
+      len -= bytes;
+      if (size[count-1]<0 || size[count-1] > len)
+         return OPUS_INVALID_PACKET;
+      data += bytes;
+      /* For CBR packets, apply the size to all the frames. */
+      if (cbr)
+      {
+         if (size[count-1]*count > len)
+            return OPUS_INVALID_PACKET;
+         for (i=0;i<count-1;i++)
+            size[i] = size[count-1];
+      } else if(size[count-1] > last_size)
+         return OPUS_INVALID_PACKET;
+   } else
+   {
+      /* Because it's not encoded explicitly, it's possible the size of the
+         last packet (or all the packets, for the CBR case) is larger than
+         1275. Reject them here.*/
+      if (last_size > 1275)
+         return OPUS_INVALID_PACKET;
+      size[count-1] = last_size;
+   }
+
+   if (frames)
+   {
+      for (i=0;i<count;i++)
+      {
+         frames[i] = data;
+         data += size[i];
+      }
+   }
+
+   if (out_toc)
+      *out_toc = toc;
+
+   if (payload_offset)
+      *payload_offset = data-data0;
+
+   return count;
+}
+
+void save_range(FILE *frange, int frame_size, unsigned char *packet, int nbBytes, opus_uint32 *rngs, int nb_streams){
+  int i, parsed_size;
+  const unsigned char *subpkt;
+  static const char *bw_strings[5]={"NB","MB","WB","SWB","FB"};
+  static const char *mode_strings[3]={"LP","HYB","MDCT"};
+  fprintf(frange,"%d, %d, ",frame_size,nbBytes);
+  subpkt=packet;
+  parsed_size=nbBytes;
+  for(i=0;i<nb_streams;i++){
+    int j,payload_offset,nf;
+    const unsigned char *frames[48];
+    unsigned char toc;
+    short size[48];
+    payload_offset=0;
+    nf=opus_packet_parse_impl(subpkt,parsed_size,i+1!=nb_streams,
+      &toc,frames,size,&payload_offset);
+    fprintf(frange,"[[%d",(int)(frames[0]-subpkt));
+    for(j=0;j<nf;j++)fprintf(frange,", %d",size[j]);
+    fprintf(frange,"], %s, %s, %c, %d",
+       mode_strings[((((subpkt[0]>>3)+48)&92)+4)>>5],
+       bw_strings[opus_packet_get_bandwidth(subpkt)-OPUS_BANDWIDTH_NARROWBAND],
+       subpkt[0]&4?'S':'M',opus_packet_get_samples_per_frame(subpkt,48000));
+    fprintf(frange,", %" I64uFORMAT "]%s",(unsigned long long)rngs[i],i+1==nb_streams?"\n":", ");
+    parsed_size-=payload_offset;
+    subpkt+=payload_offset;
+  }
+}
diff --git a/src/diag_range.h b/src/diag_range.h
new file mode 100644
index 0000000..aa0006c
--- /dev/null
+++ b/src/diag_range.h
@@ -0,0 +1,28 @@
+/* Copyright (C)2012 Xiph.Org Foundation
+   File: diag_range.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.
+*/
+
+void save_range(FILE *frange, int frame_size, unsigned char *packet, int nbBytes, opus_uint32 *rngs, int nb_streams);
diff --git a/src/flac.c b/src/flac.c
new file mode 100644
index 0000000..4a775b8
--- /dev/null
+++ b/src/flac.c
@@ -0,0 +1,435 @@
+/*Copyright 2012-2013, Xiph.Org Foundation and contributors.
+
+  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.*/
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <locale.h>
+#include "flac.h"
+#include "opus_header.h"
+#include "picture.h"
+
+#if defined(HAVE_LIBFLAC)
+
+/*Callback to read more data for the FLAC decoder.*/
+static FLAC__StreamDecoderReadStatus read_callback(
+   const FLAC__StreamDecoder *decoder,FLAC__byte buffer[],size_t *bytes,
+   void *client_data){
+  flacfile *flac;
+  (void)decoder;
+  flac=(flacfile *)client_data;
+  if(*bytes>0){
+    int bufpos;
+    int buflen;
+    bufpos=flac->bufpos;
+    buflen=flac->buflen;
+    if(bufpos<buflen){
+      size_t bytes_to_copy;
+      /*If we haven't consumed all the data we used for file ID yet, consume
+        some more.*/
+      bytes_to_copy=buflen-bufpos;
+      bytes_to_copy=*bytes<bytes_to_copy?*bytes:bytes_to_copy;
+      memcpy(buffer,flac->oldbuf,bytes_to_copy);
+      flac->bufpos+=bytes_to_copy;
+      *bytes=bytes_to_copy;
+    }else{
+      /*Otherwise just read from the file.*/
+      *bytes=fread(buffer,sizeof(*buffer),*bytes,flac->f);
+    }
+    /*This pretty much comes from the FLAC documentation, except that we only
+      check ferror() if we didn't read any bytes at all.*/
+    return *bytes==0?ferror(flac->f)?
+       FLAC__STREAM_DECODER_READ_STATUS_ABORT:
+       FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM:
+       FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+  }
+  return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+}
+
+/*Callback to test the stream for EOF.*/
+static FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder,
+   void *client_data){
+  flacfile *flac;
+  (void)decoder;
+  flac=(flacfile *)client_data;
+  return feof(flac->f)?true:false;
+}
+
+/*Callback to process a metadata packet.*/
+static void metadata_callback(const FLAC__StreamDecoder *decoder,
+   const FLAC__StreamMetadata *metadata,void *client_data){
+  flacfile *flac;
+  oe_enc_opt *inopt;
+  (void)decoder;
+  flac=(flacfile *)client_data;
+  inopt=flac->inopt;
+  switch(metadata->type){
+    case FLAC__METADATA_TYPE_STREAMINFO:
+      flac->max_blocksize=metadata->data.stream_info.max_blocksize;
+      inopt->rate=metadata->data.stream_info.sample_rate;
+      inopt->channels=flac->channels=metadata->data.stream_info.channels;
+      inopt->samplesize=metadata->data.stream_info.bits_per_sample;
+      inopt->total_samples_per_channel=
+         metadata->data.stream_info.total_samples;
+      flac->block_buf=malloc(
+         flac->max_blocksize*flac->channels*sizeof(*flac->block_buf));
+      flac->block_buf_pos=0;
+      flac->block_buf_len=0;
+      break;
+    case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+      {
+        FLAC__StreamMetadata_VorbisComment_Entry *comments;
+        FLAC__uint32 num_comments;
+        FLAC__uint32 i;
+        double reference_loudness;
+        double album_gain;
+        double track_gain;
+        double gain;
+        int saw_album_gain;
+        int saw_track_gain;
+        char *saved_locale;
+        if(!inopt->copy_comments)break;
+        num_comments=metadata->data.vorbis_comment.num_comments;
+        comments=metadata->data.vorbis_comment.comments;
+        saw_album_gain=saw_track_gain=0;
+        album_gain=track_gain=0;
+        /*The default reference loudness for ReplayGain is 89.0 dB*/
+        reference_loudness=89;
+        /*The code below uses strtod for the gain tags, so make sure the locale is C*/
+        saved_locale=setlocale(LC_NUMERIC,"C");
+        for(i=0;i<num_comments;i++){
+          char *entry;
+          char *end;
+          entry=(char *)comments[i].entry;
+          /*Check for ReplayGain tags.
+            Parse the ones we have R128 equivalents for, and skip the others.*/
+          if(oi_strncasecmp(entry,"REPLAYGAIN_REFERENCE_LOUDNESS=",30)==0){
+            gain=strtod(entry+30,&end);
+            if(end<=entry+30){
+              fprintf(stderr,_("WARNING: Invalid ReplayGain tag: %s\n"),entry);
+            }
+            else reference_loudness=gain;
+            continue;
+          }
+          if(oi_strncasecmp(entry,"REPLAYGAIN_ALBUM_GAIN=",22)==0){
+            gain=strtod(entry+22,&end);
+            if(end<=entry+22){
+              fprintf(stderr,_("WARNING: Invalid ReplayGain tag: %s\n"),entry);
+            }
+            else{
+              album_gain=gain;
+              saw_album_gain=1;
+            }
+            continue;
+          }
+          if(oi_strncasecmp(entry,"REPLAYGAIN_TRACK_GAIN=",22)==0){
+            gain=strtod(entry+22,&end);
+            if(end<entry+22){
+              fprintf(stderr,_("WARNING: Invalid ReplayGain tag: %s\n"),entry);
+            }
+            else{
+              track_gain=gain;
+              saw_track_gain=1;
+            }
+            continue;
+          }
+          if(oi_strncasecmp(entry,"REPLAYGAIN_ALBUM_PEAK=",22)==0
+             ||oi_strncasecmp(entry,"REPLAYGAIN_TRACK_PEAK=",22)==0){
+            continue;
+          }
+          if(!strchr(entry,'=')){
+            fprintf(stderr,_("WARNING: Invalid comment: %s\n"),entry);
+            fprintf(stderr,
+               _("Discarding comment not in the form name=value\n"));
+            continue;
+          }
+          comment_add(&inopt->comments,&inopt->comments_length,NULL,entry);
+        }
+        setlocale(LC_NUMERIC,saved_locale);
+        /*Set the header gain to the album gain after converting to the R128
+          reference level.*/
+        if(saw_album_gain){
+          gain=256*(album_gain+(84-reference_loudness))+0.5;
+          inopt->gain=gain<-32768?-32768:gain<32767?(int)floor(gain):32767;
+        }
+        /*If there was a track gain, then add an equivalent R128 tag for that.*/
+        if(saw_track_gain){
+          char track_gain_buf[7];
+          int track_gain_val;
+          gain=256*(track_gain-album_gain)+0.5;
+          track_gain_val=gain<-32768?-32768:gain<32767?(int)floor(gain):32767;
+          sprintf(track_gain_buf,"%i",track_gain_val);
+          comment_add(&inopt->comments,&inopt->comments_length,
+             "R128_TRACK_GAIN",track_gain_buf);
+        }
+      }
+      break;
+    case FLAC__METADATA_TYPE_PICTURE:
+      {
+        char  *buf;
+        char  *b64;
+        size_t mime_type_length;
+        size_t description_length;
+        size_t buf_sz;
+        size_t b64_sz;
+        size_t offs;
+        if(!inopt->copy_pictures)break;
+        mime_type_length=strlen(metadata->data.picture.mime_type);
+        description_length=strlen((char *)metadata->data.picture.description);
+        buf_sz=32+mime_type_length+description_length
+         +metadata->data.picture.data_length;
+        buf=(char *)malloc(buf_sz);
+        offs=0;
+        WRITE_U32_BE(buf+offs,metadata->data.picture.type);
+        offs+=4;
+        WRITE_U32_BE(buf+offs,(FLAC__uint32)mime_type_length);
+        offs+=4;
+        memcpy(buf+offs,metadata->data.picture.mime_type,mime_type_length);
+        offs+=mime_type_length;
+        WRITE_U32_BE(buf+offs,(FLAC__uint32)description_length);
+        offs+=4;
+        memcpy(buf+offs,metadata->data.picture.description,description_length);
+        offs+=description_length;
+        WRITE_U32_BE(buf+offs,metadata->data.picture.width);
+        offs+=4;
+        WRITE_U32_BE(buf+offs,metadata->data.picture.height);
+        offs+=4;
+        WRITE_U32_BE(buf+offs,metadata->data.picture.depth);
+        offs+=4;
+        WRITE_U32_BE(buf+offs,metadata->data.picture.colors);
+        offs+=4;
+        WRITE_U32_BE(buf+offs,metadata->data.picture.data_length);
+        offs+=4;
+        memcpy(buf+offs,metadata->data.picture.data,
+           metadata->data.picture.data_length);
+        b64_sz=BASE64_LENGTH(buf_sz)+1;
+        b64=(char *)malloc(b64_sz);
+        base64_encode(b64,buf,buf_sz);
+        free(buf);
+        comment_add(&inopt->comments,&inopt->comments_length,
+           "METADATA_BLOCK_PICTURE",b64);
+        free(b64);
+      }
+      break;
+    default:
+      break;
+  }
+}
+
+/*Callback to process an audio frame.*/
+static FLAC__StreamDecoderWriteStatus write_callback(
+   const FLAC__StreamDecoder *decoder,const FLAC__Frame *frame,
+   const FLAC__int32 *const buffer[],void *client_data){
+  flacfile *flac;
+  int channels;
+  opus_int32 blocksize;
+  int bits_per_sample;
+  float scale;
+  const int *channel_permute;
+  float *block_buf;
+  int ci;
+  opus_int32 si;
+  (void)decoder;
+  flac=(flacfile *)client_data;
+  /*We do not allow the number of channels to change.*/
+  channels=frame->header.channels;
+  if(channels!=flac->channels){
+    return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+  }
+  /*We do not allow block sizes larger than the declared maximum.*/
+  blocksize=frame->header.blocksize;
+  if(blocksize>flac->max_blocksize){
+    return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+  }
+  /*We do allow the bits per sample to change, though this will confound Opus's
+    silence detection.*/
+  bits_per_sample=frame->header.bits_per_sample;
+  speex_assert(bits_per_sample>0&&bits_per_sample<=32);
+  scale=(0x80000000U>>(bits_per_sample-1))*(1.0F/0x80000000U);
+  channel_permute=flac->channel_permute;
+  block_buf=flac->block_buf;
+  for(ci=0;ci<channels;ci++){
+    const FLAC__int32 *channel_buf;
+    channel_buf=buffer[channel_permute[ci]];
+    for(si=0;si<blocksize;si++){
+      /*There's a loss of precision here for 32-bit samples, but libFLAC
+        doesn't currently support more than 24.*/
+      block_buf[si*channels+ci]=scale*(float)channel_buf[si];
+    }
+  }
+  flac->block_buf_pos=0;
+  flac->block_buf_len=blocksize;
+  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+/*Dummy error callback (required by libFLAC).*/
+static void error_callback(const FLAC__StreamDecoder *decoder,
+   FLAC__StreamDecoderErrorStatus status,void *client_data){
+  (void)decoder;
+  (void)status;
+  (void)client_data;
+}
+
+int flac_id(unsigned char *buf,int len){
+  /*Something screwed up.*/
+  if(len<4)return 0;
+  /*Not FLAC.*/
+  if(memcmp(buf,"fLaC",4))return 0;
+  /*Looks like FLAC.*/
+  return 1;
+}
+
+int oggflac_id(unsigned char *buf,int len){
+  /*Something screwed up.*/
+  if(len<33)return 0;
+  /*Not Ogg.*/
+  if(memcmp(buf,"OggS",4))return 0;
+  /*Not FLAC.*/
+  if(memcmp(buf+28,"\177FLAC",5))return 0;
+  /*Looks like OggFLAC.*/
+  return 1;
+}
+
+/*Read more data for the encoder.*/
+static long flac_read(void *client_data,float *buffer,int samples){
+  flacfile *flac;
+  int channels;
+  float *block_buf;
+  long ret;
+  flac=(flacfile *)client_data;
+  channels=flac->channels;
+  block_buf=flac->block_buf;
+  ret=0;
+  /*Keep reading until we get all the samples or hit an error/EOF.
+    Short reads are not allowed.*/
+  while(samples>0){
+    opus_int32 block_buf_pos;
+    opus_int32 block_buf_len;
+    size_t samples_to_copy;
+    block_buf_pos=flac->block_buf_pos;
+    block_buf_len=flac->block_buf_len;
+    if(block_buf_pos>=block_buf_len){
+      /*Read the next frame from the stream.*/
+      if(!FLAC__stream_decoder_process_single(flac->decoder))return ret;
+      block_buf_pos=flac->block_buf_pos;
+      block_buf_len=flac->block_buf_len;
+      /*If we didn't get another block, we hit EOF.
+        FLAC__stream_decoder_process_single still returns successfully in this
+        case.*/
+      if(block_buf_pos>=block_buf_len)return ret;
+    }
+    block_buf_len-=block_buf_pos;
+    samples_to_copy=samples<block_buf_len?samples:block_buf_len;
+    memcpy(buffer,block_buf+block_buf_pos*channels,
+       samples_to_copy*channels*sizeof(*buffer));
+    flac->block_buf_pos+=samples_to_copy;
+    ret+=samples_to_copy;
+    buffer+=samples_to_copy*channels;
+    samples-=samples_to_copy;
+  }
+  return ret;
+}
+
+int flac_open(FILE *in,oe_enc_opt *opt,unsigned char *oldbuf,int buflen){
+  flacfile *flac;
+  /*Ok. At this point, we know we have a FLAC or an OggFLAC file.
+    Set up the FLAC decoder.*/
+  flac=malloc(sizeof(*flac));
+  flac->decoder=FLAC__stream_decoder_new();
+  FLAC__stream_decoder_set_md5_checking(flac->decoder,false);
+  /*We get STREAMINFO packets by default, but not VORBIS_COMMENT or PICTURE.*/
+  FLAC__stream_decoder_set_metadata_respond(flac->decoder,
+     FLAC__METADATA_TYPE_VORBIS_COMMENT);
+  FLAC__stream_decoder_set_metadata_respond(flac->decoder,
+     FLAC__METADATA_TYPE_PICTURE);
+  flac->inopt=opt;
+  flac->f=in;
+  flac->oldbuf=malloc(buflen*sizeof(*flac->oldbuf));
+  memcpy(flac->oldbuf,oldbuf,buflen*sizeof(*flac->oldbuf));
+  flac->bufpos=0;
+  flac->buflen=buflen;
+  flac->block_buf=NULL;
+  if((*(flac_id(oldbuf,buflen)?
+     FLAC__stream_decoder_init_stream:FLAC__stream_decoder_init_ogg_stream))(
+        flac->decoder,read_callback,NULL,NULL,NULL,eof_callback,
+        write_callback,metadata_callback,error_callback,flac)==
+     FLAC__STREAM_DECODER_INIT_STATUS_OK){
+    /*Decode until we get the file length, sample rate, the number of channels,
+      and the Vorbis comments (if any).*/
+    if(FLAC__stream_decoder_process_until_end_of_metadata(flac->decoder)){
+      opt->read_samples=flac_read;
+      opt->readdata=flac;
+      /*FLAC supports 1 to 8 channels only.*/
+      speex_assert(flac->channels>0&&flac->channels<=8);
+      /*It uses the same channel mappings as WAV.*/
+      flac->channel_permute=wav_permute_matrix[flac->channels-1];
+      return 1;
+    }
+  }
+  flac_close(flac);
+  fprintf(stderr,_("ERROR: Could not open FLAC stream.\n"));
+  return 0;
+}
+
+void flac_close(void *client_data){
+  flacfile *flac;
+  flac=(flacfile *)client_data;
+  free(flac->block_buf);
+  free(flac->oldbuf);
+  FLAC__stream_decoder_delete(flac->decoder);
+  free(flac);
+}
+
+#else
+
+/*FLAC support is disabled.*/
+
+int flac_id(unsigned char *buf,int len){
+  (void)buf;
+  (void)len;
+  return 0;
+}
+
+int oggflac_id(unsigned char *buf,int len){
+  (void)buf;
+  (void)len;
+  return 0;
+}
+
+int flac_open(FILE *in,oe_enc_opt *opt,unsigned char *oldbuf,int buflen){
+  (void)in;
+  (void)opt;
+  (void)oldbuf;
+  (void)buflen;
+  return 0;
+}
+
+void flac_close(void *client_data){
+  (void)client_data;
+}
+
+#endif
diff --git a/src/flac.h b/src/flac.h
new file mode 100644
index 0000000..ff9ca73
--- /dev/null
+++ b/src/flac.h
@@ -0,0 +1,58 @@
+/*Copyright 2012-2013, Xiph.Org Foundation and contributors.
+
+  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 __FLAC_H
+# define __FLAC_H
+# include <stdio.h>
+# include "os_support.h"
+# include "opusenc.h"
+# if defined(HAVE_LIBFLAC)
+#  include <FLAC/stream_decoder.h>
+#  include <FLAC/metadata.h>
+
+typedef struct flacfile flacfile;
+
+struct flacfile{
+  FLAC__StreamDecoder *decoder;
+  oe_enc_opt *inopt;
+  short channels;
+  FILE *f;
+  const int *channel_permute;
+  unsigned char *oldbuf;
+  int bufpos;
+  int buflen;
+  float *block_buf;
+  opus_int32 block_buf_pos;
+  opus_int32 block_buf_len;
+  opus_int32 max_blocksize;
+};
+
+# endif
+
+int flac_id(unsigned char *buf,int len);
+int oggflac_id(unsigned char *buf,int len);
+int flac_open(FILE *in,oe_enc_opt *opt,unsigned char *oldbuf,int buflen);
+void flac_close(void *client_data);
+
+#endif
diff --git a/src/info_opus.c b/src/info_opus.c
new file mode 100644
index 0000000..7b29d43
--- /dev/null
+++ b/src/info_opus.c
@@ -0,0 +1,320 @@
+/* Copyright (C)2012 Gregory Maxwell
+   File: info_opus.c
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <ogg/ogg.h>
+
+#ifndef OPUSTOOLS
+# include "ogginfo2.h"
+#else
+# include "opusinfo.h"
+#endif
+#include "opus_header.h"
+#include "info_opus.h"
+
+/* From libopus, src/opus_decode.c */
+static int packet_get_samples_per_frame(const unsigned char *data, ogg_int32_t Fs)
+{
+   int audiosize;
+   if (data[0]&0x80)
+   {
+      audiosize = ((data[0]>>3)&0x3);
+      audiosize = (Fs<<audiosize)/400;
+   } else if ((data[0]&0x60) == 0x60)
+   {
+      audiosize = (data[0]&0x08) ? Fs/50 : Fs/100;
+   } else {
+      audiosize = ((data[0]>>3)&0x3);
+      if (audiosize == 3)
+         audiosize = Fs*60/1000;
+      else
+         audiosize = (Fs<<audiosize)/100;
+   }
+   return audiosize;
+}
+
+/* From libopus, src/opus_decode.c */
+static int packet_get_nb_frames(const unsigned char packet[], ogg_int32_t len)
+{
+   int count;
+   if (len<1)
+      return -1;
+   count = packet[0]&0x3;
+   if (count==0)
+      return 1;
+   else if (count!=3)
+      return 2;
+   else if (len<2)
+      return -4;
+   else
+      return packet[1]&0x3F;
+}
+
+#define readle32(buf, base) (((buf[base+3]<<24)&0xff000000)| \
+                             ((buf[base+2]<<16)&0xff0000)| \
+                             ((buf[base+1]<<8)&0xff00)| \
+                              (buf[base]&0xff))
+
+void info_opus_process(stream_processor *stream, ogg_page *page )
+{
+    ogg_packet packet;
+    ogg_int64_t page_samples=0;
+    misc_opus_info *inf = stream->data;
+    int header=0, packets=0;
+    int res;
+
+    ogg_stream_pagein(&stream->os, page);
+    if(inf->doneheaders < 2)
+        header = 1;
+    inf->last_eos = ogg_page_eos(page);
+
+    while(1) {
+        ogg_int32_t spp;
+        res = ogg_stream_packetout(&stream->os, &packet);
+        if(res < 0) {
+           oi_warn(_("WARNING: discontinuity in stream (%d)\n"), stream->num);
+           continue;
+        }
+        else if (res == 0)
+            break;
+
+        packets++;
+        if(inf->doneheaders < 2) {
+            if(inf->doneheaders==0 && opus_header_parse(packet.packet,packet.bytes,&inf->oh)!=1) {
+                oi_warn(_("WARNING: Could not decode Opus header "
+                       "packet %d - invalid Opus stream (%d)\n"),
+                        inf->doneheaders, stream->num);
+                continue;
+            } else if (inf->doneheaders==0){
+                if(inf->oh.preskip<120)oi_warn(_("WARNING: Implausibly low preskip in Opus stream (%d)\n"),stream->num);
+            }
+            if(inf->doneheaders==1 && (packet.bytes<8 || memcmp(packet.packet, "OpusTags",8)!=0)) {
+               oi_warn(_("WARNING: Could not decode OpusTags header "
+                       "packet %d - invalid Opus stream (%d)\n"),
+                        inf->doneheaders, stream->num);
+                continue;
+            } else if (inf->doneheaders==1) {
+                char *tmp;
+                char *c=(char *)packet.packet;
+                int length, len, i, nb_fields;
+
+                length=packet.bytes;
+                if (length<(8+4+4)) {
+                    oi_warn(_("Invalid/corrupted comments in stream %d\n"),stream->num);
+                    continue;
+                }
+                c += 8;
+                len=readle32(c, 0);
+                c+=4;
+                if (len < 0 || len>(length-16)) {
+                    oi_warn(_("Invalid/corrupted comments in stream %d\n"),stream->num);
+                    continue;
+                }
+                tmp=calloc(len+1,1);
+                memcpy(tmp,c,len);
+                oi_info(_("Encoded with %s\n"),tmp);
+                free(tmp);
+                c+=len;
+                /*The -16 check above makes sure we can read this.*/
+                nb_fields=readle32(c, 0);
+                c+=4;
+                length-=16+len;
+                if (nb_fields < 0 || nb_fields>(length>>2)) {
+                    oi_warn(_("Invalid/corrupted comments in stream %d\n"),stream->num);
+                    continue;
+                }
+                if(nb_fields)oi_info(_("User comments section follows...\n"));
+                for (i=0;i<nb_fields;i++) {
+                    char *comment;
+                    if (length<4) {
+                        oi_warn(_("Invalid/corrupted comments in stream %d\n"),stream->num);
+                        break;
+                    }
+                    len=readle32(c, 0);
+                    c+=4;
+                    length-=4;
+                    if (len < 0 || len>length) {
+                        oi_warn(_("Invalid/corrupted comments in stream %d\n"),stream->num);
+                        break;
+                    }
+                    /*check_xiph_comment expects a null terminated comment*/
+                    comment=malloc((len+1)*sizeof(char));
+                    memcpy(comment,c,len);
+                    comment[len]=0;
+                    check_xiph_comment(stream, i, comment, len);
+                    free(comment);
+                    c+=len;
+                    length-=len;
+                }
+            }
+
+            inf->doneheaders++;
+            continue;
+        }
+        if(packet.bytes>=2 && memcmp(packet.packet, "Op",2)==0) {
+            oi_warn(_("WARNING: Invalid packet or misplaced header in stream %d\n"),stream->num);
+            continue;
+        }
+        if(packet.bytes<1) {
+            oi_warn(_("WARNING: Invalid zero byte packet in stream %d\n"),stream->num);
+            continue;
+        }
+        spp = packet_get_nb_frames(packet.packet,packet.bytes);
+        if(spp<1 || spp>48) {
+            oi_warn(_("WARNING: Invalid packet TOC in stream %d\n"),stream->num);
+            continue;
+        }
+        spp *= packet_get_samples_per_frame(packet.packet,48000);
+        if(spp<120 || spp>5760 || (spp%120)!=0) {
+            oi_warn(_("WARNING: Invalid packet TOC in stream %d\n"),stream->num);
+            continue;
+        }
+        inf->total_samples += spp;
+        page_samples += spp;
+        inf->total_packets++;
+        inf->last_packet_duration = spp;
+        if(inf->max_packet_duration<spp)inf->max_packet_duration=spp;
+        if(inf->min_packet_duration>spp)inf->min_packet_duration=spp;
+        if(inf->max_packet_bytes<packet.bytes)inf->max_packet_bytes=packet.bytes;
+        if(inf->min_packet_bytes>packet.bytes)inf->min_packet_bytes=packet.bytes;
+    }
+
+    if(!header) {
+        ogg_int64_t gp = ogg_page_granulepos(page);
+        if(gp > 0) {
+            if(gp < inf->lastgranulepos)
+                oi_warn(_("WARNING: granulepos in stream %d decreases from %"
+                        I64FORMAT " to %" I64FORMAT "\n" ),
+                        stream->num, inf->lastgranulepos, gp);
+            if(inf->lastgranulepos==0 && inf->firstgranule==-1) {
+                /*First timed page, now we can recover the start time.*/
+                inf->firstgranule = gp-inf->total_samples;
+                if(inf->firstgranule<0) {
+                  /*There shouldn't be any negative samples after counting the samples in the page backwards
+                    from the first GP, but if this is the last page of the stream there may need to be to trim.*/
+                  if(!ogg_page_eos(page))oi_warn(_("WARNING: Samples with negative granpos in stream %d\n"),stream->num);
+                  else inf->firstgranule=0;
+                }
+            }
+            if(inf->total_samples<gp-inf->firstgranule)oi_warn(_("WARNING: Sample count behind granule (%" I64FORMAT "<%" I64FORMAT ") in stream %d\n"),
+                (long long)inf->total_samples,(long long)(gp-inf->firstgranule),stream->num);
+            if(!ogg_page_eos(page) && (inf->total_samples>gp-inf->firstgranule))
+                oi_warn(_("WARNING: Sample count ahead of granule (%" I64FORMAT ">%" I64FORMAT ") in stream %d\n"),
+                (long long)inf->total_samples,(long long)(gp-inf->firstgranule),stream->num);
+            inf->lastlastgranulepos = inf->lastgranulepos;
+            inf->lastgranulepos = gp;
+            if(!packets)
+                oi_warn(_("WARNING: Page with positive granpos (%" I64FORMAT ") on a page with no completed packets in stream %d\n"),gp,stream->num);
+        }
+        else if(packets) {
+            /* Only do this if we saw at least one packet ending on this page.
+             * It's legal (though very unusual) to have no packets in a page at
+             * all - this is occasionally used to have an empty EOS page */
+            oi_warn(_("Negative or zero granulepos (%" I64FORMAT ") on Opus stream outside of headers. This file was created by a buggy encoder\n"), gp);
+        }
+        inf->overhead_bytes += page->header_len;
+        if(page_samples)inf->last_page_duration = page_samples;
+        if(inf->max_page_duration<page_samples)inf->max_page_duration=page_samples;
+        if(inf->min_page_duration>page_samples)inf->min_page_duration=page_samples;
+        inf->total_pages++;
+    } else {
+        /* Headers and metadata are pure overhead. */
+        inf->overhead_bytes += page->header_len + page->body_len;
+    }
+    inf->bytes += page->header_len + page->body_len;
+}
+
+void info_opus_end(stream_processor *stream)
+{
+    misc_opus_info *inf = stream->data;
+
+    oi_info(_("Opus stream %d:\n"),stream->num);
+
+    if(inf && inf->total_packets>0){
+        int i;
+        long minutes, seconds, milliseconds;
+        double time;
+        time = (inf->lastgranulepos-inf->firstgranule-inf->oh.preskip) / 48000.;
+        if(time<=0)time=0;
+        minutes = (long)(time) / 60;
+        seconds = (long)(time - minutes*60);
+        milliseconds = (long)((time - minutes*60 - seconds)*1000);
+        if(inf->lastgranulepos-inf->firstgranule<inf->oh.preskip)
+           oi_error(_("\tERROR: stream %d has a negative duration: %" I64FORMAT "-%" I64FORMAT "-%d=%" I64FORMAT "\n"),stream->num,
+           inf->lastgranulepos,inf->firstgranule,inf->oh.preskip,inf->lastgranulepos-inf->firstgranule-inf->oh.preskip);
+        if((inf->total_samples-inf->last_page_duration)>(inf->lastgranulepos-inf->firstgranule))
+           oi_error(_("\tERROR: stream %d has interior holes or more than one page of end trimming\n"),stream->num);
+        if(inf->last_eos &&( (inf->last_page_duration-inf->last_packet_duration)>(inf->lastgranulepos-inf->lastlastgranulepos)))
+           oi_warn(_("\tWARNING: stream %d has more than one packet of end trimming\n"),stream->num);
+        if(inf->max_page_duration>=240000)
+           oi_warn(_("\tWARNING: stream %d has high muxing delay\n"),stream->num);
+        oi_info(_("\tPre-skip: %d\n"),inf->oh.preskip);
+        oi_info(_("\tPlayback gain: %g dB\n"),inf->oh.gain/256.);
+        oi_info(_("\tChannels: %d\n"),inf->oh.channels);
+        if(inf->oh.input_sample_rate)oi_info(_("\tOriginal sample rate: %dHz\n"),inf->oh.input_sample_rate);
+        if(inf->oh.nb_streams>1)oi_info(_("\tStreams: %d, Coupled: %d\n"),inf->oh.nb_streams,inf->oh.nb_coupled);
+        if(inf->oh.channel_mapping>0) {
+          oi_info(_("\tChannel Mapping family: %d Map:"),inf->oh.channel_mapping);
+          for(i=0;i<inf->oh.channels;i++)oi_info("%s%d%s",i==0?" [":", ",inf->oh.stream_map[i],i==inf->oh.channels-1?"]\n":"");
+        }
+        if(inf->total_packets)oi_info(_("\tPacket duration: %6.1fms (max), %6.1fms (avg), %6.1fms (min)\n"),
+            inf->max_packet_duration/48.,inf->total_samples/(double)inf->total_packets/48.,inf->min_packet_duration/48.);
+        if(inf->total_pages)oi_info(_("\tPage duration: %8.1fms (max), %6.1fms (avg), %6.1fms (min)\n"),
+            inf->max_page_duration/48.,inf->total_samples/(double)inf->total_pages/48.,inf->min_page_duration/48.);
+        oi_info(_("\tTotal data length: %" I64FORMAT " bytes (overhead: %0.3g%%)\n"),inf->bytes,(double)inf->overhead_bytes/inf->bytes*100.);
+        oi_info(_("\tPlayback length: %ldm:%02ld.%03lds\n"), minutes, seconds, milliseconds);
+        oi_info(_("\tAverage bitrate: %0.4g kb/s, w/o overhead: %.04g kb/s%s\n"),time<=0?0:inf->bytes*8/time/1000.0,
+            time<=0?0:(inf->bytes-inf->overhead_bytes)*8/time/1000.0,
+            (inf->min_packet_duration==inf->max_packet_duration)&&(inf->min_packet_bytes==inf->max_packet_bytes)?" (hard-CBR)":"");
+    } else {
+      oi_warn(_("\tWARNING: stream %d is empty\n"),stream->num);
+    }
+    free(stream->data);
+}
+
+void info_opus_start(stream_processor *stream)
+{
+    misc_opus_info *oinfo;
+
+    stream->type = "opus";
+    stream->process_page = info_opus_process;
+    stream->process_end = info_opus_end;
+
+    stream->data = calloc(1, sizeof(misc_opus_info));
+
+    oinfo = stream->data;
+    oinfo->firstgranule=-1;
+    oinfo->min_packet_duration=5760;
+    oinfo->min_page_duration=5760*255;
+    oinfo->min_packet_bytes=2147483647;
+}
diff --git a/src/info_opus.h b/src/info_opus.h
new file mode 100644
index 0000000..ac3b0e0
--- /dev/null
+++ b/src/info_opus.h
@@ -0,0 +1,51 @@
+/* Copyright (C)2012 Gregory Maxwell
+   File: info_opus.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.
+*/
+
+typedef struct {
+    OpusHeader  oh;
+    ogg_int64_t bytes;
+    ogg_int64_t overhead_bytes;
+    ogg_int64_t lastlastgranulepos;
+    ogg_int64_t lastgranulepos;
+    ogg_int64_t firstgranule;
+    ogg_int64_t total_samples;
+    ogg_int64_t total_packets;
+    ogg_int64_t total_pages;
+    ogg_int32_t last_packet_duration;
+    ogg_int32_t last_page_duration;
+    ogg_int32_t max_page_duration;
+    ogg_int32_t min_page_duration;
+    ogg_int32_t max_packet_duration;
+    ogg_int32_t min_packet_duration;
+    ogg_int32_t max_packet_bytes;
+    ogg_int32_t min_packet_bytes;
+    int last_eos;
+
+    int doneheaders;
+} misc_opus_info;
+
+void info_opus_start(stream_processor *stream);
diff --git a/src/lpc.c b/src/lpc.c
new file mode 100644
index 0000000..40e51f8
--- /dev/null
+++ b/src/lpc.c
@@ -0,0 +1,161 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
+ *                                                                  *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009             *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+  function: LPC low level routines
+  last mod: $Id: lpc.c 16227 2009-07-08 06:58:46Z xiphmont $
+
+ ********************************************************************/
+
+/* Some of these routines (autocorrelator, LPC coefficient estimator)
+   are derived from code written by Jutta Degener and Carsten Bormann;
+   thus we include their copyright below.  The entirety of this file
+   is freely redistributable on the condition that both of these
+   copyright notices are preserved without modification.  */
+
+/* Preserved Copyright: *********************************************/
+
+/* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
+Technische Universita"t Berlin
+
+Any use of this software is permitted provided that this notice is not
+removed and that neither the authors nor the Technische Universita"t
+Berlin are deemed to have made any representations as to the
+suitability of this software for any purpose nor are held responsible
+for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR
+THIS SOFTWARE.
+
+As a matter of courtesy, the authors request to be informed about uses
+this software has found, about bugs in this software, and about any
+improvements that may be of general interest.
+
+Berlin, 28.11.1994
+Jutta Degener
+Carsten Bormann
+
+*********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "stack_alloc.h"
+#include "lpc.h"
+
+/* Autocorrelation LPC coeff generation algorithm invented by
+   N. Levinson in 1947, modified by J. Durbin in 1959. */
+
+/* Input : n elements of time doamin data
+   Output: m lpc coefficients, excitation energy */
+
+float vorbis_lpc_from_data(float *data,float *lpci,int n,int m,int stride){
+  double *aut=alloca(sizeof(*aut)*(m+1));
+  double *lpc=alloca(sizeof(*lpc)*(m));
+  double error;
+  double epsilon;
+  int i,j;
+
+  /* autocorrelation, p+1 lag coefficients */
+  j=m+1;
+  while(j--){
+    double d=0; /* double needed for accumulator depth */
+    for(i=j;i<n;i++)d+=(double)data[i*stride]*data[(i-j)*stride];
+    aut[j]=d;
+  }
+
+  /* Generate lpc coefficients from autocorr values */
+
+  /* set our noise floor to about -100dB */
+  error=aut[0] * (1. + 1e-10);
+  epsilon=1e-9*aut[0]+1e-10;
+
+  for(i=0;i<m;i++){
+    double r= -aut[i+1];
+
+    if(error<epsilon){
+      memset(lpc+i,0,(m-i)*sizeof(*lpc));
+      goto done;
+    }
+
+    /* Sum up this iteration's reflection coefficient; note that in
+       Vorbis we don't save it.  If anyone wants to recycle this code
+       and needs reflection coefficients, save the results of 'r' from
+       each iteration. */
+
+    for(j=0;j<i;j++)r-=lpc[j]*aut[i-j];
+    r/=error;
+
+    /* Update LPC coefficients and total error */
+
+    lpc[i]=r;
+    for(j=0;j<i/2;j++){
+      double tmp=lpc[j];
+
+      lpc[j]+=r*lpc[i-1-j];
+      lpc[i-1-j]+=r*tmp;
+    }
+    if(i&1)lpc[j]+=lpc[j]*r;
+
+    error*=1.-r*r;
+
+  }
+
+ done:
+
+  /* slightly damp the filter */
+  {
+    double g = .99;
+    double damp = g;
+    for(j=0;j<m;j++){
+      lpc[j]*=damp;
+      damp*=g;
+    }
+  }
+
+  for(j=0;j<m;j++)lpci[j]=(float)lpc[j];
+
+  /* we need the error value to know how big an impulse to hit the
+     filter with later */
+
+  return error;
+}
+
+void vorbis_lpc_predict(float *coeff,float *prime,int m,
+                     float *data,long n,int stride){
+
+  /* in: coeff[0...m-1] LPC coefficients
+         prime[0...m-1] initial values (allocated size of n+m-1)
+    out: data[0...n-1] data samples */
+
+  long i,j,o,p;
+  float y;
+  float *work=alloca(sizeof(*work)*(m+n));
+
+  if(!prime)
+    for(i=0;i<m;i++)
+      work[i]=0.f;
+  else
+    for(i=0;i<m;i++)
+      work[i]=prime[i*stride];
+
+  for(i=0;i<n;i++){
+    y=0;
+    o=i;
+    p=m;
+    for(j=0;j<m;j++)
+      y-=work[o++]*coeff[--p];
+
+    data[i*stride]=work[o]=y;
+  }
+}
diff --git a/src/lpc.h b/src/lpc.h
new file mode 100644
index 0000000..e4097a3
--- /dev/null
+++ b/src/lpc.h
@@ -0,0 +1,27 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
+ *                                                                  *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007             *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+  function: LPC low level routines
+  last mod: $Id: lpc.h 16037 2009-05-26 21:10:58Z xiphmont $
+
+ ********************************************************************/
+
+#ifndef _V_LPC_H_
+#define _V_LPC_H_
+
+/* simple linear scale LPC code */
+extern float vorbis_lpc_from_data(float *data,float *lpc,int n,int m,int stride);
+
+extern void vorbis_lpc_predict(float *coeff,float *prime,int m,
+                               float *data,long n,int stride);
+
+#endif
diff --git a/src/opus_header.c b/src/opus_header.c
new file mode 100644
index 0000000..d00d4fb
--- /dev/null
+++ b/src/opus_header.c
@@ -0,0 +1,286 @@
+/* Copyright (C)2012 Xiph.Org Foundation
+   File: opus_header.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 "opus_header.h"
+#include <string.h>
+#include <stdio.h>
+
+/* Header contents:
+  - "OpusHead" (64 bits)
+  - version number (8 bits)
+  - Channels C (8 bits)
+  - Pre-skip (16 bits)
+  - Sampling rate (32 bits)
+  - Gain in dB (16 bits, S7.8)
+  - Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping,
+             2..254: reserved, 255: multistream with no mapping)
+
+  - if (mapping != 0)
+     - N = totel number of streams (8 bits)
+     - M = number of paired streams (8 bits)
+     - C times channel origin
+          - if (C<2*M)
+             - stream = byte/2
+             - if (byte&0x1 == 0)
+                 - left
+               else
+                 - right
+          - else
+             - stream = byte-M
+*/
+
+typedef struct {
+   unsigned char *data;
+   int maxlen;
+   int pos;
+} Packet;
+
+typedef struct {
+   const unsigned char *data;
+   int maxlen;
+   int pos;
+} ROPacket;
+
+static int write_uint32(Packet *p, ogg_uint32_t val)
+{
+   if (p->pos>p->maxlen-4)
+      return 0;
+   p->data[p->pos  ] = (val    ) & 0xFF;
+   p->data[p->pos+1] = (val>> 8) & 0xFF;
+   p->data[p->pos+2] = (val>>16) & 0xFF;
+   p->data[p->pos+3] = (val>>24) & 0xFF;
+   p->pos += 4;
+   return 1;
+}
+
+static int write_uint16(Packet *p, ogg_uint16_t val)
+{
+   if (p->pos>p->maxlen-2)
+      return 0;
+   p->data[p->pos  ] = (val    ) & 0xFF;
+   p->data[p->pos+1] = (val>> 8) & 0xFF;
+   p->pos += 2;
+   return 1;
+}
+
+static int write_chars(Packet *p, const unsigned char *str, int nb_chars)
+{
+   int i;
+   if (p->pos>p->maxlen-nb_chars)
+      return 0;
+   for (i=0;i<nb_chars;i++)
+      p->data[p->pos++] = str[i];
+   return 1;
+}
+
+static int read_uint32(ROPacket *p, ogg_uint32_t *val)
+{
+   if (p->pos>p->maxlen-4)
+      return 0;
+   *val =  (ogg_uint32_t)p->data[p->pos  ];
+   *val |= (ogg_uint32_t)p->data[p->pos+1]<< 8;
+   *val |= (ogg_uint32_t)p->data[p->pos+2]<<16;
+   *val |= (ogg_uint32_t)p->data[p->pos+3]<<24;
+   p->pos += 4;
+   return 1;
+}
+
+static int read_uint16(ROPacket *p, ogg_uint16_t *val)
+{
+   if (p->pos>p->maxlen-2)
+      return 0;
+   *val =  (ogg_uint16_t)p->data[p->pos  ];
+   *val |= (ogg_uint16_t)p->data[p->pos+1]<<8;
+   p->pos += 2;
+   return 1;
+}
+
+static int read_chars(ROPacket *p, unsigned char *str, int nb_chars)
+{
+   int i;
+   if (p->pos>p->maxlen-nb_chars)
+      return 0;
+   for (i=0;i<nb_chars;i++)
+      str[i] = p->data[p->pos++];
+   return 1;
+}
+
+int opus_header_parse(const unsigned char *packet, int len, OpusHeader *h)
+{
+   int i;
+   char str[9];
+   ROPacket p;
+   unsigned char ch;
+   ogg_uint16_t shortval;
+
+   p.data = packet;
+   p.maxlen = len;
+   p.pos = 0;
+   str[8] = 0;
+   if (len<19)return 0;
+   read_chars(&p, (unsigned char*)str, 8);
+   if (memcmp(str, "OpusHead", 8)!=0)
+      return 0;
+
+   if (!read_chars(&p, &ch, 1))
+      return 0;
+   h->version = ch;
+   if((h->version&240) != 0) /* Only major version 0 supported. */
+      return 0;
+
+   if (!read_chars(&p, &ch, 1))
+      return 0;
+   h->channels = ch;
+   if (h->channels == 0)
+      return 0;
+
+   if (!read_uint16(&p, &shortval))
+      return 0;
+   h->preskip = shortval;
+
+   if (!read_uint32(&p, &h->input_sample_rate))
+      return 0;
+
+   if (!read_uint16(&p, &shortval))
+      return 0;
+   h->gain = (short)shortval;
+
+   if (!read_chars(&p, &ch, 1))
+      return 0;
+   h->channel_mapping = ch;
+
+   if (h->channel_mapping != 0)
+   {
+      if (!read_chars(&p, &ch, 1))
+         return 0;
+
+      if (ch<1)
+         return 0;
+      h->nb_streams = ch;
+
+      if (!read_chars(&p, &ch, 1))
+         return 0;
+
+      if (ch>h->nb_streams || (ch+h->nb_streams)>255)
+         return 0;
+      h->nb_coupled = ch;
+
+      /* Multi-stream support */
+      for (i=0;i<h->channels;i++)
+      {
+         if (!read_chars(&p, &h->stream_map[i], 1))
+            return 0;
+         if (h->stream_map[i]>(h->nb_streams+h->nb_coupled) && h->stream_map[i]!=255)
+            return 0;
+      }
+   } else {
+      if(h->channels>2)
+         return 0;
+      h->nb_streams = 1;
+      h->nb_coupled = h->channels>1;
+      h->stream_map[0]=0;
+      h->stream_map[1]=1;
+   }
+   /*For version 0/1 we know there won't be any more data
+     so reject any that have data past the end.*/
+   if ((h->version==0 || h->version==1) && p.pos != len)
+      return 0;
+   return 1;
+}
+
+int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len)
+{
+   int i;
+   Packet p;
+   unsigned char ch;
+
+   p.data = packet;
+   p.maxlen = len;
+   p.pos = 0;
+   if (len<19)return 0;
+   if (!write_chars(&p, (const unsigned char*)"OpusHead", 8))
+      return 0;
+   /* Version is 1 */
+   ch = 1;
+   if (!write_chars(&p, &ch, 1))
+      return 0;
+
+   ch = h->channels;
+   if (!write_chars(&p, &ch, 1))
+      return 0;
+
+   if (!write_uint16(&p, h->preskip))
+      return 0;
+
+   if (!write_uint32(&p, h->input_sample_rate))
+      return 0;
+
+   if (!write_uint16(&p, h->gain))
+      return 0;
+
+   ch = h->channel_mapping;
+   if (!write_chars(&p, &ch, 1))
+      return 0;
+
+   if (h->channel_mapping != 0)
+   {
+      ch = h->nb_streams;
+      if (!write_chars(&p, &ch, 1))
+         return 0;
+
+      ch = h->nb_coupled;
+      if (!write_chars(&p, &ch, 1))
+         return 0;
+
+      /* Multi-stream support */
+      for (i=0;i<h->channels;i++)
+      {
+         if (!write_chars(&p, &h->stream_map[i], 1))
+            return 0;
+      }
+   }
+
+   return p.pos;
+}
+
+/* This is just here because it's a convenient file linked by both opusenc and
+   opusdec (to guarantee this maps stays in sync). */
+const int wav_permute_matrix[8][8] =
+{
+  {0},              /* 1.0 mono   */
+  {0,1},            /* 2.0 stereo */
+  {0,2,1},          /* 3.0 channel ('wide') stereo */
+  {0,1,2,3},        /* 4.0 discrete quadraphonic */
+  {0,2,1,3,4},      /* 5.0 surround */
+  {0,2,1,4,5,3},    /* 5.1 surround */
+  {0,2,1,5,6,4,3},  /* 6.1 surround */
+  {0,2,1,6,7,4,5,3} /* 7.1 surround (classic theater 8-track) */
+};
diff --git a/src/opus_header.h b/src/opus_header.h
new file mode 100644
index 0000000..473e03f
--- /dev/null
+++ b/src/opus_header.h
@@ -0,0 +1,51 @@
+/* Copyright (C)2012 Xiph.Org Foundation
+   File: opus_header.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 OPUS_HEADER_H
+#define OPUS_HEADER_H
+
+#include <ogg/ogg.h>
+
+typedef struct {
+   int version;
+   int channels; /* Number of channels: 1..255 */
+   int preskip;
+   ogg_uint32_t input_sample_rate;
+   int gain; /* in dB S7.8 should be zero whenever possible */
+   int channel_mapping;
+   /* The rest is only used if channel_mapping != 0 */
+   int nb_streams;
+   int nb_coupled;
+   unsigned char stream_map[255];
+} OpusHeader;
+
+int opus_header_parse(const unsigned char *header, int len, OpusHeader *h);
+int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len);
+
+extern const int wav_permute_matrix[8][8];
+
+#endif
diff --git a/src/opusdec.c b/src/opusdec.c
new file mode 100644
index 0000000..e34290e
--- /dev/null
+++ b/src/opusdec.c
@@ -0,0 +1,1136 @@
+/* Copyright (c) 2002-2007 Jean-Marc Valin
+   Copyright (c) 2008 CSIRO
+   Copyright (c) 2007-2013 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
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h> /*tolower()*/
+
+#include <opus.h>
+#include <opus_multistream.h>
+#include <ogg/ogg.h>
+
+#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
+# include "unicode_support.h"
+# include "wave_out.h"
+/* We need the following two to set stdout to binary */
+# include <io.h>
+# include <fcntl.h>
+# define I64FORMAT "I64d"
+#else
+# define I64FORMAT "lld"
+# define fopen_utf8(_x,_y) fopen((_x),(_y))
+# define argc_utf8 argc
+# define argv_utf8 argv
+#endif
+
+#include <math.h>
+
+#ifdef HAVE_LRINTF
+# define float2int(x) lrintf(x)
+#else
+# define float2int(flt) ((int)(floor(.5+flt)))
+#endif
+
+#if defined HAVE_LIBSNDIO
+# include <sndio.h>
+#elif defined HAVE_SYS_SOUNDCARD_H || defined HAVE_MACHINE_SOUNDCARD_H || HAVE_SOUNDCARD_H
+# ifdef HAVE_SYS_SOUNDCARD_H
+#  include <sys/soundcard.h>
+# elif HAVE_MACHINE_SOUNDCARD_H
+#  include <machine/soundcard.h>
+# else
+#  include <soundcard.h>
+# endif
+# 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"
+#include "diag_range.h"
+#include "speex_resampler.h"
+#include "stack_alloc.h"
+#include "cpusupport.h"
+
+#define MINI(_a,_b)      ((_a)<(_b)?(_a):(_b))
+#define MAXI(_a,_b)      ((_a)>(_b)?(_a):(_b))
+#define CLAMPI(_a,_b,_c) (MAXI(_a,MINI(_b,_c)))
+
+/* 120ms at 48000 */
+#define MAX_FRAME_SIZE (960*6)
+
+#define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
+                           ((buf[base+2]<<16)&0xff0000)| \
+                           ((buf[base+1]<<8)&0xff00)| \
+                           (buf[base]&0xff))
+
+#ifdef HAVE_LIBSNDIO
+struct sio_hdl *hdl;
+#endif
+
+typedef struct shapestate shapestate;
+struct shapestate {
+  float * b_buf;
+  float * a_buf;
+  int fs;
+  int mute;
+};
+
+static unsigned int rngseed = 22222;
+static inline unsigned int fast_rand(void) {
+  rngseed = (rngseed * 96314165) + 907633515;
+  return rngseed;
+}
+
+#ifndef HAVE_FMINF
+# define fminf(_x,_y) ((_x)<(_y)?(_x):(_y))
+#endif
+
+#ifndef HAVE_FMAXF
+# define fmaxf(_x,_y) ((_x)>(_y)?(_x):(_y))
+#endif
+
+static void quit(int _x) {
+#ifdef WIN_UNICODE
+  uninit_console_utf8();
+#endif
+  exit(_x);
+}
+
+/* This implements a 16 bit quantization with full triangular dither
+   and IIR noise shaping. The noise shaping filters were designed by
+   Sebastian Gesemann based on the LAME ATH curves with flattening
+   to limit their peak gain to 20 dB.
+   (Everyone elses' noise shaping filters are mildly crazy)
+   The 48kHz version of this filter is just a warped version of the
+   44.1kHz filter and probably could be improved by shifting the
+   HF shelf up in frequency a little bit since 48k has a bit more
+   room and being more conservative against bat-ears is probably
+   more important than more noise suppression.
+   This process can increase the peak level of the signal (in theory
+   by the peak error of 1.5 +20 dB though this much is unobservable rare)
+   so to avoid clipping the signal is attenuated by a couple thousandths
+   of a dB. Initially the approach taken here was to only attenuate by
+   the 99.9th percentile, making clipping rare but not impossible (like
+   SoX) but the limited gain of the filter means that the worst case was
+   only two thousandths of a dB more, so this just uses the worst case.
+   The attenuation is probably also helpful to prevent clipping in the DAC
+   reconstruction filters or downstream resampling in any case.*/
+static inline void shape_dither_toshort(shapestate *_ss, short *_o, float *_i, int _n, int _CC)
+{
+  const float gains[3]={32768.f-15.f,32768.f-15.f,32768.f-3.f};
+  const float fcoef[3][8] =
+  {
+    {2.2374f, -.7339f, -.1251f, -.6033f, 0.9030f, .0116f, -.5853f, -.2571f}, /* 48.0kHz noise shaping filter sd=2.34*/
+    {2.2061f, -.4706f, -.2534f, -.6214f, 1.0587f, .0676f, -.6054f, -.2738f}, /* 44.1kHz noise shaping filter sd=2.51*/
+    {1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f,0.0000f, 0.0000f, 0.0000f}, /* lowpass noise shaping filter sd=0.65*/
+  };
+  int i;
+  int rate=_ss->fs==44100?1:(_ss->fs==48000?0:2);
+  float gain=gains[rate];
+  float *b_buf;
+  float *a_buf;
+  int mute=_ss->mute;
+  b_buf=_ss->b_buf;
+  a_buf=_ss->a_buf;
+  /*In order to avoid replacing digital silence with quiet dither noise
+    we mute if the output has been silent for a while*/
+  if(mute>64)
+    memset(a_buf,0,sizeof(float)*_CC*4);
+  for(i=0;i<_n;i++)
+  {
+    int c;
+    int pos = i*_CC;
+    int silent=1;
+    for(c=0;c<_CC;c++)
+    {
+      int j, si;
+      float r,s,err=0;
+      silent&=_i[pos+c]==0;
+      s=_i[pos+c]*gain;
+      for(j=0;j<4;j++)
+        err += fcoef[rate][j]*b_buf[c*4+j] - fcoef[rate][j+4]*a_buf[c*4+j];
+      memmove(&a_buf[c*4+1],&a_buf[c*4],sizeof(float)*3);
+      memmove(&b_buf[c*4+1],&b_buf[c*4],sizeof(float)*3);
+      a_buf[c*4]=err;
+      s = s - err;
+      r=(float)fast_rand()*(1/(float)UINT_MAX) - (float)fast_rand()*(1/(float)UINT_MAX);
+      if (mute>16)r=0;
+      /*Clamp in float out of paranoia that the input will be >96 dBFS and wrap if the
+        integer is clamped.*/
+      _o[pos+c] = si = float2int(fmaxf(-32768,fminf(s + r,32767)));
+      /*Including clipping in the noise shaping is generally disastrous:
+        the futile effort to restore the clipped energy results in more clipping.
+        However, small amounts-- at the level which could normally be created by
+        dither and rounding-- are harmless and can even reduce clipping somewhat
+        due to the clipping sometimes reducing the dither+rounding error.*/
+      b_buf[c*4] = (mute>16)?0:fmaxf(-1.5f,fminf(si - s,1.5f));
+    }
+    mute++;
+    if(!silent)mute=0;
+  }
+  _ss->mute=MINI(mute,960);
+}
+
+static void print_comments(char *comments, int length)
+{
+   char *c=comments;
+   int len, i, nb_fields, err=0;
+
+   if (length<(8+4+4))
+   {
+      fprintf (stderr, "Invalid/corrupted comments\n");
+      return;
+   }
+   if (strncmp(c, "OpusTags", 8) != 0)
+   {
+      fprintf (stderr, "Invalid/corrupted comments\n");
+      return;
+   }
+   c += 8;
+   fprintf(stderr, "Encoded with ");
+   len=readint(c, 0);
+   c+=4;
+   if (len < 0 || len>(length-16))
+   {
+      fprintf (stderr, "Invalid/corrupted comments\n");
+      return;
+   }
+   err&=fwrite(c, 1, len, stderr)!=(unsigned)len;
+   c+=len;
+   fprintf (stderr, "\n");
+   /*The -16 check above makes sure we can read this.*/
+   nb_fields=readint(c, 0);
+   c+=4;
+   length-=16+len;
+   if (nb_fields < 0 || nb_fields>(length>>2))
+   {
+      fprintf (stderr, "Invalid/corrupted comments\n");
+      return;
+   }
+   for (i=0;i<nb_fields;i++)
+   {
+      if (length<4)
+      {
+         fprintf (stderr, "Invalid/corrupted comments\n");
+         return;
+      }
+      len=readint(c, 0);
+      c+=4;
+      length-=4;
+      if (len < 0 || len>length)
+      {
+         fprintf (stderr, "Invalid/corrupted comments\n");
+         return;
+      }
+      err&=fwrite(c, 1, len, stderr)!=(unsigned)len;
+      c+=len;
+      length-=len;
+      fprintf (stderr, "\n");
+   }
+}
+
+FILE *out_file_open(char *outFile, int *wav_format, int rate, int mapping_family, int *channels, int fp)
+{
+   FILE *fout=NULL;
+   /*Open output file*/
+   if (strlen(outFile)==0)
+   {
+#if defined HAVE_LIBSNDIO
+      struct sio_par par;
+
+      hdl = sio_open(NULL, SIO_PLAY, 0);
+      if (!hdl)
+      {
+         fprintf(stderr, "Cannot open sndio device\n");
+         quit(1);
+      }
+
+      sio_initpar(&par);
+      par.sig = 1;
+      par.bits = 16;
+      par.rate = rate;
+      par.pchan = *channels;
+
+      if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par) ||
+        par.sig != 1 || par.bits != 16 || par.rate != rate) {
+          fprintf(stderr, "could not set sndio parameters\n");
+          quit(1);
+      }
+      *channels = par.pchan;
+      if (!sio_start(hdl)) {
+          fprintf(stderr, "could not start sndio\n");
+          quit(1);
+      }
+#elif defined HAVE_SYS_SOUNDCARD_H || defined HAVE_MACHINE_SOUNDCARD_H || HAVE_SOUNDCARD_H
+      int audio_fd, format, stereo;
+      audio_fd=open("/dev/dsp", O_WRONLY);
+      if (audio_fd<0)
+      {
+         perror("Cannot open /dev/dsp");
+         quit(1);
+      }
+
+      format=AFMT_S16_NE;
+      if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1)
+      {
+         perror("SNDCTL_DSP_SETFMT");
+         close(audio_fd);
+         quit(1);
+      }
+
+      if (*channels > 2)
+      {
+        /* There doesn't seem to be a way to get or set the channel
+         * matrix with the sys/soundcard api, so we can't support
+         * multichannel. We should fall back to stereo downmix.
+         */
+        fprintf(stderr, "Cannot configure multichannel playback."
+                        " Try decoding to a file instead.\n");
+        close(audio_fd);
+        quit(1);
+      }
+      stereo=0;
+      if (*channels==2)
+         stereo=1;
+      if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1)
+      {
+         perror("SNDCTL_DSP_STEREO");
+         close(audio_fd);
+         quit(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);
+         quit(1);
+      }
+      fout = fdopen(audio_fd, "w");
+      if(!fout)
+      {
+        perror("Cannot open output");
+        quit(1);
+      }
+#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");
+         quit(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.input_sample_rate = rate;
+      info.play.channels = *channels;
+
+      if (ioctl(audio_fd, AUDIO_SETINFO, &info) < 0)
+      {
+         perror ("AUDIO_SETINFO");
+         quit(1);
+      }
+      fout = fdopen(audio_fd, "w");
+      if(!fout)
+      {
+        perror("Cannot open output");
+        quit(1);
+      }
+#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");
+            quit(1);
+         }
+      }
+#else
+      fprintf (stderr, "No soundcard support\n");
+      quit(1);
+#endif
+   } else {
+      if (strcmp(outFile,"-")==0)
+      {
+#if defined WIN32 || defined _WIN32
+         _setmode(_fileno(stdout), _O_BINARY);
+#endif
+         fout=stdout;
+      }
+      else
+      {
+         fout = fopen_utf8(outFile, "wb");
+         if (!fout)
+         {
+            perror(outFile);
+            quit(1);
+         }
+      }
+      if (*wav_format)
+      {
+         *wav_format = write_wav_header(fout, rate, mapping_family, *channels, fp);
+         if (*wav_format < 0)
+         {
+            fprintf (stderr, "Error writing WAV header.\n");
+            quit(1);
+         }
+      }
+   }
+   return fout;
+}
+
+void usage(void)
+{
+   printf ("Usage: opusdec [options] input_file.opus [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.opus        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 than .wav)\n");
+   printf ("  -                    stdout (raw; unless --force-wav)\n");
+   printf ("  (nothing)            Will be played to soundcard\n");
+   printf ("\n");
+   printf ("Options:\n");
+   printf (" --rate n              Force decoding at sampling rate n Hz\n");
+   printf (" --gain n              Manually adjust gain by n.nn dB (0 default)\n");
+   printf (" --no-dither           Do not dither 16-bit output\n");
+   printf (" --float               32-bit floating-point output\n");
+   printf (" --force-wav           Force wav header on output\n");
+   printf (" --packet-loss n       Simulate n %% random packet loss\n");
+   printf (" --save-range file     Saves check values for every frame to a file\n");
+   printf (" -h, --help            This help\n");
+   printf (" -V, --version         Version information\n");
+   printf (" --quiet               Quiet mode\n");
+   printf ("\n");
+}
+
+void version(void)
+{
+   printf("opusdec %s %s (using %s)\n",PACKAGE_NAME,PACKAGE_VERSION,opus_get_version_string());
+   printf("Copyright (C) 2008-2013 Xiph.Org Foundation\n");
+}
+
+void version_short(void)
+{
+   version();
+}
+
+/*Process an Opus header and setup the opus decoder based on it.
+  It takes several pointers for header values which are needed
+  elsewhere in the code.*/
+static OpusMSDecoder *process_header(ogg_packet *op, opus_int32 *rate,
+       int *mapping_family, int *channels, int *preskip, float *gain,
+       float manual_gain, int *streams, int wav_format, int quiet)
+{
+   int err;
+   OpusMSDecoder *st;
+   OpusHeader header;
+
+   if (opus_header_parse(op->packet, op->bytes, &header)==0)
+   {
+      fprintf(stderr, "Cannot parse header\n");
+      return NULL;
+   }
+
+   *mapping_family = header.channel_mapping;
+   *channels = header.channels;
+   if(wav_format)adjust_wav_mapping(*mapping_family, *channels, header.stream_map);
+
+   if(!*rate)*rate=header.input_sample_rate;
+   /*If the rate is unspecified we decode to 48000*/
+   if(*rate==0)*rate=48000;
+   if(*rate<8000||*rate>192000){
+     fprintf(stderr,"Warning: Crazy input_rate %d, decoding to 48000 instead.\n",*rate);
+     *rate=48000;
+   }
+
+   *preskip = header.preskip;
+   st = opus_multistream_decoder_create(48000, header.channels, header.nb_streams, header.nb_coupled, header.stream_map, &err);
+   if(err != OPUS_OK){
+     fprintf(stderr, "Cannot create decoder: %s\n", opus_strerror(err));
+     return NULL;
+   }
+   if (!st)
+   {
+      fprintf (stderr, "Decoder initialization failed: %s\n", opus_strerror(err));
+      return NULL;
+   }
+
+   *streams=header.nb_streams;
+
+   if(header.gain!=0 || manual_gain!=0)
+   {
+      /*Gain API added in a newer libopus version, if we don't have it
+        we apply the gain ourselves. We also add in a user provided
+        manual gain at the same time.*/
+      int gainadj = (int)(manual_gain*256.)+header.gain;
+#ifdef OPUS_SET_GAIN
+      err=opus_multistream_decoder_ctl(st,OPUS_SET_GAIN(gainadj));
+      if(err==OPUS_UNIMPLEMENTED)
+      {
+#endif
+         *gain = pow(10., gainadj/5120.);
+#ifdef OPUS_SET_GAIN
+      } else if (err!=OPUS_OK)
+      {
+         fprintf (stderr, "Error setting gain: %s\n", opus_strerror(err));
+         return NULL;
+      }
+#endif
+   }
+
+   if (!quiet)
+   {
+      fprintf(stderr, "Decoding to %d Hz (%d channel%s)", *rate,
+        *channels, *channels>1?"s":"");
+      if(header.version!=1)fprintf(stderr, ", Header v%d",header.version);
+      fprintf(stderr, "\n");
+      if (header.gain!=0)fprintf(stderr,"Playback gain: %f dB\n", header.gain/256.);
+      if (manual_gain!=0)fprintf(stderr,"Manual gain: %f dB\n", manual_gain);
+   }
+
+   return st;
+}
+
+opus_int64 audio_write(float *pcm, int channels, int frame_size, FILE *fout, SpeexResamplerState *resampler,
+                       int *skip, shapestate *shapemem, int file, opus_int64 maxout, int fp)
+{
+   opus_int64 sampout=0;
+   int i,ret,tmp_skip;
+   unsigned out_len;
+   short *out;
+   float *buf;
+   float *output;
+   out=alloca(sizeof(short)*MAX_FRAME_SIZE*channels);
+   buf=alloca(sizeof(float)*MAX_FRAME_SIZE*channels);
+   maxout=maxout<0?0:maxout;
+   do {
+     if (skip){
+       tmp_skip = (*skip>frame_size) ? (int)frame_size : *skip;
+       *skip -= tmp_skip;
+     } else {
+       tmp_skip = 0;
+     }
+     if (resampler){
+       unsigned in_len;
+       output=buf;
+       in_len = frame_size-tmp_skip;
+       out_len = 1024<maxout?1024:maxout;
+       speex_resampler_process_interleaved_float(resampler, pcm+channels*tmp_skip, &in_len, buf, &out_len);
+       pcm += channels*(in_len+tmp_skip);
+       frame_size -= in_len+tmp_skip;
+     } else {
+       output=pcm+channels*tmp_skip;
+       out_len=frame_size-tmp_skip;
+       frame_size=0;
+     }
+
+     if(!file||!fp)
+     {
+        /*Convert to short and save to output file*/
+        if (shapemem){
+          shape_dither_toshort(shapemem,out,output,out_len,channels);
+        }else{
+          for (i=0;i<(int)out_len*channels;i++)
+            out[i]=(short)float2int(fmaxf(-32768,fminf(output[i]*32768.f,32767)));
+        }
+        if ((le_short(1)!=1)&&file){
+          for (i=0;i<(int)out_len*channels;i++)
+            out[i]=le_short(out[i]);
+        }
+     }
+
+     if(maxout>0)
+     {
+#if defined WIN32 || defined _WIN32
+       if(!file){
+         ret=WIN_Play_Samples (out, sizeof(short) * channels * (out_len<maxout?out_len:maxout));
+         if(ret>0)ret/=sizeof(short)*channels;
+         else fprintf(stderr, "Error playing audio.\n");
+       }else
+#elif defined HAVE_LIBSNDIO
+       if(!file){
+         ret=sio_write (hdl, out, sizeof(short) * channels * (out_len<maxout?out_len:maxout));
+         if(ret>0)ret/=sizeof(short)*channels;
+         else fprintf(stderr, "Error playing audio.\n");
+       }else
+#endif
+         ret=fwrite(fp?(char *)output:(char *)out, (fp?4:2)*channels, out_len<maxout?out_len:maxout, fout);
+       sampout+=ret;
+       maxout-=ret;
+     }
+   } while (frame_size>0 && maxout>0);
+   return sampout;
+}
+
+int main(int argc, char **argv)
+{
+   int c;
+   int option_index = 0;
+   char *inFile, *outFile;
+   FILE *fin, *fout=NULL, *frange=NULL;
+   float *output;
+   int frame_size=0;
+   OpusMSDecoder *st=NULL;
+   opus_int64 packet_count=0;
+   int total_links=0;
+   int stream_init = 0;
+   int quiet = 0;
+   int forcewav = 0;
+   ogg_int64_t page_granule=0;
+   ogg_int64_t link_out=0;
+   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},
+      {"gain", required_argument, NULL, 0},
+      {"no-dither", no_argument, NULL, 0},
+      {"float", no_argument, NULL, 0},
+      {"force-wav", no_argument, NULL, 0},
+      {"packet-loss", required_argument, NULL, 0},
+      {"save-range", required_argument, NULL, 0},
+      {0, 0, 0, 0}
+   };
+   ogg_sync_state oy;
+   ogg_page       og;
+   ogg_packet     op;
+   ogg_stream_state os;
+   int close_in=0;
+   int eos=0;
+   ogg_int64_t audio_size=0;
+   double last_coded_seconds=0;
+   float loss_percent=-1;
+   float manual_gain=0;
+   int channels=-1;
+   int mapping_family;
+   int rate=0;
+   int wav_format=0;
+   int preskip=0;
+   int gran_offset=0;
+   int has_opus_stream=0;
+   int has_tags_packet=0;
+   ogg_int32_t opus_serialno;
+   int dither=1;
+   int fp=0;
+   shapestate shapemem;
+   SpeexResamplerState *resampler=NULL;
+   float gain=1;
+   int streams=0;
+   size_t last_spin=0;
+#ifdef WIN_UNICODE
+   int argc_utf8;
+   char **argv_utf8;
+#endif
+
+   if(query_cpu_support()){
+     fprintf(stderr,"\n\n** WARNING: This program with compiled with SSE%s\n",query_cpu_support()>1?"2":"");
+     fprintf(stderr,"            but this CPU claims to lack these instructions. **\n\n");
+   }
+
+#ifdef WIN_UNICODE
+   (void)argc;
+   (void)argv;
+
+   init_console_utf8();
+   init_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
+#endif
+
+   output=0;
+   shapemem.a_buf=0;
+   shapemem.b_buf=0;
+   shapemem.mute=960;
+   shapemem.fs=0;
+
+   /*Process options*/
+   while(1)
+   {
+      c = getopt_long (argc_utf8, argv_utf8, "hV",
+                       long_options, &option_index);
+      if (c==-1)
+         break;
+
+      switch(c)
+      {
+      case 0:
+         if (strcmp(long_options[option_index].name,"help")==0)
+         {
+            usage();
+            quit(0);
+         } else if (strcmp(long_options[option_index].name,"quiet")==0)
+         {
+            quiet = 1;
+         } else if (strcmp(long_options[option_index].name,"version")==0)
+         {
+            version();
+            quit(0);
+         } else if (strcmp(long_options[option_index].name,"version-short")==0)
+         {
+            version_short();
+            quit(0);
+         } else if (strcmp(long_options[option_index].name,"no-dither")==0)
+         {
+            dither=0;
+         } else if (strcmp(long_options[option_index].name,"float")==0)
+         {
+            fp=1;
+         } else if (strcmp(long_options[option_index].name,"force-wav")==0)
+         {
+            forcewav=1;
+         } else if (strcmp(long_options[option_index].name,"rate")==0)
+         {
+            rate=atoi (optarg);
+         } else if (strcmp(long_options[option_index].name,"gain")==0)
+         {
+            manual_gain=atof (optarg);
+         }else if(strcmp(long_options[option_index].name,"save-range")==0){
+          frange=fopen_utf8(optarg,"w");
+          if(frange==NULL){
+            perror(optarg);
+            fprintf(stderr,"Could not open save-range file: %s\n",optarg);
+            fprintf(stderr,"Must provide a writable file name.\n");
+            quit(1);
+          }
+         } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
+         {
+            loss_percent = atof(optarg);
+         }
+         break;
+      case 'h':
+         usage();
+         quit(0);
+         break;
+      case 'V':
+         version();
+         quit(0);
+         break;
+      case '?':
+         usage();
+         quit(1);
+         break;
+      }
+   }
+   if (argc_utf8-optind!=2 && argc_utf8-optind!=1)
+   {
+      usage();
+      quit(1);
+   }
+   inFile=argv_utf8[optind];
+
+   /*Output to a file or playback?*/
+   if (argc_utf8-optind==2){
+     /*If we're outputting to a file, should we apply a wav header?*/
+     int i;
+     char *ext;
+     outFile=argv_utf8[optind+1];
+     ext=".wav";
+     i=strlen(outFile)-4;
+     wav_format=i>=0;
+     while(wav_format&&ext&&outFile[i]) {
+       wav_format&=tolower(outFile[i++])==*ext++;
+     }
+     wav_format|=forcewav;
+   }else {
+     outFile="";
+     wav_format=0;
+     /*If playing to audio out, default the rate to 48000
+       instead of the original rate. The original rate is
+       only important for minimizing surprise about the rate
+       of output files and preserving length, which aren't
+       relevant for playback. Many audio devices sound
+       better at 48kHz and not resampling also saves CPU.*/
+     if(rate==0)rate=48000;
+     /*Playback is 16-bit only.*/
+     fp=0;
+   }
+   /*If the output is floating point, don't dither.*/
+   if(fp)dither=0;
+
+   /*Open input file*/
+   if (strcmp(inFile, "-")==0)
+   {
+#if defined WIN32 || defined _WIN32
+      _setmode(_fileno(stdin), _O_BINARY);
+#endif
+      fin=stdin;
+   }
+   else
+   {
+      fin = fopen_utf8(inFile, "rb");
+      if (!fin)
+      {
+         perror(inFile);
+         quit(1);
+      }
+      close_in=1;
+   }
+
+   /* .opus files use the Ogg container to provide framing and timekeeping.
+    * http://tools.ietf.org/html/draft-terriberry-oggopus
+    * The easiest way to decode the Ogg container is to use libogg, so
+    *  thats what we do here.
+    * Using libogg is fairly straight forward-- you take your stream of bytes
+    *  and feed them to ogg_sync_ and it periodically returns Ogg pages, you
+    *  check if the pages belong to the stream you're decoding then you give
+    *  them to libogg and it gives you packets. You decode the packets. The
+    *  pages also provide timing information.*/
+   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);
+         /*Extract all available packets*/
+         while (ogg_stream_packetout(&os, &op) == 1)
+         {
+            /*OggOpus streams are identified by a magic string in the initial
+              stream header.*/
+            if (op.b_o_s && op.bytes>=8 && !memcmp(op.packet, "OpusHead", 8)) {
+               if(has_opus_stream && has_tags_packet)
+               {
+                 /*If we're seeing another BOS OpusHead now it means
+                   the stream is chained without an EOS.*/
+                 has_opus_stream=0;
+                 if(st)opus_multistream_decoder_destroy(st);
+                 st=NULL;
+                 fprintf(stderr,"\nWarning: stream %" I64FORMAT " ended without EOS and a new stream began.\n",(long long)os.serialno);
+               }
+               if(!has_opus_stream)
+               {
+                 if(packet_count>0 && opus_serialno==os.serialno)
+                 {
+                   fprintf(stderr,"\nError: Apparent chaining without changing serial number (%" I64FORMAT "==%" I64FORMAT ").\n",
+                     (long long)opus_serialno,(long long)os.serialno);
+                   quit(1);
+                 }
+                 opus_serialno = os.serialno;
+                 has_opus_stream = 1;
+                 has_tags_packet = 0;
+                 link_out = 0;
+                 packet_count = 0;
+                 eos = 0;
+                 total_links++;
+               } else {
+                 fprintf(stderr,"\nWarning: ignoring opus stream %" I64FORMAT "\n",(long long)os.serialno);
+               }
+            }
+            if (!has_opus_stream || os.serialno != opus_serialno)
+               break;
+            /*If first packet in a logical stream, process the Opus header*/
+            if (packet_count==0)
+            {
+               st = process_header(&op, &rate, &mapping_family, &channels, &preskip, &gain, manual_gain, &streams, wav_format, quiet);
+               if (!st)
+                  quit(1);
+
+               if(ogg_stream_packetout(&os, &op)!=0 || og.header[og.header_len-1]==255)
+               {
+                  /*The format specifies that the initial header and tags packets are on their
+                    own pages. To aid implementors in discovering that their files are wrong
+                    we reject them explicitly here. In some player designs files like this would
+                    fail even without an explicit test.*/
+                  fprintf(stderr, "Extra packets on initial header page. Invalid stream.\n");
+                  quit(1);
+               }
+
+               /*Remember how many samples at the front we were told to skip
+                 so that we can adjust the timestamp counting.*/
+               gran_offset=preskip;
+
+               /*Setup the memory for the dithered output*/
+               if(!shapemem.a_buf)
+               {
+                  shapemem.a_buf=calloc(channels,sizeof(float)*4);
+                  shapemem.b_buf=calloc(channels,sizeof(float)*4);
+                  shapemem.fs=rate;
+               }
+               if(!output)output=malloc(sizeof(float)*MAX_FRAME_SIZE*channels);
+
+               /*Normal players should just play at 48000 or their maximum rate,
+                 as described in the OggOpus spec.  But for commandline tools
+                 like opusdec it can be desirable to exactly preserve the original
+                 sampling rate and duration, so we have a resampler here.*/
+               if (rate != 48000 && resampler==NULL)
+               {
+                  int err;
+                  resampler = speex_resampler_init(channels, 48000, rate, 5, &err);
+                  if (err!=0)
+                     fprintf(stderr, "resampler error: %s\n", speex_resampler_strerror(err));
+                  speex_resampler_skip_zeros(resampler);
+               }
+               if(!fout)fout=out_file_open(outFile, &wav_format, rate, mapping_family, &channels, fp);
+            } else if (packet_count==1)
+            {
+               if (!quiet)
+                  print_comments((char*)op.packet, op.bytes);
+               has_tags_packet=1;
+               if(ogg_stream_packetout(&os, &op)!=0 || og.header[og.header_len-1]==255)
+               {
+                  fprintf(stderr, "Extra packets on initial tags page. Invalid stream.\n");
+                  quit(1);
+               }
+            } else {
+               int ret;
+               opus_int64 maxout;
+               opus_int64 outsamp;
+               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)eos=1; /* don't care for anything except opus eos */
+
+               /*Are we simulating loss for this packet?*/
+               if (!lost){
+                  /*Decode Opus packet*/
+                  ret = opus_multistream_decode_float(st, (unsigned char*)op.packet, op.bytes, output, MAX_FRAME_SIZE, 0);
+               } else {
+                  /*Extract the original duration.
+                    Normally you wouldn't have it for a lost packet, but normally the
+                    transports used on lossy channels will effectively tell you.
+                    This avoids opusdec squaking when the decoded samples and
+                    granpos mismatches.*/
+                  opus_int32 lost_size;
+                  lost_size = MAX_FRAME_SIZE;
+                  if(op.bytes>0){
+                    opus_int32 spp;
+                    spp=opus_packet_get_nb_frames(op.packet,op.bytes);
+                    if(spp>0){
+                      spp*=opus_packet_get_samples_per_frame(op.packet,48000/*decoding_rate*/);
+                      if(spp>0)lost_size=spp;
+                    }
+                  }
+                  /*Invoke packet loss concealment.*/
+                  ret = opus_multistream_decode_float(st, NULL, 0, output, lost_size, 0);
+               }
+
+               if(!quiet){
+                  /*Display a progress spinner while decoding.*/
+                  static const char spinner[]="|/-\\";
+                  double coded_seconds = (double)audio_size/(channels*rate*(fp?4:2));
+                  if(coded_seconds>=last_coded_seconds+1){
+                     fprintf(stderr,"\r[%c] %02d:%02d:%02d", spinner[last_spin&3],
+                             (int)(coded_seconds/3600),(int)(coded_seconds/60)%60,
+                             (int)(coded_seconds)%60);
+                     fflush(stderr);
+                     last_spin++;
+                     last_coded_seconds=coded_seconds;
+                  }
+               }
+
+               /*If the decoder returned less than zero, we have an error.*/
+               if (ret<0)
+               {
+                  fprintf (stderr, "Decoding error: %s\n", opus_strerror(ret));
+                  break;
+               }
+               frame_size = ret;
+
+               /*If we're collecting --save-range debugging data, collect it now.*/
+               if(frange!=NULL){
+                 OpusDecoder *od;
+                 opus_uint32 rngs[256];
+                 for(i=0;i<streams;i++){
+                   ret=opus_multistream_decoder_ctl(st,OPUS_MULTISTREAM_GET_DECODER_STATE(i,&od));
+                   ret=opus_decoder_ctl(od,OPUS_GET_FINAL_RANGE(&rngs[i]));
+                 }
+                 save_range(frange,frame_size*(48000/48000/*decoding_rate*/),op.packet,op.bytes,
+                            rngs,streams);
+               }
+
+               /*Apply header gain, if we're not using an opus library new
+                 enough to do this internally.*/
+               if (gain!=0){
+                 for (i=0;i<frame_size*channels;i++)
+                    output[i] *= gain;
+               }
+
+               /*This handles making sure that our output duration respects
+                 the final end-trim by not letting the output sample count
+                 get ahead of the granpos indicated value.*/
+               maxout=((page_granule-gran_offset)*rate/48000)-link_out;
+               outsamp=audio_write(output, channels, frame_size, fout, resampler, &preskip, dither?&shapemem:0, strlen(outFile)!=0,0>maxout?0:maxout,fp);
+               link_out+=outsamp;
+               audio_size+=(fp?4:2)*outsamp*channels;
+            }
+            packet_count++;
+         }
+         /*We're done, drain the resampler if we were using it.*/
+         if(eos && resampler)
+         {
+            float *zeros;
+            int drain;
+
+            zeros=(float *)calloc(100*channels,sizeof(float));
+            drain = speex_resampler_get_input_latency(resampler);
+            do {
+               opus_int64 outsamp;
+               int tmp = drain;
+               if (tmp > 100)
+                  tmp = 100;
+               outsamp=audio_write(zeros, channels, tmp, fout, resampler, NULL, &shapemem, strlen(outFile)!=0, ((page_granule-gran_offset)*rate/48000)-link_out,fp);
+               link_out+=outsamp;
+               audio_size+=(fp?4:2)*outsamp*channels;
+               drain -= tmp;
+            } while (drain>0);
+            free(zeros);
+            speex_resampler_destroy(resampler);
+            resampler=NULL;
+         }
+         if(eos)
+         {
+            has_opus_stream=0;
+            if(st)opus_multistream_decoder_destroy(st);
+            st=NULL;
+         }
+      }
+      if (feof(fin)) {
+         if(!quiet) {
+           fprintf(stderr, "\rDecoding complete.        \n");
+           fflush(stderr);
+         }
+         break;
+      }
+   }
+
+   /*If we were writing wav, go set the duration.*/
+   if (strlen(outFile)!=0 && fout && wav_format>0 && audio_size<0x7FFFFFFF)
+   {
+      if (fseek(fout,4,SEEK_SET)==0)
+      {
+         int tmp;
+         tmp = le_int(audio_size+20+wav_format);
+         if(fwrite(&tmp,4,1,fout)!=1)fprintf(stderr,"Error writing end length.\n");
+         if (fseek(fout,16+wav_format,SEEK_CUR)==0)
+         {
+            tmp = le_int(audio_size);
+            if(fwrite(&tmp,4,1,fout)!=1)fprintf(stderr,"Error writing header length.\n");
+         } else
+         {
+            fprintf (stderr, "First seek worked, second didn't\n");
+         }
+      } else {
+         fprintf (stderr, "Cannot seek on wav file output, wav size chunk will be incorrect\n");
+      }
+   }
+
+   /*Did we make it to the end without recovering ANY opus logical streams?*/
+   if(!total_links)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(shapemem.a_buf)free(shapemem.a_buf);
+   if(shapemem.b_buf)free(shapemem.b_buf);
+
+   if(output)free(output);
+
+   if(frange)fclose(frange);
+
+   if (close_in)
+      fclose(fin);
+   if (fout != NULL)
+      fclose(fout);
+
+#ifdef WIN_UNICODE
+   free_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
+   uninit_console_utf8();
+#endif
+
+   return 0;
+}
diff --git a/src/opusdec.vcxproj b/src/opusdec.vcxproj
new file mode 100644
index 0000000..39640a3
--- /dev/null
+++ b/src/opusdec.vcxproj
@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\share\getopt.c" />
+    <ClCompile Include="..\share\getopt1.c" />
+    <ClCompile Include="audio-in.c" />
+    <ClCompile Include="diag_range.c" />
+    <ClCompile Include="flac.c" />
+    <ClCompile Include="lpc.c" />
+    <ClCompile Include="opusdec.c" />
+    <ClCompile Include="opus_header.c" />
+    <ClCompile Include="resample.c" />
+    <ClCompile Include="..\win32\unicode_support.c" />
+    <ClCompile Include="wave_out.c" />
+    <ClCompile Include="wav_io.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\win32\config.h" />
+    <ClInclude Include="arch.h" />
+    <ClInclude Include="diag_range.h" />
+    <ClInclude Include="flac.h" />
+    <ClInclude Include="info_opus.h" />
+    <ClInclude Include="lpc.h" />
+    <ClInclude Include="opusenc.h" />
+    <ClInclude Include="opusinfo.h" />
+    <ClInclude Include="opus_header.h" />
+    <ClInclude Include="os_support.h" />
+    <ClInclude Include="resample_sse.h" />
+    <ClInclude Include="speex_resampler.h" />
+    <ClInclude Include="stack_alloc.h" />
+    <ClInclude Include="..\win32\unicode_support.h" />
+    <ClInclude Include="wave_out.h" />
+    <ClInclude Include="wav_io.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <Keyword>Win32Proj</Keyword>
+    <ProjectName>opusdec</ProjectName>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Configuration)_opusdec\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Platform)\$(Configuration)_opusdec\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Configuration)_opusdec\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Platform)\$(Configuration)_opusdec\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <TargetMachine>MachineX86</TargetMachine>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;WIN64;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+      <FloatingPointModel>Fast</FloatingPointModel>
+    </ClCompile>
+    <Link>
+      <TargetMachine>MachineX86</TargetMachine>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;WIN64;NDEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+      <FloatingPointModel>Fast</FloatingPointModel>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
diff --git a/src/opusdec.vcxproj.filters b/src/opusdec.vcxproj.filters
new file mode 100644
index 0000000..9d62fac
--- /dev/null
+++ b/src/opusdec.vcxproj.filters
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{24918d8b-7cfd-42bd-a0b3-cd6e23c146a5}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{8c349b04-68c5-46dc-a94d-0bf5c1511206}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="opusdec.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="resample.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="wav_io.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="wave_out.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="audio-in.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="diag_range.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="lpc.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="opus_header.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\share\getopt.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\share\getopt1.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\win32\unicode_support.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="flac.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="lpc.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="opus_header.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="opusenc.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="opusinfo.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="os_support.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="speex_resampler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="stack_alloc.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="wav_io.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="wave_out.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="arch.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="diag_range.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="info_opus.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="resample_sse.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\win32\config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\win32\unicode_support.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="flac.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/opusenc.c b/src/opusenc.c
new file mode 100644
index 0000000..366893b
--- /dev/null
+++ b/src/opusenc.c
@@ -0,0 +1,1183 @@
+/* Copyright (C)2002-2011 Jean-Marc Valin
+   Copyright (C)2007-2013 Xiph.Org Foundation
+   Copyright (C)2008-2013 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>
+#if (!defined WIN32 && !defined _WIN32) || defined(__MINGW32__)
+#include <unistd.h>
+#include <time.h>
+#endif
+#include <math.h>
+
+#ifdef _MSC_VER
+#define snprintf _snprintf
+#endif
+
+#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
+# include "unicode_support.h"
+/* We need the following two to set stdout to binary */
+# include <io.h>
+# include <fcntl.h>
+# define I64FORMAT "I64d"
+#else
+# define I64FORMAT "lld"
+# define fopen_utf8(_x,_y) fopen((_x),(_y))
+# define argc_utf8 argc
+# define argv_utf8 argv
+#endif
+
+#include <opus.h>
+#include <opus_multistream.h>
+#include <ogg/ogg.h>
+#include "wav_io.h"
+
+#include "picture.h"
+#include "opus_header.h"
+#include "opusenc.h"
+#include "diag_range.h"
+#include "cpusupport.h"
+
+#ifdef VALGRIND
+#include <valgrind/memcheck.h>
+#define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y))
+#define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y))
+#else
+#define VG_UNDEF(x,y)
+#define VG_CHECK(x,y)
+#endif
+
+static void comment_init(char **comments, int* length, const char *vendor_string);
+static void comment_pad(char **comments, int* length, int amount);
+
+/*Write an Ogg page to a file pointer*/
+static inline int oe_write_page(ogg_page *page, FILE *fp)
+{
+   int written;
+   written=fwrite(page->header,1,page->header_len, fp);
+   written+=fwrite(page->body,1,page->body_len, fp);
+   return written;
+}
+
+#define MAX_FRAME_BYTES 61295
+#define IMIN(a,b) ((a) < (b) ? (a) : (b))   /**< Minimum int value.   */
+#define IMAX(a,b) ((a) > (b) ? (a) : (b))   /**< Maximum int value.   */
+
+void opustoolsversion(const char *opusversion)
+{
+  printf("opusenc %s %s (using %s)\n",PACKAGE_NAME,PACKAGE_VERSION,opusversion);
+  printf("Copyright (C) 2008-2013 Xiph.Org Foundation\n");
+}
+
+void opustoolsversion_short(const char *opusversion)
+{
+  opustoolsversion(opusversion);
+}
+
+void usage(void)
+{
+  printf("Usage: opusenc [options] input_file output_file.opus\n");
+  printf("\n");
+  printf("Encodes input_file using Opus.\n");
+#if defined(HAVE_LIBFLAC)
+  printf("It can read the WAV, AIFF, FLAC, Ogg/FLAC, or raw files.\n");
+#else
+  printf("It can read the WAV, AIFF, or raw files.\n");
+#endif
+  printf("\nGeneral options:\n");
+  printf(" -h, --help         This help\n");
+  printf(" -V, --version      Version information\n");
+  printf(" --quiet            Quiet mode\n");
+  printf("\n");
+  printf("input_file can be:\n");
+  printf("  filename.wav      file\n");
+  printf("  -                 stdin\n");
+  printf("\n");
+  printf("output_file can be:\n");
+  printf("  filename.opus     compressed file\n");
+  printf("  -                 stdout\n");
+  printf("\nEncoding options:\n");
+  printf(" --bitrate n.nnn    Target bitrate in kbit/sec (6-256/channel)\n");
+  printf(" --vbr              Use variable bitrate encoding (default)\n");
+  printf(" --cvbr             Use constrained variable bitrate encoding\n");
+  printf(" --hard-cbr         Use hard constant bitrate encoding\n");
+  printf(" --comp n           Encoding complexity (0-10, default: 10 (slowest))\n");
+  printf(" --framesize n      Maximum frame size in milliseconds\n");
+  printf("                      (2.5, 5, 10, 20, 40, 60, default: 20)\n");
+  printf(" --expect-loss      Percentage packet loss to expect (default: 0)\n");
+  printf(" --downmix-mono     Downmix to mono\n");
+  printf(" --downmix-stereo   Downmix to stereo (if >2 channels)\n");
+  printf(" --max-delay n      Maximum container delay in milliseconds\n");
+  printf("                      (0-1000, default: 1000)\n");
+  printf("\nDiagnostic options:\n");
+  printf(" --serial n         Forces a specific stream serial number\n");
+  printf(" --save-range file  Saves check values for every frame to a file\n");
+  printf(" --set-ctl-int x=y  Pass the encoder control x with value y (advanced)\n");
+  printf("                      Preface with s: to direct the ctl to multistream s\n");
+  printf("                      This may be used multiple times\n");
+  printf("\nMetadata options:\n");
+  printf(" --comment          Add the given string as an extra comment\n");
+  printf("                      This may be used multiple times\n");
+  printf(" --artist           Author of this track\n");
+  printf(" --title            Title for this track\n");
+  printf(" --album            Album or collection this track belongs to\n");
+  printf(" --date             Date for this track\n");
+  printf(" --genre            Genre for this track\n");
+  printf(" --picture          Album art for this track\n");
+  printf("                      More than one --picture option can be specified.\n");
+  printf("                      Either a FILENAME for the picture file or a more\n");
+  printf("                      complete SPECIFICATION form can be used. The\n");
+  printf("                      SPECIFICATION is a string whose parts are\n");
+  printf("                      separated by | (pipe) characters. Some parts may\n");
+  printf("                      be left empty to invoke default values. A\n");
+  printf("                      FILENAME is just shorthand for \"||||FILENAME\".\n");
+  printf("                      The format of SPECIFICATION is\n");
+  printf("\n");
+  printf("                      [TYPE]|[MIME-TYPE]|[DESCRIPTION]|[WIDTHxHEIGHT\n");
+  printf("                      xDEPTH[/COLORS]]|FILENAME\n");
+  printf("\n");
+  printf("                      TYPE is an optional number from one of:\n");
+  printf("                      0: Other\n");
+  printf("                      1: 32x32 pixel 'file icon' (PNG only)\n");
+  printf("                      2: Other file icon\n");
+  printf("                      3: Cover (front)\n");
+  printf("                      4: Cover (back)\n");
+  printf("                      5: Leaflet page\n");
+  printf("                      6: Media (e.g., label side of a CD)\n");
+  printf("                      7: Lead artist/lead performer/soloist\n");
+  printf("                      8: Artist/performer\n");
+  printf("                      9: Conductor\n");
+  printf("                      10: Band/Orchestra\n");
+  printf("                      11: Composer\n");
+  printf("                      12: Lyricist/text writer\n");
+  printf("                      13: Recording location\n");
+  printf("                      14: During recording\n");
+  printf("                      15: During performance\n");
+  printf("                      16: Movie/video screen capture\n");
+  printf("                      17: A bright colored fish\n");
+  printf("                      18: Illustration\n");
+  printf("                      19: Band/artist logotype\n");
+  printf("                      20: Publisher/studio logotype\n");
+  printf("\n");
+  printf("                      The default is 3 (front cover). There may only be\n");
+  printf("                      one picture each of type 1 and 2 in a file.\n");
+  printf("\n");
+  printf("                      MIME-TYPE is optional. If left blank, it will be\n");
+  printf("                      detected from the file. For best compatibility\n");
+  printf("                      with players, use pictures with a MIME-TYPE of\n");
+  printf("                      image/jpeg or image/png. The MIME-TYPE can also\n");
+  printf("                      be --> to mean that FILENAME is actually a URL to\n");
+  printf("                      an image, though this use is discouraged. The\n");
+  printf("                      file at the URL will not be fetched. The URL\n");
+  printf("                      itself is stored in the metadata.\n");
+  printf("\n");
+  printf("                      DESCRIPTION is optional. The default is an empty\n");
+  printf("                      string.\n");
+  printf("\n");
+  printf("                      The next part specifies the resolution and color\n");
+  printf("                      information. If the MIME-TYPE is image/jpeg,\n");
+  printf("                      image/png, or image/gif, you can usually leave\n");
+  printf("                      this empty and they can be detected from the\n");
+  printf("                      file. Otherwise, you must specify the width in\n");
+  printf("                      pixels, height in pixels, and color depth in\n");
+  printf("                      bits-per-pixel. If the image has indexed colors\n");
+  printf("                      you should also specify the number of colors\n");
+  printf("                      used. If possible, these are checked against the\n");
+  printf("                      file for accuracy.\n");
+  printf("\n");
+  printf("                      FILENAME is the path to the picture file to be\n");
+  printf("                      imported, or the URL if the MIME-TYPE is -->.\n");
+  printf(" --padding n        Extra bytes to reserve for metadata (default: 512)\n");
+  printf(" --discard-comments Don't keep metadata when transcoding\n");
+  printf(" --discard-pictures Don't keep pictures when transcoding\n");
+  printf("\nInput options:\n");
+  printf(" --raw              Raw input\n");
+  printf(" --raw-bits n       Set bits/sample for raw input (default: 16)\n");
+  printf(" --raw-rate n       Set sampling rate for raw input (default: 48000)\n");
+  printf(" --raw-chan n       Set number of channels for raw input (default: 2)\n");
+  printf(" --raw-endianness n 1 for bigendian, 0 for little (defaults to 0)\n");
+  printf(" --ignorelength     Always ignore the datalength in Wave headers\n");
+}
+
+static inline void print_time(double seconds)
+{
+  opus_int64 hours, minutes;
+  hours=seconds/3600;
+  seconds-=hours*3600.;
+  minutes=seconds/60;
+  seconds-=minutes*60.;
+  if(hours)fprintf(stderr," %" I64FORMAT " hour%s%s",hours,hours>1?"s":"",
+                   minutes&&!(seconds>0)?" and":"");
+  if(minutes)fprintf(stderr,"%s%" I64FORMAT " minute%s%s",hours?", ":" ",minutes,
+                     minutes>1?"s":"",!hours&&seconds>0?" and":seconds>0?", and":"");
+  if(seconds>0)fprintf(stderr," %0.4g second%s",seconds,seconds!=1?"s":"");
+}
+
+int main(int argc, char **argv)
+{
+  static const input_format raw_format = {NULL, 0, raw_open, wav_close, "raw",N_("RAW file reader")};
+  int option_index=0;
+  struct option long_options[] =
+  {
+    {"quiet", no_argument, NULL, 0},
+    {"bitrate", required_argument, NULL, 0},
+    {"hard-cbr",no_argument,NULL, 0},
+    {"vbr",no_argument,NULL, 0},
+    {"cvbr",no_argument,NULL, 0},
+    {"comp", required_argument, NULL, 0},
+    {"complexity", required_argument, NULL, 0},
+    {"framesize", required_argument, NULL, 0},
+    {"expect-loss", required_argument, NULL, 0},
+    {"downmix-mono",no_argument,NULL, 0},
+    {"downmix-stereo",no_argument,NULL, 0},
+    {"no-downmix",no_argument,NULL, 0},
+    {"max-delay", required_argument, NULL, 0},
+    {"serial", required_argument, NULL, 0},
+    {"save-range", required_argument, NULL, 0},
+    {"set-ctl-int", required_argument, NULL, 0},
+    {"help", no_argument, NULL, 0},
+    {"raw", no_argument, NULL, 0},
+    {"raw-bits", required_argument, NULL, 0},
+    {"raw-rate", required_argument, NULL, 0},
+    {"raw-chan", required_argument, NULL, 0},
+    {"raw-endianness", required_argument, NULL, 0},
+    {"ignorelength", 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},
+    {"artist", required_argument, NULL, 0},
+    {"title", required_argument, NULL, 0},
+    {"album", required_argument, NULL, 0},
+    {"date", required_argument, NULL, 0},
+    {"genre", required_argument, NULL, 0},
+    {"picture", required_argument, NULL, 0},
+    {"padding", required_argument, NULL, 0},
+    {"discard-comments", no_argument, NULL, 0},
+    {"discard-pictures", no_argument, NULL, 0},
+    {0, 0, 0, 0}
+  };
+  int i, ret;
+  int                cline_size;
+  OpusMSEncoder      *st;
+  const char         *opus_version;
+  unsigned char      *packet;
+  float              *input;
+  /*I/O*/
+  oe_enc_opt         inopt;
+  const input_format *in_format;
+  char               *inFile;
+  char               *outFile;
+  char               *range_file;
+  FILE               *fin;
+  FILE               *fout;
+  FILE               *frange;
+  ogg_stream_state   os;
+  ogg_page           og;
+  ogg_packet         op;
+  ogg_int64_t        last_granulepos=0;
+  ogg_int64_t        enc_granulepos=0;
+  ogg_int64_t        original_samples=0;
+  ogg_int32_t        id=-1;
+  int                last_segments=0;
+  int                eos=0;
+  OpusHeader         header;
+  char               ENCODER_string[1024];
+  /*Counters*/
+  opus_int64         nb_encoded=0;
+  opus_int64         bytes_written=0;
+  opus_int64         pages_out=0;
+  opus_int64         total_bytes=0;
+  opus_int64         total_samples=0;
+  opus_int32         nbBytes;
+  opus_int32         nb_samples;
+  opus_int32         peak_bytes=0;
+  opus_int32         min_bytes;
+  time_t             start_time;
+  time_t             stop_time;
+  time_t             last_spin=0;
+  int                last_spin_len=0;
+  /*Settings*/
+  int                quiet=0;
+  int                max_frame_bytes;
+  opus_int32         bitrate=-1;
+  opus_int32         rate=48000;
+  opus_int32         coding_rate=48000;
+  opus_int32         frame_size=960;
+  int                chan=2;
+  int                with_hard_cbr=0;
+  int                with_cvbr=0;
+  int                expect_loss=0;
+  int                complexity=10;
+  int                downmix=0;
+  int                *opt_ctls_ctlval;
+  int                opt_ctls=0;
+  int                max_ogg_delay=48000; /*48kHz samples*/
+  int                seen_file_icons=0;
+  int                comment_padding=512;
+  int                serialno;
+  opus_int32         lookahead=0;
+#ifdef WIN_UNICODE
+   int argc_utf8;
+   char **argv_utf8;
+#endif
+
+   if(query_cpu_support()){
+     fprintf(stderr,"\n\n** WARNING: This program was compiled with SSE%s\n",query_cpu_support()>1?"2":"");
+     fprintf(stderr,"            but this CPU claims to lack these instructions. **\n\n");
+   }
+
+#ifdef WIN_UNICODE
+   (void)argc;
+   (void)argv;
+
+   init_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
+#endif
+
+  opt_ctls_ctlval=NULL;
+  frange=NULL;
+  range_file=NULL;
+  in_format=NULL;
+  inopt.channels=chan;
+  inopt.rate=coding_rate=rate;
+  /* 0 dB gain is recommended unless you know what you're doing */
+  inopt.gain=0;
+  inopt.samplesize=16;
+  inopt.endianness=0;
+  inopt.rawmode=0;
+  inopt.ignorelength=0;
+  inopt.copy_comments=1;
+  inopt.copy_pictures=1;
+
+  start_time = time(NULL);
+  srand(((getpid()&65535)<<15)^start_time);
+  serialno=rand();
+
+  opus_version=opus_get_version_string();
+  /*Vendor string should just be the encoder library,
+    the ENCODER comment specifies the tool used.*/
+  comment_init(&inopt.comments, &inopt.comments_length, opus_version);
+  snprintf(ENCODER_string, sizeof(ENCODER_string), "opusenc from %s %s",PACKAGE_NAME,PACKAGE_VERSION);
+  comment_add(&inopt.comments, &inopt.comments_length, "ENCODER", ENCODER_string);
+
+  /*Process command-line options*/
+  cline_size=0;
+  while(1){
+    int c;
+    int save_cmd=1;
+    c=getopt_long(argc_utf8, argv_utf8, "hV",
+                  long_options, &option_index);
+    if(c==-1)
+       break;
+
+    switch(c){
+      case 0:
+        if(strcmp(long_options[option_index].name,"quiet")==0){
+          quiet=1;
+        }else if(strcmp(long_options[option_index].name,"bitrate")==0){
+          bitrate=atof(optarg)*1000.;
+        }else if(strcmp(long_options[option_index].name,"hard-cbr")==0){
+          with_hard_cbr=1;
+          with_cvbr=0;
+        }else if(strcmp(long_options[option_index].name,"cvbr")==0){
+          with_cvbr=1;
+          with_hard_cbr=0;
+        }else if(strcmp(long_options[option_index].name,"vbr")==0){
+          with_cvbr=0;
+          with_hard_cbr=0;
+        }else if(strcmp(long_options[option_index].name,"help")==0){
+          usage();
+          exit(0);
+        }else if(strcmp(long_options[option_index].name,"version")==0){
+          opustoolsversion(opus_version);
+          exit(0);
+        }else if(strcmp(long_options[option_index].name,"version-short")==0){
+          opustoolsversion_short(opus_version);
+          exit(0);
+        }else if(strcmp(long_options[option_index].name,"ignorelength")==0){
+          inopt.ignorelength=1;
+        }else if(strcmp(long_options[option_index].name,"raw")==0){
+          inopt.rawmode=1;
+          save_cmd=0;
+        }else if(strcmp(long_options[option_index].name,"raw-bits")==0){
+          inopt.rawmode=1;
+          inopt.samplesize=atoi(optarg);
+          save_cmd=0;
+          if(inopt.samplesize!=8&&inopt.samplesize!=16&&inopt.samplesize!=24){
+            fprintf(stderr,"Invalid bit-depth: %s\n",optarg);
+            fprintf(stderr,"--raw-bits must be one of 8,16, or 24\n");
+            exit(1);
+          }
+        }else if(strcmp(long_options[option_index].name,"raw-rate")==0){
+          inopt.rawmode=1;
+          inopt.rate=atoi(optarg);
+          save_cmd=0;
+        }else if(strcmp(long_options[option_index].name,"raw-chan")==0){
+          inopt.rawmode=1;
+          inopt.channels=atoi(optarg);
+          save_cmd=0;
+        }else if(strcmp(long_options[option_index].name,"raw-endianness")==0){
+          inopt.rawmode=1;
+          inopt.endianness=atoi(optarg);
+          save_cmd=0;
+        }else if(strcmp(long_options[option_index].name,"downmix-mono")==0){
+          downmix=1;
+        }else if(strcmp(long_options[option_index].name,"downmix-stereo")==0){
+          downmix=2;
+        }else if(strcmp(long_options[option_index].name,"no-downmix")==0){
+          downmix=-1;
+        }else if(strcmp(long_options[option_index].name,"expect-loss")==0){
+          expect_loss=atoi(optarg);
+          if(expect_loss>100||expect_loss<0){
+            fprintf(stderr,"Invalid expect-loss: %s\n",optarg);
+            fprintf(stderr,"Expected loss is a percent and must be 0-100.\n");
+            exit(1);
+          }
+        }else if(strcmp(long_options[option_index].name,"comp")==0 ||
+                 strcmp(long_options[option_index].name,"complexity")==0){
+          complexity=atoi(optarg);
+          if(complexity>10||complexity<0){
+            fprintf(stderr,"Invalid complexity: %s\n",optarg);
+            fprintf(stderr,"Complexity must be 0-10.\n");
+            exit(1);
+          }
+        }else if(strcmp(long_options[option_index].name,"framesize")==0){
+          if(strcmp(optarg,"2.5")==0)frame_size=120;
+          else if(strcmp(optarg,"5")==0)frame_size=240;
+          else if(strcmp(optarg,"10")==0)frame_size=480;
+          else if(strcmp(optarg,"20")==0)frame_size=960;
+          else if(strcmp(optarg,"40")==0)frame_size=1920;
+          else if(strcmp(optarg,"60")==0)frame_size=2880;
+          else{
+            fprintf(stderr,"Invalid framesize: %s\n",optarg);
+            fprintf(stderr,"Framesize must be 2.5, 5, 10, 20, 40, or 60.\n");
+            exit(1);
+          }
+        }else if(strcmp(long_options[option_index].name,"max-delay")==0){
+          max_ogg_delay=floor(atof(optarg)*48.);
+          if(max_ogg_delay<0||max_ogg_delay>48000){
+            fprintf(stderr,"Invalid max-delay: %s\n",optarg);
+            fprintf(stderr,"max-delay 0-1000 ms.\n");
+            exit(1);
+          }
+        }else if(strcmp(long_options[option_index].name,"serial")==0){
+          serialno=atoi(optarg);
+        }else if(strcmp(long_options[option_index].name,"set-ctl-int")==0){
+          int len=strlen(optarg),target;
+          char *spos,*tpos;
+          spos=strchr(optarg,'=');
+          if(len<3||spos==NULL||(spos-optarg)<1||(spos-optarg)>=len){
+            fprintf(stderr, "Invalid set-ctl-int: %s\n", optarg);
+            fprintf(stderr, "Syntax is --set-ctl-int intX=intY or\n");
+            fprintf(stderr, "Syntax is --set-ctl-int intS:intX=intY\n");
+            exit(1);
+          }
+          tpos=strchr(optarg,':');
+          if(tpos==NULL){
+            target=-1;
+            tpos=optarg-1;
+          }else target=atoi(optarg);
+          if((atoi(tpos+1)&1)!=0){
+            fprintf(stderr, "Invalid set-ctl-int: %s\n", optarg);
+            fprintf(stderr, "libopus set CTL values are even.\n");
+            exit(1);
+          }
+          if(opt_ctls==0)opt_ctls_ctlval=malloc(sizeof(int)*3);
+          else opt_ctls_ctlval=realloc(opt_ctls_ctlval,sizeof(int)*(opt_ctls+1)*3);
+          opt_ctls_ctlval[opt_ctls*3]=target;
+          opt_ctls_ctlval[opt_ctls*3+1]=atoi(tpos+1);
+          opt_ctls_ctlval[opt_ctls*3+2]=atoi(spos+1);
+          opt_ctls++;
+        }else if(strcmp(long_options[option_index].name,"save-range")==0){
+          frange=fopen_utf8(optarg,"w");
+          save_cmd=0;
+          if(frange==NULL){
+            perror(optarg);
+            fprintf(stderr,"Could not open save-range file: %s\n",optarg);
+            fprintf(stderr,"Must provide a writable file name.\n");
+            exit(1);
+          }
+          range_file=optarg;
+        }else if(strcmp(long_options[option_index].name,"comment")==0){
+          save_cmd=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(&inopt.comments, &inopt.comments_length, NULL, optarg);
+        }else if(strcmp(long_options[option_index].name,"artist")==0){
+          save_cmd=0;
+          comment_add(&inopt.comments, &inopt.comments_length, "artist", optarg);
+        } else if(strcmp(long_options[option_index].name,"title")==0){
+          save_cmd=0;
+          comment_add(&inopt.comments, &inopt.comments_length, "title", optarg);
+        } else if(strcmp(long_options[option_index].name,"album")==0){
+          save_cmd=0;
+          comment_add(&inopt.comments, &inopt.comments_length, "album", optarg);
+        } else if(strcmp(long_options[option_index].name,"date")==0){
+          save_cmd=0;
+          comment_add(&inopt.comments, &inopt.comments_length, "date", optarg);
+        } else if(strcmp(long_options[option_index].name,"genre")==0){
+          save_cmd=0;
+          comment_add(&inopt.comments, &inopt.comments_length, "genre", optarg);
+        } else if(strcmp(long_options[option_index].name,"picture")==0){
+          const char *error_message;
+          char       *picture_data;
+          save_cmd=0;
+          picture_data=parse_picture_specification(optarg,&error_message,
+                                                   &seen_file_icons);
+          if(picture_data==NULL){
+            fprintf(stderr,"Error parsing picture option: %s\n",error_message);
+            exit(1);
+          }
+          comment_add(&inopt.comments,&inopt.comments_length,
+                      "METADATA_BLOCK_PICTURE",picture_data);
+          free(picture_data);
+        } else if(strcmp(long_options[option_index].name,"padding")==0){
+          comment_padding=atoi(optarg);
+        } else if(strcmp(long_options[option_index].name,"discard-comments")==0){
+          inopt.copy_comments=0;
+          inopt.copy_pictures=0;
+        } else if(strcmp(long_options[option_index].name,"discard-pictures")==0){
+          inopt.copy_pictures=0;
+        }
+        /*Commands whos arguments would leak file paths or just end up as metadata
+           should have save_cmd=0; to prevent them from being saved in the
+           command-line tag.*/
+        break;
+      case 'h':
+        usage();
+        exit(0);
+        break;
+      case 'V':
+        opustoolsversion(opus_version);
+        exit(0);
+        break;
+      case '?':
+        usage();
+        exit(1);
+        break;
+    }
+    if(save_cmd && cline_size<(int)sizeof(ENCODER_string)){
+      ret=snprintf(&ENCODER_string[cline_size], sizeof(ENCODER_string)-cline_size, "%s--%s",cline_size==0?"":" ",long_options[option_index].name);
+      if(ret<0||ret>=((int)sizeof(ENCODER_string)-cline_size)){
+        cline_size=sizeof(ENCODER_string);
+      } else {
+        cline_size+=ret;
+        if(optarg){
+          ret=snprintf(&ENCODER_string[cline_size], sizeof(ENCODER_string)-cline_size, " %s",optarg);
+          if(ret<0||ret>=((int)sizeof(ENCODER_string)-cline_size)){
+            cline_size=sizeof(ENCODER_string);
+          } else {
+            cline_size+=ret;
+          }
+        }
+      }
+    }
+  }
+  if(argc_utf8-optind!=2){
+    usage();
+    exit(1);
+  }
+  inFile=argv_utf8[optind];
+  outFile=argv_utf8[optind+1];
+
+  if(cline_size>0)comment_add(&inopt.comments, &inopt.comments_length, "ENCODER_OPTIONS", ENCODER_string);
+
+  if(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_utf8(inFile, "rb");
+    if(!fin){
+      perror(inFile);
+      exit(1);
+    }
+  }
+
+  if(inopt.rawmode){
+    in_format = &raw_format;
+    in_format->open_func(fin, &inopt, NULL, 0);
+  }else in_format=open_audio_file(fin,&inopt);
+
+  if(!in_format){
+    fprintf(stderr,"Error parsing input file: %s\n",inFile);
+    exit(1);
+  }
+
+  if(downmix==0&&inopt.channels>2&&bitrate>0&&bitrate<(16000*inopt.channels)){
+    if(!quiet)fprintf(stderr,"Notice: Surround bitrate less than 16kbit/sec/channel, downmixing.\n");
+    downmix=inopt.channels>8?1:2;
+  }
+
+  if(downmix>0&&downmix<inopt.channels)downmix=setup_downmix(&inopt,downmix);
+  else downmix=0;
+
+  rate=inopt.rate;
+  chan=inopt.channels;
+  inopt.skip=0;
+
+  /*In order to code the complete length we'll need to do a little padding*/
+  setup_padder(&inopt,&original_samples);
+
+  if(rate>24000)coding_rate=48000;
+  else if(rate>16000)coding_rate=24000;
+  else if(rate>12000)coding_rate=16000;
+  else if(rate>8000)coding_rate=12000;
+  else coding_rate=8000;
+
+  frame_size=frame_size/(48000/coding_rate);
+
+  /*Scale the resampler complexity, but only for 48000 output because
+    the near-cutoff behavior matters a lot more at lower rates.*/
+  if(rate!=coding_rate)setup_resample(&inopt,coding_rate==48000?(complexity+1)/2:5,coding_rate);
+
+  if(rate!=coding_rate&&complexity!=10&&!quiet){
+    fprintf(stderr,"Notice: Using resampling with complexity<10.\n");
+    fprintf(stderr,"Opusenc is fastest with 48, 24, 16, 12, or 8kHz input.\n\n");
+  }
+
+  /*OggOpus headers*/ /*FIXME: broke forcemono*/
+  header.channels=chan;
+  header.channel_mapping=header.channels>8?255:chan>2;
+  header.input_sample_rate=rate;
+  header.gain=inopt.gain;
+
+  /*Initialize OPUS encoder*/
+  /*Framesizes <10ms can only use the MDCT modes, so we switch on RESTRICTED_LOWDELAY
+    to save the extra 2.5ms of codec lookahead when we'll be using only small frames.*/
+  st=opus_multistream_surround_encoder_create(coding_rate, chan, header.channel_mapping, &header.nb_streams, &header.nb_coupled,
+     header.stream_map, frame_size<480/(48000/coding_rate)?OPUS_APPLICATION_RESTRICTED_LOWDELAY:OPUS_APPLICATION_AUDIO, &ret);
+  if(ret!=OPUS_OK){
+    fprintf(stderr, "Error cannot create encoder: %s\n", opus_strerror(ret));
+    exit(1);
+  }
+
+  min_bytes=max_frame_bytes=(1275*3+7)*header.nb_streams;
+  packet=malloc(sizeof(unsigned char)*max_frame_bytes);
+  if(packet==NULL){
+    fprintf(stderr,"Error allocating packet buffer.\n");
+    exit(1);
+  }
+
+  if(bitrate<0){
+    /*Lower default rate for sampling rates [8000-44100) by a factor of (rate+16k)/(64k)*/
+    bitrate=((64000*header.nb_streams+32000*header.nb_coupled)*
+             (IMIN(48,IMAX(8,((rate<44100?rate:48000)+1000)/1000))+16)+32)>>6;
+  }
+
+  if(bitrate>(1024000*chan)||bitrate<500){
+    fprintf(stderr,"Error: Bitrate %d bits/sec is insane.\nDid you mistake bits for kilobits?\n",bitrate);
+    fprintf(stderr,"--bitrate values from 6-256 kbit/sec per channel are meaningful.\n");
+    exit(1);
+  }
+  bitrate=IMIN(chan*256000,bitrate);
+
+  ret=opus_multistream_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"Error OPUS_SET_BITRATE returned: %s\n",opus_strerror(ret));
+    exit(1);
+  }
+
+  ret=opus_multistream_encoder_ctl(st, OPUS_SET_VBR(!with_hard_cbr));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"Error OPUS_SET_VBR returned: %s\n",opus_strerror(ret));
+    exit(1);
+  }
+
+  if(!with_hard_cbr){
+    ret=opus_multistream_encoder_ctl(st, OPUS_SET_VBR_CONSTRAINT(with_cvbr));
+    if(ret!=OPUS_OK){
+      fprintf(stderr,"Error OPUS_SET_VBR_CONSTRAINT returned: %s\n",opus_strerror(ret));
+      exit(1);
+    }
+  }
+
+  ret=opus_multistream_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"Error OPUS_SET_COMPLEXITY returned: %s\n",opus_strerror(ret));
+    exit(1);
+  }
+
+  ret=opus_multistream_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(expect_loss));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"Error OPUS_SET_PACKET_LOSS_PERC returned: %s\n",opus_strerror(ret));
+    exit(1);
+  }
+
+#ifdef OPUS_SET_LSB_DEPTH
+  ret=opus_multistream_encoder_ctl(st, OPUS_SET_LSB_DEPTH(IMAX(8,IMIN(24,inopt.samplesize))));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"Warning OPUS_SET_LSB_DEPTH returned: %s\n",opus_strerror(ret));
+  }
+#endif
+
+  /*This should be the last set of CTLs, except the lookahead get, so it can override the defaults.*/
+  for(i=0;i<opt_ctls;i++){
+    int target=opt_ctls_ctlval[i*3];
+    if(target==-1){
+      ret=opus_multistream_encoder_ctl(st,opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2]);
+      if(ret!=OPUS_OK){
+        fprintf(stderr,"Error opus_multistream_encoder_ctl(st,%d,%d) returned: %s\n",opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2],opus_strerror(ret));
+        exit(1);
+      }
+    }else if(target<header.nb_streams){
+      OpusEncoder *oe;
+      opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(i,&oe));
+      ret=opus_encoder_ctl(oe, opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2]);
+      if(ret!=OPUS_OK){
+        fprintf(stderr,"Error opus_encoder_ctl(st[%d],%d,%d) returned: %s\n",target,opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2],opus_strerror(ret));
+        exit(1);
+      }
+    }else{
+      fprintf(stderr,"Error --set-ctl-int target stream %d is higher than the maximum stream number %d.\n",target,header.nb_streams-1);
+      exit(1);
+    }
+  }
+
+  /*We do the lookahead check late so user CTLs can change it*/
+  ret=opus_multistream_encoder_ctl(st, OPUS_GET_LOOKAHEAD(&lookahead));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"Error OPUS_GET_LOOKAHEAD returned: %s\n",opus_strerror(ret));
+    exit(1);
+  }
+  inopt.skip+=lookahead;
+  /*Regardless of the rate we're coding at the ogg timestamping/skip is
+    always timed at 48000.*/
+  header.preskip=inopt.skip*(48000./coding_rate);
+  /* Extra samples that need to be read to compensate for the pre-skip */
+  inopt.extraout=(int)header.preskip*(rate/48000.);
+
+  if(!quiet){
+    int opus_app;
+    fprintf(stderr,"Encoding using %s",opus_version);
+    opus_multistream_encoder_ctl(st,OPUS_GET_APPLICATION(&opus_app));
+    if(opus_app==OPUS_APPLICATION_VOIP)fprintf(stderr," (VoIP)\n");
+    else if(opus_app==OPUS_APPLICATION_AUDIO)fprintf(stderr," (audio)\n");
+    else if(opus_app==OPUS_APPLICATION_RESTRICTED_LOWDELAY)fprintf(stderr," (low-delay)\n");
+    else fprintf(stderr," (unknown)\n");
+    fprintf(stderr,"-----------------------------------------------------\n");
+    fprintf(stderr,"   Input: %0.6gkHz %d channel%s\n",
+            header.input_sample_rate/1000.,chan,chan<2?"":"s");
+    fprintf(stderr,"  Output: %d channel%s (",header.channels,header.channels<2?"":"s");
+    if(header.nb_coupled>0)fprintf(stderr,"%d coupled",header.nb_coupled*2);
+    if(header.nb_streams-header.nb_coupled>0)fprintf(stderr,
+       "%s%d uncoupled",header.nb_coupled>0?", ":"",
+       header.nb_streams-header.nb_coupled);
+    fprintf(stderr,")\n          %0.2gms packets, %0.6gkbit/sec%s\n",
+       frame_size/(coding_rate/1000.), bitrate/1000.,
+       with_hard_cbr?" CBR":with_cvbr?" CVBR":" VBR");
+    fprintf(stderr," Preskip: %d\n",header.preskip);
+
+    if(frange!=NULL)fprintf(stderr,"         Writing final range file %s\n",range_file);
+    fprintf(stderr,"\n");
+  }
+
+  if(strcmp(outFile,"-")==0){
+#if defined WIN32 || defined _WIN32
+    _setmode(_fileno(stdout), _O_BINARY);
+#endif
+    fout=stdout;
+  }else{
+    fout=fopen_utf8(outFile, "wb");
+    if(!fout){
+      perror(outFile);
+      exit(1);
+    }
+  }
+
+  /*Initialize Ogg stream struct*/
+  if(ogg_stream_init(&os, serialno)==-1){
+    fprintf(stderr,"Error: stream init failed\n");
+    exit(1);
+  }
+
+  /*Write header*/
+  {
+    unsigned char header_data[100];
+    int packet_size=opus_header_to_packet(&header, header_data, 100);
+    op.packet=header_data;
+    op.bytes=packet_size;
+    op.b_o_s=1;
+    op.e_o_s=0;
+    op.granulepos=0;
+    op.packetno=0;
+    ogg_stream_packetin(&os, &op);
+
+    while((ret=ogg_stream_flush(&os, &og))){
+      if(!ret)break;
+      ret=oe_write_page(&og, fout);
+      if(ret!=og.header_len+og.body_len){
+        fprintf(stderr,"Error: failed writing header to output stream\n");
+        exit(1);
+      }
+      bytes_written+=ret;
+      pages_out++;
+    }
+
+    comment_pad(&inopt.comments, &inopt.comments_length, comment_padding);
+    op.packet=(unsigned char *)inopt.comments;
+    op.bytes=inopt.comments_length;
+    op.b_o_s=0;
+    op.e_o_s=0;
+    op.granulepos=0;
+    op.packetno=1;
+    ogg_stream_packetin(&os, &op);
+  }
+
+  /* writing the rest of the opus header packets */
+  while((ret=ogg_stream_flush(&os, &og))){
+    if(!ret)break;
+    ret=oe_write_page(&og, fout);
+    if(ret!=og.header_len + og.body_len){
+      fprintf(stderr,"Error: failed writing header to output stream\n");
+      exit(1);
+    }
+    bytes_written+=ret;
+    pages_out++;
+  }
+
+  free(inopt.comments);
+
+  input=malloc(sizeof(float)*frame_size*chan);
+  if(input==NULL){
+    fprintf(stderr,"Error: couldn't allocate sample buffer.\n");
+    exit(1);
+  }
+
+  /*Main encoding loop (one frame per iteration)*/
+  eos=0;
+  nb_samples=-1;
+  while(!op.e_o_s){
+    int size_segments,cur_frame_size;
+    id++;
+
+    if(nb_samples<0){
+      nb_samples = inopt.read_samples(inopt.readdata,input,frame_size);
+      total_samples+=nb_samples;
+      if(nb_samples<frame_size)op.e_o_s=1;
+      else op.e_o_s=0;
+    }
+    op.e_o_s|=eos;
+
+    if(start_time==0){
+      start_time = time(NULL);
+    }
+
+    cur_frame_size=frame_size;
+
+    /*No fancy end padding, just fill with zeros for now.*/
+    if(nb_samples<cur_frame_size)for(i=nb_samples*chan;i<cur_frame_size*chan;i++)input[i]=0;
+
+    /*Encode current frame*/
+    VG_UNDEF(packet,max_frame_bytes);
+    VG_CHECK(input,sizeof(float)*chan*cur_frame_size);
+    nbBytes=opus_multistream_encode_float(st, input, cur_frame_size, packet, max_frame_bytes);
+    if(nbBytes<0){
+      fprintf(stderr, "Encoding failed: %s. Aborting.\n", opus_strerror(nbBytes));
+      break;
+    }
+    VG_CHECK(packet,nbBytes);
+    VG_UNDEF(input,sizeof(float)*chan*cur_frame_size);
+    nb_encoded+=cur_frame_size;
+    enc_granulepos+=cur_frame_size*48000/coding_rate;
+    total_bytes+=nbBytes;
+    size_segments=(nbBytes+255)/255;
+    peak_bytes=IMAX(nbBytes,peak_bytes);
+    min_bytes=IMIN(nbBytes,min_bytes);
+
+    if(frange!=NULL){
+      opus_uint32 rngs[256];
+      OpusEncoder *oe;
+      for(i=0;i<header.nb_streams;i++){
+        ret=opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(i,&oe));
+        ret=opus_encoder_ctl(oe,OPUS_GET_FINAL_RANGE(&rngs[i]));
+      }
+      save_range(frange,cur_frame_size*(48000/coding_rate),packet,nbBytes,
+                 rngs,header.nb_streams);
+    }
+
+    /*Flush early if adding this packet would make us end up with a
+      continued page which we wouldn't have otherwise.*/
+    while((((size_segments<=255)&&(last_segments+size_segments>255))||
+           (enc_granulepos-last_granulepos>max_ogg_delay))&&
+#ifdef OLD_LIBOGG
+           ogg_stream_flush(&os, &og)){
+#else
+           ogg_stream_flush_fill(&os, &og,255*255)){
+#endif
+      if(ogg_page_packets(&og)!=0)last_granulepos=ogg_page_granulepos(&og);
+      last_segments-=og.header[26];
+      ret=oe_write_page(&og, fout);
+      if(ret!=og.header_len+og.body_len){
+         fprintf(stderr,"Error: failed writing data to output stream\n");
+         exit(1);
+      }
+      bytes_written+=ret;
+      pages_out++;
+    }
+
+    /*The downside of early reading is if the input is an exact
+      multiple of the frame_size you'll get an extra frame that needs
+      to get cropped off. The downside of late reading is added delay.
+      If your ogg_delay is 120ms or less we'll assume you want the
+      low delay behavior.*/
+    if((!op.e_o_s)&&max_ogg_delay>5760){
+      nb_samples = inopt.read_samples(inopt.readdata,input,frame_size);
+      total_samples+=nb_samples;
+      if(nb_samples<frame_size)eos=1;
+      if(nb_samples==0)op.e_o_s=1;
+    } else nb_samples=-1;
+
+    op.packet=(unsigned char *)packet;
+    op.bytes=nbBytes;
+    op.b_o_s=0;
+    op.granulepos=enc_granulepos;
+    if(op.e_o_s){
+      /*We compute the final GP as ceil(len*48k/input_rate). When a resampling
+        decoder does the matching floor(len*input/48k) conversion the length will
+        be exactly the same as the input.*/
+      op.granulepos=((original_samples*48000+rate-1)/rate)+header.preskip;
+    }
+    op.packetno=2+id;
+    ogg_stream_packetin(&os, &op);
+    last_segments+=size_segments;
+
+    /*If the stream is over or we're sure that the delayed flush will fire,
+      go ahead and flush now to avoid adding delay.*/
+    while((op.e_o_s||(enc_granulepos+(frame_size*48000/coding_rate)-last_granulepos>max_ogg_delay)||
+           (last_segments>=255))?
+#ifdef OLD_LIBOGG
+    /*Libogg > 1.2.2 allows us to achieve lower overhead by
+      producing larger pages. For 20ms frames this is only relevant
+      above ~32kbit/sec.*/
+           ogg_stream_flush(&os, &og):
+           ogg_stream_pageout(&os, &og)){
+#else
+           ogg_stream_flush_fill(&os, &og,255*255):
+           ogg_stream_pageout_fill(&os, &og,255*255)){
+#endif
+      if(ogg_page_packets(&og)!=0)last_granulepos=ogg_page_granulepos(&og);
+      last_segments-=og.header[26];
+      ret=oe_write_page(&og, fout);
+      if(ret!=og.header_len+og.body_len){
+         fprintf(stderr,"Error: failed writing data to output stream\n");
+         exit(1);
+      }
+      bytes_written+=ret;
+      pages_out++;
+    }
+
+    if(!quiet){
+      stop_time = time(NULL);
+      if(stop_time>last_spin){
+        double estbitrate;
+        double coded_seconds=nb_encoded/(double)coding_rate;
+        double wall_time=(stop_time-start_time)+1e-6;
+        char sbuf[55];
+        static const char spinner[]="|/-\\";
+        if(!with_hard_cbr){
+          double tweight=1./(1+exp(-((coded_seconds/10.)-3.)));
+          estbitrate=(total_bytes*8.0/coded_seconds)*tweight+
+                      bitrate*(1.-tweight);
+        }else estbitrate=nbBytes*8*((double)coding_rate/frame_size);
+        fprintf(stderr,"\r");
+        for(i=0;i<last_spin_len;i++)fprintf(stderr," ");
+        if(inopt.total_samples_per_channel>0 && inopt.total_samples_per_channel<nb_encoded){
+          snprintf(sbuf,54,"\r[%c] %02d%% ",spinner[last_spin&3],
+          (int)floor(nb_encoded/(double)(inopt.total_samples_per_channel+inopt.skip)*100.));
+        }else{
+          snprintf(sbuf,54,"\r[%c] ",spinner[last_spin&3]);
+        }
+        last_spin_len=strlen(sbuf);
+        snprintf(sbuf+last_spin_len,54-last_spin_len,
+          "%02d:%02d:%02d.%02d %4.3gx realtime, %5.4gkbit/s",
+          (int)(coded_seconds/3600),(int)(coded_seconds/60)%60,
+          (int)(coded_seconds)%60,(int)(coded_seconds*100)%100,
+          coded_seconds/wall_time,
+          estbitrate/1000.);
+        fprintf(stderr,"%s",sbuf);
+        fflush(stderr);
+        last_spin_len=strlen(sbuf);
+        last_spin=stop_time;
+      }
+    }
+  }
+  stop_time = time(NULL);
+
+  for(i=0;i<last_spin_len;i++)fprintf(stderr," ");
+  if(last_spin_len)fprintf(stderr,"\r");
+
+  if(!quiet){
+    double coded_seconds=nb_encoded/(double)coding_rate;
+    double wall_time=(stop_time-start_time)+1e-6;
+    fprintf(stderr,"Encoding complete                                    \n");
+    fprintf(stderr,"-----------------------------------------------------\n");
+    fprintf(stderr,"       Encoded:");
+    print_time(coded_seconds);
+    fprintf(stderr,"\n       Runtime:");
+    print_time(wall_time);
+    fprintf(stderr,"\n                (%0.4gx realtime)\n",coded_seconds/wall_time);
+    fprintf(stderr,"         Wrote: %" I64FORMAT " bytes, %d packets, %" I64FORMAT " pages\n",bytes_written,id+1,pages_out);
+    fprintf(stderr,"       Bitrate: %0.6gkbit/s (without overhead)\n",
+            total_bytes*8.0/(coded_seconds)/1000.0);
+    fprintf(stderr," Instant rates: %0.6gkbit/s to %0.6gkbit/s\n                (%d to %d bytes per packet)\n",
+            min_bytes*8*((double)coding_rate/frame_size/1000.),
+            peak_bytes*8*((double)coding_rate/frame_size/1000.),min_bytes,peak_bytes);
+    fprintf(stderr,"      Overhead: %0.3g%% (container+metadata)\n",(bytes_written-total_bytes)/(double)bytes_written*100.);
+#ifdef OLD_LIBOGG
+    if(max_ogg_delay>(frame_size*(48000/coding_rate)*4))fprintf(stderr,"    (use libogg 1.3 or later for lower overhead)\n");
+#endif
+    fprintf(stderr,"\n");
+  }
+
+  opus_multistream_encoder_destroy(st);
+  ogg_stream_clear(&os);
+  free(packet);
+  free(input);
+  if(opt_ctls)free(opt_ctls_ctlval);
+
+  if(rate!=coding_rate)clear_resample(&inopt);
+  clear_padder(&inopt);
+  if(downmix)clear_downmix(&inopt);
+  in_format->close_func(inopt.readdata);
+  if(fin)fclose(fin);
+  if(fout)fclose(fout);
+  if(frange)fclose(frange);
+#ifdef WIN_UNICODE
+   free_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
+#endif
+  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
+
+ However, Opus and other non-vorbis formats omit the "framing_bit".
+
+The comment header is decoded as follows:
+  1) [vendor_length] = read an unsigned integer of 32 bits
+  2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
+  3) [user_comment_list_length] = read an unsigned integer of 32 bits
+  4) iterate [user_comment_list_length] times {
+     5) [length] = read an unsigned integer of 32 bits
+     6) this iteration's user comment = read a UTF-8 vector as [length] octets
+     }
+  7) done.
+*/
+
+#define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
+                           ((buf[base+2]<<16)&0xff0000)| \
+                           ((buf[base+1]<<8)&0xff00)| \
+                           (buf[base]&0xff))
+#define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
+                                     buf[base+2]=((val)>>16)&0xff; \
+                                     buf[base+1]=((val)>>8)&0xff; \
+                                     buf[base]=(val)&0xff; \
+                                 }while(0)
+
+static void comment_init(char **comments, int* length, const char *vendor_string)
+{
+  /*The 'vendor' field should be the actual encoding library used.*/
+  int vendor_length=strlen(vendor_string);
+  int user_comment_list_length=0;
+  int len=8+4+vendor_length+4;
+  char *p=(char*)malloc(len);
+  if(p==NULL){
+    fprintf(stderr, "malloc failed in comment_init()\n");
+    exit(1);
+  }
+  memcpy(p, "OpusTags", 8);
+  writeint(p, 8, vendor_length);
+  memcpy(p+12, vendor_string, vendor_length);
+  writeint(p, 12+vendor_length, user_comment_list_length);
+  *length=len;
+  *comments=p;
+}
+
+void comment_add(char **comments, int* length, char *tag, char *val)
+{
+  char* p=*comments;
+  int vendor_length=readint(p, 8);
+  int user_comment_list_length=readint(p, 8+4+vendor_length);
+  int tag_len=(tag?strlen(tag)+1:0);
+  int val_len=strlen(val);
+  int len=(*length)+4+tag_len+val_len;
+
+  p=(char*)realloc(p, len);
+  if(p==NULL){
+    fprintf(stderr, "realloc failed in comment_add()\n");
+    exit(1);
+  }
+
+  writeint(p, *length, tag_len+val_len);      /* length of comment */
+  if(tag){
+    memcpy(p+*length+4, tag, tag_len);        /* comment tag */
+    (p+*length+4)[tag_len-1] = '=';           /* separator */
+  }
+  memcpy(p+*length+4+tag_len, val, val_len);  /* comment */
+  writeint(p, 8+4+vendor_length, user_comment_list_length+1);
+  *comments=p;
+  *length=len;
+}
+
+static void comment_pad(char **comments, int* length, int amount)
+{
+  if(amount>0){
+    int i;
+    int newlen;
+    char* p=*comments;
+    /*Make sure there is at least amount worth of padding free, and
+       round up to the maximum that fits in the current ogg segments.*/
+    newlen=(*length+amount+255)/255*255-1;
+    p=realloc(p,newlen);
+    if(p==NULL){
+      fprintf(stderr,"realloc failed in comment_pad()\n");
+      exit(1);
+    }
+    for(i=*length;i<newlen;i++)p[i]=0;
+    *comments=p;
+    *length=newlen;
+  }
+}
+#undef readint
+#undef writeint
diff --git a/src/opusenc.h b/src/opusenc.h
new file mode 100644
index 0000000..91c1548
--- /dev/null
+++ b/src/opusenc.h
@@ -0,0 +1,109 @@
+#ifndef __OPUSENC_H
+#define __OPUSENC_H
+
+#include <opus_types.h>
+#include <ogg/ogg.h>
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(X) gettext(X)
+#else
+#define _(X) (X)
+#define textdomain(X)
+#define bindtextdomain(X, Y)
+#endif
+#ifdef gettext_noop
+#define N_(X) gettext_noop(X)
+#else
+#define N_(X) (X)
+#endif
+
+typedef long (*audio_read_func)(void *src, float *buffer, int samples);
+
+typedef struct
+{
+    audio_read_func read_samples;
+    void *readdata;
+    opus_int64 total_samples_per_channel;
+    int rawmode;
+    int channels;
+    long rate;
+    int gain;
+    int samplesize;
+    int endianness;
+    char *infilename;
+    int ignorelength;
+    int skip;
+    int extraout;
+    char *comments;
+    int comments_length;
+    int copy_comments;
+    int copy_pictures;
+} oe_enc_opt;
+
+void setup_scaler(oe_enc_opt *opt, float scale);
+void clear_scaler(oe_enc_opt *opt);
+void setup_padder(oe_enc_opt *opt, ogg_int64_t *original_samples);
+void clear_padder(oe_enc_opt *opt);
+int setup_downmix(oe_enc_opt *opt, int out_channels);
+void clear_downmix(oe_enc_opt *opt);
+void comment_add(char **comments, int* length, char *tag, char *val);
+
+typedef struct
+{
+    int (*id_func)(unsigned char *buf, int len); /* Returns true if can load file */
+    int id_data_len; /* Amount of data needed to id whether this can load the file */
+    int (*open_func)(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+    void (*close_func)(void *);
+    char *format;
+    char *description;
+} input_format;
+
+typedef struct {
+    short format;
+    short channels;
+    int samplerate;
+    int bytespersec;
+    short align;
+    short samplesize;
+    unsigned int mask;
+} wav_fmt;
+
+typedef struct {
+    short channels;
+    short samplesize;
+    opus_int64 totalsamples;
+    opus_int64 samplesread;
+    FILE *f;
+    short bigendian;
+    short unsigned8bit;
+    int *channel_permute;
+} wavfile;
+
+typedef struct {
+    short channels;
+    opus_int64 totalframes;
+    short samplesize;
+    int rate;
+    int offset;
+    int blocksize;
+} aiff_fmt;
+
+typedef wavfile aifffile; /* They're the same */
+
+input_format *open_audio_file(FILE *in, oe_enc_opt *opt);
+
+int raw_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+int aiff_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+int wav_id(unsigned char *buf, int len);
+int aiff_id(unsigned char *buf, int len);
+void wav_close(void *);
+void raw_close(void *);
+int setup_resample(oe_enc_opt *opt, int complexity, long outfreq);
+void clear_resample(oe_enc_opt *opt);
+
+long wav_read(void *, float *buffer, int samples);
+long wav_ieee_read(void *, float *buffer, int samples);
+
+#endif /* __OPUSENC_H */
diff --git a/src/opusenc.vcxproj b/src/opusenc.vcxproj
new file mode 100644
index 0000000..2734176
--- /dev/null
+++ b/src/opusenc.vcxproj
@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\share\getopt.c" />
+    <ClCompile Include="..\share\getopt1.c" />
+    <ClCompile Include="audio-in.c" />
+    <ClCompile Include="diag_range.c" />
+    <ClCompile Include="flac.c" />
+    <ClCompile Include="lpc.c" />
+    <ClCompile Include="opusenc.c" />
+    <ClCompile Include="opus_header.c" />
+    <ClCompile Include="picture.c" />
+    <ClCompile Include="resample.c" />
+    <ClCompile Include="..\win32\unicode_support.c" />
+    <ClCompile Include="wave_out.c" />
+    <ClCompile Include="wav_io.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\win32\config.h" />
+    <ClInclude Include="arch.h" />
+    <ClInclude Include="diag_range.h" />
+    <ClInclude Include="flac.h" />
+    <ClInclude Include="info_opus.h" />
+    <ClInclude Include="lpc.h" />
+    <ClInclude Include="opusenc.h" />
+    <ClInclude Include="opusinfo.h" />
+    <ClInclude Include="opus_header.h" />
+    <ClInclude Include="os_support.h" />
+    <ClInclude Include="picture.h" />
+    <ClInclude Include="resample_sse.h" />
+    <ClInclude Include="speex_resampler.h" />
+    <ClInclude Include="stack_alloc.h" />
+    <ClInclude Include="..\win32\unicode_support.h" />
+    <ClInclude Include="wave_out.h" />
+    <ClInclude Include="wav_io.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <Keyword>Win32Proj</Keyword>
+    <ProjectName>opusenc</ProjectName>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Configuration)_opusenc\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Platform)\$(Configuration)_opusenc\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Configuration)_opusenc\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Platform)\$(Configuration)_opusenc\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <TargetMachine>MachineX86</TargetMachine>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;WIN64;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+      <FloatingPointModel>Fast</FloatingPointModel>
+    </ClCompile>
+    <Link>
+      <TargetMachine>MachineX86</TargetMachine>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;WIN64;NDEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+      <FloatingPointModel>Fast</FloatingPointModel>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
diff --git a/src/opusenc.vcxproj.filters b/src/opusenc.vcxproj.filters
new file mode 100644
index 0000000..5ec65fb
--- /dev/null
+++ b/src/opusenc.vcxproj.filters
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{a97dcbdc-73ba-4511-a2e2-94d54ff2e4cf}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{3eef0629-e020-4efb-a840-9fee027384f4}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="wav_io.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="wave_out.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="audio-in.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="diag_range.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="lpc.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="opus_header.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="opusenc.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="picture.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="resample.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\share\getopt.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\share\getopt1.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\win32\unicode_support.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="flac.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="info_opus.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="lpc.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="opus_header.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="opusenc.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="opusinfo.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="os_support.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="picture.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="speex_resampler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="stack_alloc.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="wav_io.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="wave_out.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="arch.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="diag_range.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="resample_sse.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\win32\config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\win32\unicode_support.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="flac.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
diff --git a/src/opusinfo.c b/src/opusinfo.c
new file mode 100644
index 0000000..e35327e
--- /dev/null
+++ b/src/opusinfo.c
@@ -0,0 +1,1020 @@
+/* Opusinfo
+ *
+ * A tool to describe opus file contents and metadata.
+ *
+ * This is a fork of ogginfo from the vorbis-tools package
+ * which has been cut down to only have opus support.
+ *
+ * Ogginfo is
+ * Copyright 2002-2005 Michael Smith <msmith@xiph.org>
+ * Licensed under the GNU GPL, distributed with this program.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <getopt.h>
+#include <math.h>
+
+#include <ogg/ogg.h>
+
+/*No NLS support for now*/
+#define _(X) (X)
+
+#include "opusinfo.h"
+#include "opus_header.h"
+#include "info_opus.h"
+#include "picture.h"
+
+#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
+# include "unicode_support.h"
+#else
+# define fopen_utf8(_x,_y) fopen((_x),(_y))
+# define argc_utf8 argc
+# define argv_utf8 argv
+#endif
+
+#define CHUNK 4500
+
+static int printlots = 0;
+static int printinfo = 1;
+static int printwarn = 1;
+static int verbose = 1;
+
+static int flawed;
+
+#define CONSTRAINT_PAGE_AFTER_EOS   1
+#define CONSTRAINT_MUXING_VIOLATED  2
+
+static stream_set *create_stream_set(void) {
+    stream_set *set = calloc(1, sizeof(stream_set));
+
+    set->streams = calloc(5, sizeof(stream_processor));
+    set->allocated = 5;
+    set->used = 0;
+
+    return set;
+}
+
+void oi_info(char *format, ...)
+{
+    va_list ap;
+
+    if(!printinfo)
+        return;
+
+    va_start(ap, format);
+    vfprintf(stdout, format, ap);
+    va_end(ap);
+}
+
+void oi_warn(char *format, ...)
+{
+    va_list ap;
+
+    flawed = 1;
+    if(!printwarn)
+        return;
+
+    va_start(ap, format);
+    vfprintf(stdout, format, ap);
+    va_end(ap);
+}
+
+void oi_error(char *format, ...)
+{
+    va_list ap;
+
+    flawed = 1;
+
+    va_start(ap, format);
+    vfprintf(stdout, format, ap);
+    va_end(ap);
+}
+
+#define READ_U32_BE(buf) \
+    (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
+
+void check_xiph_comment(stream_processor *stream, int i, const char *comment,
+    int comment_length)
+{
+    char *sep = strchr(comment, '=');
+    int j;
+    int broken = 0;
+    unsigned char *val;
+    int bytes;
+    int remaining;
+
+    if(sep == NULL) {
+        oi_warn(_("WARNING: Comment %d in stream %d has invalid "
+              "format, does not contain '=': \"%s\"\n"),
+              i, stream->num, comment);
+             return;
+    }
+
+    for(j=0; j < sep-comment; j++) {
+        if(comment[j] < 0x20 || comment[j] > 0x7D) {
+            oi_warn(_("WARNING: Invalid comment fieldname in "
+                   "comment %d (stream %d): \"%s\"\n"),
+                   i, stream->num, comment);
+            broken = 1;
+            break;
+        }
+    }
+
+    if(broken)
+        return;
+
+    val = (unsigned char *)comment;
+
+    j = sep-comment+1;
+    while(j < comment_length)
+    {
+        remaining = comment_length - j;
+        if((val[j] & 0x80) == 0)
+            bytes = 1;
+        else if((val[j] & 0x40) == 0x40) {
+            if((val[j] & 0x20) == 0)
+                bytes = 2;
+            else if((val[j] & 0x10) == 0)
+                bytes = 3;
+            else if((val[j] & 0x08) == 0)
+                bytes = 4;
+            else if((val[j] & 0x04) == 0)
+                bytes = 5;
+            else if((val[j] & 0x02) == 0)
+                bytes = 6;
+            else {
+                oi_warn(_("WARNING: Illegal UTF-8 sequence in "
+                    "comment %d (stream %d): length marker wrong\n"),
+                    i, stream->num);
+                broken = 1;
+                break;
+            }
+        }
+        else {
+            oi_warn(_("WARNING: Illegal UTF-8 sequence in comment "
+                "%d (stream %d): length marker wrong\n"), i, stream->num);
+            broken = 1;
+            break;
+        }
+
+        if(bytes > remaining) {
+            oi_warn(_("WARNING: Illegal UTF-8 sequence in comment "
+                "%d (stream %d): too few bytes\n"), i, stream->num);
+            broken = 1;
+            break;
+        }
+
+        switch(bytes) {
+            case 1:
+                /* No more checks needed */
+                break;
+            case 2:
+                if((val[j+1] & 0xC0) != 0x80)
+                    broken = 1;
+                if((val[j] & 0xFE) == 0xC0)
+                    broken = 1;
+                break;
+            case 3:
+                if(!((val[j] == 0xE0 && val[j+1] >= 0xA0 && val[j+1] <= 0xBF &&
+                         (val[j+2] & 0xC0) == 0x80) ||
+                     (val[j] >= 0xE1 && val[j] <= 0xEC &&
+                         (val[j+1] & 0xC0) == 0x80 &&
+                         (val[j+2] & 0xC0) == 0x80) ||
+                     (val[j] == 0xED && val[j+1] >= 0x80 &&
+                         val[j+1] <= 0x9F &&
+                         (val[j+2] & 0xC0) == 0x80) ||
+                     (val[j] >= 0xEE && val[j] <= 0xEF &&
+                         (val[j+1] & 0xC0) == 0x80 &&
+                         (val[j+2] & 0xC0) == 0x80)))
+                     broken = 1;
+                 if(val[j] == 0xE0 && (val[j+1] & 0xE0) == 0x80)
+                     broken = 1;
+                 break;
+            case 4:
+                 if(!((val[j] == 0xF0 && val[j+1] >= 0x90 &&
+                         val[j+1] <= 0xBF &&
+                         (val[j+2] & 0xC0) == 0x80 &&
+                         (val[j+3] & 0xC0) == 0x80) ||
+                     (val[j] >= 0xF1 && val[j] <= 0xF3 &&
+                         (val[j+1] & 0xC0) == 0x80 &&
+                         (val[j+2] & 0xC0) == 0x80 &&
+                         (val[j+3] & 0xC0) == 0x80) ||
+                     (val[j] == 0xF4 && val[j+1] >= 0x80 &&
+                         val[j+1] <= 0x8F &&
+                         (val[j+2] & 0xC0) == 0x80 &&
+                         (val[j+3] & 0xC0) == 0x80)))
+                     broken = 1;
+                 if(val[j] == 0xF0 && (val[j+1] & 0xF0) == 0x80)
+                     broken = 1;
+                 break;
+             /* 5 and 6 aren't actually allowed at this point */
+             case 5:
+                 broken = 1;
+                 break;
+             case 6:
+                 broken = 1;
+                 break;
+         }
+
+         if(broken) {
+             char *simple = malloc (comment_length + 1);
+             char *seq = malloc (comment_length * 3 + 1);
+             static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                                  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+             int k, c1 = 0, c2 = 0;
+             for (k = 0; k < comment_length; k++) {
+               seq[c1++] = hex[((unsigned char)comment[k]) >> 4];
+               seq[c1++] = hex[((unsigned char)comment[k]) & 0xf];
+               seq[c1++] = ' ';
+
+               if(comment[k] < 0x20 || comment[k] > 0x7D)
+                 simple[c2++] = '?';
+               else
+                 simple[c2++] = comment[k];
+             }
+             seq[c1] = 0;
+             simple[c2] = 0;
+             oi_warn(_("WARNING: Illegal UTF-8 sequence in comment "
+                   "%d (stream %d): invalid sequence \"%s\": %s\n"), i,
+                   stream->num, simple, seq);
+             broken = 1;
+             free (simple);
+             free (seq);
+             break;
+         }
+
+         j += bytes;
+     }
+
+     if(sep - comment == 22
+           && oi_strncasecmp(comment, "METADATA_BLOCK_PICTURE", 22) == 0) {
+         ogg_uint32_t   picture_type;
+         ogg_uint32_t   mime_type_length;
+         ogg_uint32_t   description_length;
+         ogg_uint32_t   width;
+         ogg_uint32_t   height;
+         ogg_uint32_t   depth;
+         ogg_uint32_t   colors;
+         ogg_uint32_t   image_length;
+         ogg_uint32_t   file_width;
+         ogg_uint32_t   file_height;
+         ogg_uint32_t   file_depth;
+         ogg_uint32_t   file_colors;
+         unsigned char *data;
+         int            data_sz;
+         int            len;
+         int            is_url;
+         int            format;
+         int            has_palette;
+         int            colors_set;
+         len=comment_length - (sep+1-comment);
+         /*Decode the Base64 encoded data.*/
+         if(len&3) {
+             oi_warn(_("WARNING: Illegal Base64 length in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): %i is not "
+                   "divisible by 4\n"), i, stream->num, len);
+         }
+         len>>=2;
+         data_sz=3*len;
+         if(data_sz > 0) {
+             if(comment[comment_length - 1] == '=') {
+                 data_sz--;
+             }
+             if(comment[comment_length - 2] == '=') {
+                 data_sz--;
+             }
+         }
+         data=(unsigned char *)malloc(data_sz*sizeof(*data));
+         for (j = 0; j < len; j++) {
+             ogg_uint32_t value;
+             int          k;
+             value = 0;
+             for (k = 1; k <= 4; k++) {
+                 unsigned c;
+                 unsigned d;
+                 c = (unsigned char)sep[4*j+k];
+                 if(c == '+') {
+                     d = 62;
+                 }
+                 else if(c == '/') {
+                     d = 63;
+                 }
+                 else if(c >= '0' && c <= '9') {
+                     d = 52+c-'0';
+                 }
+                 else if(c >= 'a' && c <= 'z') {
+                     d = 26+c-'a';
+                 }
+                 else if(c >= 'A' && c <= 'Z') {
+                     d = c-'A';
+                 }
+                 else if(c == '=') {
+                     if(3*j+k-1 < data_sz) {
+                         oi_warn(_("WARNING: Terminating '=' in illegal "
+                               "position in Base64 encoded "
+                               "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                               "%i characters before the end.\n"), i,
+                               stream->num, data_sz - (3*j+k-1));
+                         free(data);
+                         return;
+                     }
+                     d = 0;
+                 }
+                 else {
+                     oi_warn(_("WARNING: Illegal Base64 character in "
+                           "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                           "'%c' (0x%02X)\n"), i, stream->num,
+                           (char)(c<0x20||c>0x7E?'?':c), c);
+                     free(data);
+                     return;
+                 }
+                 value = value << 6 | d;
+             }
+             data[3*j] = (unsigned char)(value>>16);
+             if(3*j+1 < data_sz) {
+                 data[3*j+1] = (unsigned char)(value>>8);
+                 if(3*j+2 < data_sz) {
+                     data[3*j+2] = (unsigned char)value;
+                 }
+             }
+         }
+         /*Now validate the METADATA_BLOCK_PICTURE structure.*/
+         if(data_sz < 32) {
+             oi_warn(_("WARNING: Not enough data for "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "expected at least 32 bytes, got %i\n"), i, stream->num,
+                   data_sz);
+             free(data);
+             return;
+         }
+         j = 0;
+         picture_type = READ_U32_BE(data+j);
+         if(picture_type > 20) {
+             oi_warn(_("WARNING: Unknown picture type in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "%li\n"), i, stream->num, (long)picture_type);
+             broken = 1;
+         }
+         if(picture_type >= 1 && picture_type <= 2) {
+             if(stream->seen_file_icons & picture_type) {
+                 oi_warn(_("WARNING: Duplicate picture type in "
+                       "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                       " %s\n"), i, stream->num, picture_type == 1 ?
+                       _("only one picture of type 1 (32x32 icon) allowed") :
+                       _("only one picture of type 2 (icon) allowed"));
+                 broken = 1;
+             }
+             stream->seen_file_icons |= picture_type;
+         }
+         j += 4;
+         mime_type_length = READ_U32_BE(data+j);
+         if(mime_type_length > (size_t)data_sz-32) {
+             oi_warn(_("WARNING: Invalid mime type length in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "%lu bytes when %i are available\n"), i, stream->num,
+                   (long)mime_type_length, data_sz-32);
+             free(data);
+             return;
+         }
+         for (j += 4; j < 8+(int)mime_type_length; j++) {
+             if(data[j] < 0x20 || data[j] > 0x7E) {
+                 oi_warn(_("WARNING: Invalid character in mime type of "
+                       "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                       "0x%02X\n"), i, stream->num, data[j]);
+                 broken = 1;
+             }
+         }
+         description_length = READ_U32_BE(data+j);
+         if(description_length > (size_t)data_sz-mime_type_length-32) {
+             oi_warn(_("WARNING: Invalid description length in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "%lu bytes when %i are available\n"), i, stream->num,
+                   (long)description_length, data_sz-mime_type_length-32);
+             free(data);
+             return;
+         }
+         /*TODO: Validate that description is UTF-8.*/
+         j += 4+description_length;
+         width = READ_U32_BE(data+j);
+         j += 4;
+         height = READ_U32_BE(data+j);
+         j += 4;
+         depth = READ_U32_BE(data+j);
+         j += 4;
+         colors = READ_U32_BE(data+j);
+         j += 4;
+         /*If any value is non-zero, then they all MUST be valid values, and
+           so colors should be treated as set (even if zero).*/
+         colors_set = width != 0 || height != 0 || depth != 0 || colors != 0;
+         /*This isn't triggered if colors == 0, since that can be a valid
+           value.*/
+         if((width == 0 || height == 0 || depth == 0) && colors_set) {
+             oi_warn(_("WARNING: Invalid picture parameters in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "width (%i), height (%i), depth (%i), and colors (%i) MUST "
+                   "either be set to valid values or all set to 0\n"), i,
+                   stream->num, (int)width, (int)height, (int)depth,
+                   (int)colors);
+             broken = 1;
+         }
+         image_length = READ_U32_BE(data+j);
+         j += 4;
+         /*This one should match exactly.*/
+         if(image_length != (size_t)data_sz-j) {
+             oi_warn(_("WARNING: Invalid image data size in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "%lu bytes when %i are available\n"), i, stream->num,
+                   (long)image_length, data_sz-j);
+             free(data);
+             return;
+         }
+         is_url = 0;
+         format = -1;
+         if(mime_type_length == 10
+               && oi_strncasecmp((const char*)data+8, "image/jpeg",
+                     mime_type_length) == 0) {
+             if(!is_jpeg(data+j, image_length)) {
+                 oi_warn(_("WARNING: Invalid image data in "
+                       "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                       "mime type is %.*s but image does not appear to be "
+                       "JPEG\n"), i, stream->num, mime_type_length, data+8);
+                 free(data);
+                 return;
+             }
+             format = PIC_FORMAT_JPEG;
+         }
+         else if(mime_type_length == 9
+               && oi_strncasecmp((const char *)data+8, "image/png",
+                     mime_type_length) == 0) {
+             if(!is_png(data+j, image_length)) {
+                 oi_warn(_("WARNING: Invalid image data in "
+                       "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                       "mime type is %.*s but image does not appear to be "
+                       "PNG\n"), i, stream->num, mime_type_length, data+8);
+                 free(data);
+                 return;
+             }
+             format = PIC_FORMAT_PNG;
+         }
+         else if(mime_type_length == 9
+               && oi_strncasecmp((const char *)data+8, "image/gif",
+                     mime_type_length) == 0) {
+             if(!is_gif(data+j, image_length)) {
+                 oi_warn(_("WARNING: Invalid image data in "
+                       "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                       "mime type is %.*s but image does not appear to be "
+                       "PNG\n"), i, stream->num, mime_type_length, data+8);
+                 free(data);
+                 return;
+             }
+             format = PIC_FORMAT_GIF;
+         }
+         else if(mime_type_length == 3
+               && strncmp((const char *)data+8, "-->", mime_type_length) == 0) {
+             is_url = 1;
+             /*TODO: validate URL.*/
+         }
+         else if(mime_type_length == 0 || (mime_type_length == 6 &&
+               oi_strncasecmp((const char *)data+8, "image/",
+                     mime_type_length) == 0)) {
+             if(is_jpeg(data+j, image_length)) {
+                 format = PIC_FORMAT_JPEG;
+             }
+             else if(is_png(data+j, image_length)) {
+                 format = PIC_FORMAT_PNG;
+             }
+             else if(is_gif(data+j, image_length)) {
+                 format = PIC_FORMAT_GIF;
+             }
+             else {
+                 oi_warn(_("WARNING: Unknown image format in "
+                       "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                       "\"%.*s\" may not be well-supported\n"), i, stream->num,
+                       mime_type_length, data+8);
+             }
+         }
+         else {
+             oi_warn(_("WARNING: Unknown mime type in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "\"%.*s\" may not be well-supported\n"), i, stream->num,
+                   mime_type_length, data+8);
+         }
+         file_width = file_height = file_depth = file_colors = 0;
+         has_palette = -1;
+         switch(format) {
+             case PIC_FORMAT_JPEG:
+                extract_jpeg_params(data+j, image_length,
+                      &file_width, &file_height, &file_depth, &file_colors,
+                      &has_palette);
+                break;
+             case PIC_FORMAT_PNG:
+                extract_png_params(data+j, image_length,
+                      &file_width, &file_height, &file_depth, &file_colors,
+                      &has_palette);
+                break;
+             case PIC_FORMAT_GIF:
+                extract_gif_params(data+j, image_length,
+                      &file_width, &file_height, &file_depth, &file_colors,
+                      &has_palette);
+                break;
+         }
+         if(format >= 0 && has_palette < 0) {
+             /*We should have been able to affirmatively determine whether or
+               not there was a palette if we parsed the image successfully.*/
+             oi_warn(_("WARNING: Could not parse image parameters in"
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "possibly corrupt image?\n"), i, stream->num);
+             broken = 1;
+         }
+         if(width && width != file_width) {
+             oi_warn(_("WARNING: Mismatched picture parameters in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "width declared as %u but appears to be %u\n"), i,
+                   stream->num, (unsigned)width, (unsigned)file_width);
+             broken = 1;
+         }
+         if(height && height != file_height) {
+             oi_warn(_("WARNING: Mismatched picture parameters in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "height declared as %u but appears to be %u\n"), i,
+                   stream->num, (unsigned)height, (unsigned)file_height);
+             broken = 1;
+         }
+         if(depth && depth != file_depth) {
+             oi_warn(_("WARNING: Mismatched picture parameters in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "depth declared as %u but appears to be %u\n"), i,
+                   stream->num, (unsigned)depth, (unsigned)file_depth);
+             broken = 1;
+         }
+         if(has_palette >= 0 && colors_set && colors != file_colors) {
+             oi_warn(_("WARNING: Mismatched picture parameters in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "palette size declared as %u but appears to be %u\n"), i,
+                   stream->num, (unsigned)colors, (unsigned)file_colors);
+             broken = 1;
+         }
+         if(picture_type == 1
+               && ((is_url && (width != 0 || height != 0)
+                           && (width != 32 || height != 32))
+                     || (!is_url && (file_width != 32 || file_height != 32)))) {
+             oi_warn(_("WARNING: Invalid picture in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "picture of type 1 (32x32 icon) MUST be a 32x32 PNG, but "
+                   "the image has dimensions %ux%u\n"), i, stream->num,
+                   (unsigned)is_url?width:file_width,
+                   (unsigned)is_url?height:file_height);
+             broken = 1;
+         }
+         if(picture_type == 1 && !is_url && format != PIC_FORMAT_PNG) {
+             oi_warn(_("WARNING: Invalid picture in "
+                   "METADATA_BLOCK_PICTURE comment %d (stream %d): "
+                   "picture of type 1 (32x32 icon) MUST be a 32x32 PNG, but "
+                   "the image does not appear to be a PNG\n"), i, stream->num);
+             broken = 1;
+         }
+         /*Print the contents of the block using the same format as the
+           SPECIFICATION argument to opusenc/flac/etc. (except without an image
+           filename, since we don't know the original).*/
+         oi_info("\t%.*s%u|%.*s|%.*s|%ux%ux%u",
+               sep+1-comment, comment, (unsigned)picture_type,
+               mime_type_length, data+8,
+               description_length, data+12+mime_type_length,
+               (unsigned)width, (unsigned)height, (unsigned)depth);
+         if(colors) {
+             oi_info("/%u", (unsigned)colors);
+         }
+         if(is_url) {
+             oi_info("|%.*s\n", image_length, data+j);
+         }
+         else {
+             oi_info("|<%u bytes of image data>\n",(unsigned)image_length);
+         }
+         free(data);
+         return;
+     }
+
+     if(!broken) {
+         oi_info("\t%s\n", comment);
+     }
+}
+
+static void process_null(stream_processor *stream, ogg_page *page)
+{
+    /* This is for invalid streams. */
+    (void)stream;
+    (void)page;
+}
+
+static void process_other(stream_processor *stream, ogg_page *page )
+{
+    ogg_packet packet;
+
+    ogg_stream_pagein(&stream->os, page);
+
+    while(ogg_stream_packetout(&stream->os, &packet) > 0) {
+        /* Should we do anything here? Currently, we don't */
+    }
+}
+
+
+static void free_stream_set(stream_set *set)
+{
+    int i;
+    for(i=0; i < set->used; i++) {
+        if(!set->streams[i].end) {
+            oi_warn(_("WARNING: EOS not set on stream %d (normal for live streams)\n"),
+                    set->streams[i].num);
+            if(set->streams[i].process_end)
+                set->streams[i].process_end(&set->streams[i]);
+        }
+        ogg_stream_clear(&set->streams[i].os);
+    }
+
+    free(set->streams);
+    free(set);
+}
+
+static int streams_open(stream_set *set)
+{
+    int i;
+    int res=0;
+    for(i=0; i < set->used; i++) {
+        if(!set->streams[i].end)
+            res++;
+    }
+
+    return res;
+}
+
+static void null_start(stream_processor *stream)
+{
+    stream->process_end = NULL;
+    stream->type = "invalid";
+    stream->process_page = process_null;
+}
+
+static void other_start(stream_processor *stream, char *type)
+{
+    if(type)
+        stream->type = type;
+    else
+        stream->type = "unknown";
+    stream->process_page = process_other;
+    stream->process_end = NULL;
+}
+
+static stream_processor *find_stream_processor(stream_set *set, ogg_page *page)
+{
+    ogg_uint32_t serial = ogg_page_serialno(page);
+    int i;
+    int invalid = 0;
+    int constraint = 0;
+    stream_processor *stream;
+
+    for(i=0; i < set->used; i++) {
+        if(serial == set->streams[i].serial) {
+            /* We have a match! */
+            stream = &(set->streams[i]);
+
+            set->in_headers = 0;
+            /* if we have detected EOS, then this can't occur here. */
+            if(stream->end) {
+                stream->isillegal = 1;
+                stream->constraint_violated = CONSTRAINT_PAGE_AFTER_EOS;
+                return stream;
+            }
+
+            stream->isnew = 0;
+            stream->start = ogg_page_bos(page);
+            stream->end = ogg_page_eos(page);
+            stream->serial = serial;
+            return stream;
+        }
+    }
+
+    /* If there are streams open, and we've reached the end of the
+     * headers, then we can't be starting a new stream.
+     * XXX: might this sometimes catch ok streams if EOS flag is missing,
+     * but the stream is otherwise ok?
+     */
+    if(streams_open(set) && !set->in_headers) {
+        constraint = CONSTRAINT_MUXING_VIOLATED;
+        invalid = 1;
+    }
+
+    set->in_headers = 1;
+
+    if(set->allocated < set->used)
+        stream = &set->streams[set->used];
+    else {
+        set->allocated += 5;
+        set->streams = realloc(set->streams, sizeof(stream_processor)*
+                set->allocated);
+        stream = &set->streams[set->used];
+    }
+    set->used++;
+    stream->num = set->used; /* We count from 1 */
+
+    stream->isnew = 1;
+    stream->isillegal = invalid;
+    stream->constraint_violated = constraint;
+    stream->seen_file_icons = 0;
+
+    {
+        int res;
+        int has_oi_supported=0;
+        ogg_packet packet;
+
+        /* We end up processing the header page twice, but that's ok. */
+        ogg_stream_init(&stream->os, serial);
+        ogg_stream_pagein(&stream->os, page);
+        res = ogg_stream_packetout(&stream->os, &packet);
+        if(res <= 0) {
+            oi_warn(_("WARNING: Invalid header page, no packet found\n"));
+            null_start(stream);
+        }
+        else if(packet.bytes >= 19 && memcmp(packet.packet, "OpusHead", 8)==0)
+            info_opus_start(stream);
+        else if(packet.bytes >= 7 && memcmp(packet.packet, "\x01vorbis", 7)==0) {
+            other_start(stream, "Vorbis");
+            has_oi_supported=1;
+        } else if(packet.bytes >= 7 && memcmp(packet.packet, "\x80theora", 7)==0) {
+            other_start(stream, "Theora");
+            has_oi_supported=1;
+        } else if(packet.bytes >= 8 && memcmp(packet.packet, "OggMIDI\0", 8)==0)
+            other_start(stream, "MIDI");
+        else if(packet.bytes >= 5 && memcmp(packet.packet, "\177FLAC", 5)==0)
+            other_start(stream, "FLAC");
+        else if(packet.bytes == 4 && memcmp(packet.packet, "fLaC", 4)==0)
+            other_start(stream, "FLAC (legacy)");
+        else if(packet.bytes >= 8 && memcmp(packet.packet, "Speex   ", 8)==0)
+            other_start(stream, "speex");
+        else if(packet.bytes >= 8 && memcmp(packet.packet, "fishead\0", 8)==0)
+            other_start(stream, "skeleton");
+        else if(packet.bytes >= 5 && memcmp(packet.packet, "BBCD\0", 5)==0)
+            other_start(stream, "dirac");
+        else if(packet.bytes >= 8 && memcmp(packet.packet, "KW-DIRAC", 8)==0)
+            other_start(stream, "dirac (legacy)");
+        else if(packet.bytes >= 8 && memcmp(packet.packet, "\x80kate\0\0\0", 8)==0) {
+            other_start(stream, "Kate");
+            has_oi_supported=1;
+        } else
+            other_start(stream, NULL);
+
+        res = ogg_stream_packetout(&stream->os, &packet);
+        if(res > 0) {
+            oi_warn(_("WARNING: Invalid header page in stream %d, "
+                              "contains multiple packets\n"), stream->num);
+        }
+        if(has_oi_supported)oi_info(_("Use ogginfo for more information on this file.\n"));
+
+        /* re-init, ready for processing */
+        ogg_stream_clear(&stream->os);
+        ogg_stream_init(&stream->os, serial);
+   }
+
+   stream->start = ogg_page_bos(page);
+   stream->end = ogg_page_eos(page);
+   stream->serial = serial;
+   stream->shownillegal = 0;
+   stream->seqno = ogg_page_pageno(page);
+
+   if(stream->serial == 0 || stream->serial == 0xFFFFFFFFUL) {
+       oi_info(_("Note: Stream %d has serial number %d, which is legal but may "
+              "cause problems with some tools.\n"), stream->num,
+               stream->serial);
+   }
+
+   return stream;
+}
+
+static int get_next_page(FILE *f, ogg_sync_state *ogsync, ogg_page *page,
+        ogg_int64_t *written)
+{
+    int ret;
+    char *buffer;
+    int bytes;
+
+    while((ret = ogg_sync_pageseek(ogsync, page)) <= 0) {
+        if(ret < 0) {
+            /* unsynced, we jump over bytes to a possible capture - we don't need to read more just yet */
+            oi_warn(_("WARNING: Hole in data (%d bytes) found at approximate offset %" I64FORMAT " bytes. Corrupted Ogg.\n"), -ret, *written);
+            continue;
+        }
+
+        /* zero return, we didn't have enough data to find a whole page, read */
+        buffer = ogg_sync_buffer(ogsync, CHUNK);
+        bytes = fread(buffer, 1, CHUNK, f);
+        if(bytes <= 0) {
+            ogg_sync_wrote(ogsync, 0);
+            return 0;
+        }
+        ogg_sync_wrote(ogsync, bytes);
+        *written += bytes;
+    }
+
+    return 1;
+}
+
+static void process_file(char *filename) {
+    FILE *file = fopen_utf8(filename, "rb");
+    ogg_sync_state ogsync;
+    ogg_page page;
+    stream_set *processors = create_stream_set();
+    int gotpage = 0;
+    ogg_int64_t written = 0;
+
+    if(!file) {
+        oi_error(_("Error opening input file \"%s\": %s\n"), filename,
+                    strerror(errno));
+        return;
+    }
+
+    printf(_("Processing file \"%s\"...\n\n"), filename);
+
+    ogg_sync_init(&ogsync);
+
+    while(get_next_page(file, &ogsync, &page, &written)) {
+        stream_processor *p = find_stream_processor(processors, &page);
+        gotpage = 1;
+
+        if(!p) {
+            oi_error(_("Could not find a processor for stream, bailing\n"));
+            return;
+        }
+
+        if(p->isillegal && !p->shownillegal) {
+            char *constraint;
+            switch(p->constraint_violated) {
+                case CONSTRAINT_PAGE_AFTER_EOS:
+                    constraint = _("Page found for stream after EOS flag");
+                    break;
+                case CONSTRAINT_MUXING_VIOLATED:
+                    constraint = _("Ogg muxing constraints violated, new "
+                                   "stream before EOS of all previous streams");
+                    break;
+                default:
+                    constraint = _("Error unknown.");
+            }
+
+            oi_warn(_("WARNING: illegally placed page(s) for logical stream %d\n"
+                   "This indicates a corrupt Ogg file: %s.\n"),
+                    p->num, constraint);
+            p->shownillegal = 1;
+            /* If it's a new stream, we want to continue processing this page
+             * anyway to suppress additional spurious errors
+             */
+            if(!p->isnew)
+                continue;
+        }
+
+        if(p->isnew) {
+            oi_info(_("New logical stream (#%d, serial: %08x): type %s\n"),
+                    p->num, p->serial, p->type);
+            if(!p->start)
+                oi_warn(_("WARNING: stream start flag not set on stream %d\n"),
+                        p->num);
+        }
+        else if(p->start)
+            oi_warn(_("WARNING: stream start flag found in mid-stream "
+                      "on stream %d\n"), p->num);
+
+        if(p->seqno++ != ogg_page_pageno(&page)) {
+            if(!p->lostseq)
+                oi_warn(_("WARNING: sequence number gap in stream %d. Got page "
+                       "%ld when expecting page %ld. Indicates missing data.%s\n"
+                       ), p->num, ogg_page_pageno(&page), p->seqno - 1, p->seqno-1==2?_(" (normal for live streams)"):"");
+            p->seqno = ogg_page_pageno(&page);
+            p->lostseq = 1;
+        }
+        else
+            p->lostseq = 0;
+
+        if(!p->isillegal) {
+            p->process_page(p, &page);
+
+            if(p->end) {
+                if(p->process_end)
+                    p->process_end(p);
+                oi_info(_("Logical stream %d ended\n"), p->num);
+                p->isillegal = 1;
+                p->constraint_violated = CONSTRAINT_PAGE_AFTER_EOS;
+            }
+        }
+    }
+
+    if(!gotpage)
+        oi_error(_("ERROR: No Ogg data found in file \"%s\".\n"
+                "Input probably not Ogg.\n"), filename);
+
+    free_stream_set(processors);
+
+    ogg_sync_clear(&ogsync);
+
+    fclose(file);
+}
+
+static void version (void) {
+    printf (_("opusinfo from %s %s\n"), PACKAGE_NAME, PACKAGE_VERSION);
+}
+
+static void usage(void) {
+    version ();
+    printf (_(" by the Xiph.Org Foundation (http://www.xiph.org/)\n\n"));
+    printf(_("(c) 2003-2005 Michael Smith <msmith@xiph.org>\n"
+             "(c) 2012 Gregory Maxwell <greg@xiph.org>\n\n"
+             "Opusinfo is a fork of ogginfo from the vorbis-tools package\n"
+             "which has been cut down to only support opus files.\n\n"
+             "Usage: opusinfo [flags] file1.opus [file2.opus ... fileN.opus]\n"
+             "Flags supported:\n"
+             "\t-h Show this help message.\n"
+             "\t-q Make less verbose. Once will remove detailed informative\n"
+             "\t   messages, twice will remove warnings.\n"
+             "\t-v Make more verbose. This may enable more detailed checks\n"
+             "\t   for some stream types.\n"));
+    printf (_("\t-V Output version information and exit.\n"));
+}
+
+int main(int argc, char **argv)
+{
+    int f, ret;
+
+#ifdef WIN_UNICODE
+    int argc_utf8;
+    char **argv_utf8;
+
+    (void)argc;
+    (void)argv;
+
+    init_console_utf8();
+    init_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
+#endif
+
+    if(argc_utf8 < 2) {
+        fprintf(stdout,
+                _("Usage: opusinfo [flags] file1.opus [file2.opus ... fileN.opus]\n"
+                  "\n"
+                  "opusinfo is a tool for printing information about Opus files\n"
+                  "and for diagnosing problems with them.\n"
+                  "Full help shown with \"opusinfo -h\".\n"));
+#ifdef WIN_UNICODE
+        uninit_console_utf8();
+#endif
+        exit(1);
+    }
+
+    while((ret = getopt(argc_utf8, argv_utf8, "hqvV")) >= 0) {
+        switch(ret) {
+            case 'h':
+                usage();
+                return 0;
+            case 'V':
+                version();
+                return 0;
+            case 'v':
+                verbose++;
+                break;
+            case 'q':
+                verbose--;
+                break;
+        }
+    }
+
+    if(verbose > 1)
+        printlots = 1;
+    if(verbose < 1)
+        printinfo = 0;
+    if(verbose < 0)
+        printwarn = 0;
+
+    if(optind >= argc_utf8) {
+        fprintf(stderr,
+                _("No input files specified. \"opusinfo -h\" for help\n"));
+        return 1;
+    }
+
+    ret = 0;
+
+    for(f=optind; f < argc_utf8; f++) {
+        flawed = 0;
+        process_file(argv_utf8[f]);
+        if(flawed != 0)
+            ret = flawed;
+    }
+
+#ifdef WIN_UNICODE
+   free_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
+   uninit_console_utf8();
+#endif
+
+    return ret;
+}
diff --git a/src/opusinfo.h b/src/opusinfo.h
new file mode 100644
index 0000000..e758dcc
--- /dev/null
+++ b/src/opusinfo.h
@@ -0,0 +1,52 @@
+/* Ogginfo
+ *
+ * A tool to describe ogg file contents and metadata.
+ *
+ * Copyright 2002-2005 Michael Smith <msmith@xiph.org>
+ * Licensed under the GNU GPL, distributed with this program.
+ */
+
+/*No NLS support for now*/
+#define _(X) (X)
+
+#ifdef _WIN32
+#define I64FORMAT "I64d"
+#else
+#define I64FORMAT "lld"
+#endif
+
+typedef struct _stream_processor {
+    void (*process_page)(struct _stream_processor *, ogg_page *);
+    void (*process_end)(struct _stream_processor *);
+    int isillegal;
+    int constraint_violated;
+    int shownillegal;
+    int isnew;
+    long seqno;
+    int lostseq;
+    int seen_file_icons;
+
+    int start;
+    int end;
+
+    int num;
+    char *type;
+
+    ogg_uint32_t serial; /* must be 32 bit unsigned */
+    ogg_stream_state os;
+    void *data;
+} stream_processor;
+
+typedef struct {
+    stream_processor *streams;
+    int allocated;
+    int used;
+
+    int in_headers;
+} stream_set;
+
+
+void oi_info(char *format, ...);
+void oi_warn(char *format, ...);
+void oi_error(char *format, ...);
+void check_xiph_comment(stream_processor *stream, int i, const char *comment, int comment_length);
diff --git a/src/opusinfo.vcxproj b/src/opusinfo.vcxproj
new file mode 100644
index 0000000..4015682
--- /dev/null
+++ b/src/opusinfo.vcxproj
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\share\getopt.c" />
+    <ClCompile Include="..\share\getopt1.c" />
+    <ClCompile Include="audio-in.c" />
+    <ClCompile Include="diag_range.c" />
+    <ClCompile Include="flac.c" />
+    <ClCompile Include="info_opus.c" />
+    <ClCompile Include="lpc.c" />
+    <ClCompile Include="opusinfo.c" />
+    <ClCompile Include="opus_header.c" />
+    <ClCompile Include="picture.c" />
+    <ClCompile Include="resample.c" />
+    <ClCompile Include="wave_out.c" />
+    <ClCompile Include="wav_io.c" />
+    <ClCompile Include="..\win32\unicode_support.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\win32\config.h" />
+    <ClInclude Include="arch.h" />
+    <ClInclude Include="diag_range.h" />
+    <ClInclude Include="flac.h" />
+    <ClInclude Include="info_opus.h" />
+    <ClInclude Include="lpc.h" />
+    <ClInclude Include="opusenc.h" />
+    <ClInclude Include="opusinfo.h" />
+    <ClInclude Include="opus_header.h" />
+    <ClInclude Include="os_support.h" />
+    <ClInclude Include="picture.h" />
+    <ClInclude Include="resample_sse.h" />
+    <ClInclude Include="speex_resampler.h" />
+    <ClInclude Include="stack_alloc.h" />
+    <ClInclude Include="wave_out.h" />
+    <ClInclude Include="wav_io.h" />
+    <ClInclude Include="..\win32\unicode_support.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <Keyword>Win32Proj</Keyword>
+    <ProjectName>opusinfo</ProjectName>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Configuration)_opusinfo\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Platform)\$(Configuration)_opusinfo\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Configuration)_opusinfo\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LibraryPath>..\..\opus\win32\VS2010\$(Platform)\$(Configuration);..\..\libogg\win32\VS2010\$(Platform)\$(Configuration);$(LibraryPath)</LibraryPath>
+    <IntDir>$(Platform)\$(Configuration)_opusinfo\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <TargetMachine>MachineX86</TargetMachine>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;WIN64;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+      <FloatingPointModel>Fast</FloatingPointModel>
+    </ClCompile>
+    <Link>
+      <TargetMachine>MachineX86</TargetMachine>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;WIN64;NDEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <AdditionalIncludeDirectories>..\..\libogg\include;..\..\opus\include;..\win32;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+      <FloatingPointModel>Fast</FloatingPointModel>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>celt.lib;opus.lib;silk_common.lib;silk_float.lib;libogg_static.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
+    </Link>
+    <PreBuildEvent>
+      <Command>"$(ProjectDir)..\win32\genversion.bat" "$(ProjectDir)..\win32\version.h" PACKAGE_VERSION</Command>
+      <Message>Generating version.h</Message>
+    </PreBuildEvent>
+    <CustomBuildStep>
+      <Command>
+      </Command>
+    </CustomBuildStep>
+    <CustomBuildStep>
+      <Outputs>
+      </Outputs>
+    </CustomBuildStep>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
diff --git a/src/opusinfo.vcxproj.filters b/src/opusinfo.vcxproj.filters
new file mode 100644
index 0000000..f9c6118
--- /dev/null
+++ b/src/opusinfo.vcxproj.filters
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{9151be1a-eb66-4999-80bf-86a9a9850aad}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{6002a8c5-eecb-40c8-b815-523357c42aeb}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="wave_out.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="audio-in.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="diag_range.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="info_opus.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="lpc.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="opus_header.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="opusinfo.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="picture.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="resample.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="wav_io.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\share\getopt.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\share\getopt1.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\win32\unicode_support.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="flac.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="opus_header.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="opusenc.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="opusinfo.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="os_support.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="picture.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="speex_resampler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="stack_alloc.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="wav_io.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="wave_out.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="arch.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="diag_range.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="info_opus.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="lpc.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="resample_sse.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\win32\config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\win32\unicode_support.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="flac.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
diff --git a/src/opusrtp.c b/src/opusrtp.c
new file mode 100644
index 0000000..8ee793d
--- /dev/null
+++ b/src/opusrtp.c
@@ -0,0 +1,938 @@
+/* Copyright 2012 Mozilla Foundation
+   Copyright 2012 Xiph.Org Foundation
+   Copyright 2012 Gregory Maxwell
+
+   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.
+ */
+
+/* dump opus rtp packets into an ogg file
+ *
+ * compile with: cc -g -Wall -o opusrtc opusrtp.c -lpcap -logg
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#endif
+
+#ifdef HAVE_PCAP
+#include <pcap.h>
+#endif
+#include <opus.h>
+#include <ogg/ogg.h>
+
+#define OPUS_PAYLOAD_TYPE 120
+
+/* state struct for passing around our handles */
+typedef struct {
+  ogg_stream_state *stream;
+  FILE *out;
+  int seq;
+  ogg_int64_t granulepos;
+  int linktype;
+} state;
+
+/* helper, write a little-endian 32 bit int to memory */
+void le32(unsigned char *p, int v)
+{
+  p[0] = v & 0xff;
+  p[1] = (v >> 8) & 0xff;
+  p[2] = (v >> 16) & 0xff;
+  p[3] = (v >> 24) & 0xff;
+}
+
+/* helper, write a little-endian 16 bit int to memory */
+void le16(unsigned char *p, int v)
+{
+  p[0] = v & 0xff;
+  p[1] = (v >> 8) & 0xff;
+}
+
+/* helper, write a big-endian 32 bit int to memory */
+void be32(unsigned char *p, int v)
+{
+  p[0] = (v >> 24) & 0xff;
+  p[1] = (v >> 16) & 0xff;
+  p[2] = (v >> 8) & 0xff;
+  p[3] = v & 0xff;
+}
+
+/* helper, write a big-endian 16 bit int to memory */
+void be16(unsigned char *p, int v)
+{
+  p[0] = (v >> 8) & 0xff;
+  p[1] = v & 0xff;
+}
+
+
+/* manufacture a generic OpusHead packet */
+ogg_packet *op_opushead(void)
+{
+  int size = 19;
+  unsigned char *data = malloc(size);
+  ogg_packet *op = malloc(sizeof(*op));
+
+  if (!data) {
+    fprintf(stderr, "Couldn't allocate data buffer.\n");
+    free(op);
+    return NULL;
+  }
+  if (!op) {
+    fprintf(stderr, "Couldn't allocate Ogg packet.\n");
+    free(data);
+    return NULL;
+  }
+
+  memcpy(data, "OpusHead", 8);  /* identifier */
+  data[8] = 1;                  /* version */
+  data[9] = 2;                  /* channels */
+  le16(data+10, 0);             /* pre-skip */
+  le32(data + 12, 48000);       /* original sample rate */
+  le16(data + 16, 0);           /* gain */
+  data[18] = 0;                 /* channel mapping family */
+
+  op->packet = data;
+  op->bytes = size;
+  op->b_o_s = 1;
+  op->e_o_s = 0;
+  op->granulepos = 0;
+  op->packetno = 0;
+
+  return op;
+}
+
+
+/* manufacture a generic OpusTags packet */
+ogg_packet *op_opustags(void)
+{
+  char *identifier = "OpusTags";
+  char *vendor = "opus rtp packet dump";
+  int size = strlen(identifier) + 4 + strlen(vendor) + 4;
+  unsigned char *data = malloc(size);
+  ogg_packet *op = malloc(sizeof(*op));
+
+  if (!data) {
+    fprintf(stderr, "Couldn't allocate data buffer.\n");
+    free(op);
+    return NULL;
+  }
+  if (!op) {
+    fprintf(stderr, "Couldn't allocate Ogg packet.\n");
+    free(data);
+    return NULL;
+  }
+
+  memcpy(data, identifier, 8);
+  le32(data + 8, strlen(vendor));
+  memcpy(data + 12, vendor, strlen(vendor));
+  le32(data + 12 + strlen(vendor), 0);
+
+  op->packet = data;
+  op->bytes = size;
+  op->b_o_s = 0;
+  op->e_o_s = 0;
+  op->granulepos = 0;
+  op->packetno = 1;
+
+  return op;
+}
+
+ogg_packet *op_from_pkt(const unsigned char *pkt, int len)
+{
+  ogg_packet *op = malloc(sizeof(*op));
+  if (!op) {
+    fprintf(stderr, "Couldn't allocate Ogg packet.\n");
+    return NULL;
+  }
+
+  op->packet = (unsigned char *)pkt;
+  op->bytes = len;
+  op->b_o_s = 0;
+  op->e_o_s = 0;
+
+  return op;
+}
+
+/* free a packet and its contents */
+void op_free(ogg_packet *op) {
+  if (op) {
+    if (op->packet) {
+      free(op->packet);
+    }
+    free(op);
+  }
+}
+
+/* check if an ogg page begins an opus stream */
+int is_opus(ogg_page *og)
+{
+  ogg_stream_state os;
+  ogg_packet op;
+
+  ogg_stream_init(&os, ogg_page_serialno(og));
+  ogg_stream_pagein(&os, og);
+  if (ogg_stream_packetout(&os, &op) == 1) {
+    if (op.bytes >= 19 && !memcmp(op.packet, "OpusHead", 8)) {
+      ogg_stream_clear(&os);
+      return 1;
+    }
+  }
+  ogg_stream_clear(&os);
+  return 0;
+}
+
+/* calculate the number of samples in an opus packet */
+int opus_samples(const unsigned char *packet, int size)
+{
+  /* number of samples per frame at 48 kHz */
+  int samples = opus_packet_get_samples_per_frame(packet, 48000);
+  /* number "frames" in this packet */
+  int frames = opus_packet_get_nb_frames(packet, size);
+
+  return samples*frames;
+}
+
+/* helper, write out available ogg pages */
+int ogg_write(state *params)
+{
+  ogg_page page;
+  size_t written;
+
+  if (!params || !params->stream || !params->out) {
+    return -1;
+  }
+
+  while (ogg_stream_pageout(params->stream, &page)) {
+    written = fwrite(page.header, 1, page.header_len, params->out);
+    if (written != (size_t)page.header_len) {
+      fprintf(stderr, "Error writing Ogg page header\n");
+      return -2;
+    }
+    written = fwrite(page.body, 1, page.body_len, params->out);
+    if (written != (size_t)page.body_len) {
+      fprintf(stderr, "Error writing Ogg page body\n");
+      return -3;
+    }
+  }
+
+  return 0;
+}
+
+/* helper, flush remaining ogg data */
+int ogg_flush(state *params)
+{
+  ogg_page page;
+  size_t written;
+
+  if (!params || !params->stream || !params->out) {
+    return -1;
+  }
+
+  while (ogg_stream_flush(params->stream, &page)) {
+    written = fwrite(page.header, 1, page.header_len, params->out);
+    if (written != (size_t)page.header_len) {
+      fprintf(stderr, "Error writing Ogg page header\n");
+      return -2;
+    }
+    written = fwrite(page.body, 1, page.body_len, params->out);
+    if (written != (size_t)page.body_len) {
+      fprintf(stderr, "Error writing Ogg page body\n");
+      return -3;
+    }
+  }
+
+  return 0;
+}
+
+#define ETH_HEADER_LEN 14
+typedef struct {
+  unsigned char src[6], dst[6]; /* ethernet MACs */
+  int type;
+} eth_header;
+
+#define LOOP_HEADER_LEN 4
+typedef struct {
+  int family;
+} loop_header;
+
+#define IP_HEADER_MIN 20
+typedef struct {
+  int version;
+  int header_size;
+  unsigned char src[4], dst[4]; /* ipv4 addrs */
+  int protocol;
+} ip_header;
+
+#define UDP_HEADER_LEN 8
+typedef struct {
+  int src, dst; /* ports */
+  int size, checksum;
+} udp_header;
+
+#define RTP_HEADER_MIN 12
+typedef struct {
+  int version;
+  int type;
+  int pad, ext, cc, mark;
+  int seq, time;
+  int ssrc;
+  int *csrc;
+  int header_size;
+  int payload_size;
+} rtp_header;
+
+/* helper, read a big-endian 16 bit int from memory */
+static int rbe16(const unsigned char *p)
+{
+  int v = p[0] << 8 | p[1];
+  return v;
+}
+
+/* helper, read a big-endian 32 bit int from memory */
+static int rbe32(const unsigned char *p)
+{
+  int v = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+  return v;
+}
+
+/* helper, read a native-endian 32 bit int from memory */
+static int rne32(const unsigned char *p)
+{
+  /* On x86 we could just cast, but that might not meet
+   * arm alignment requirements. */
+  int d = 0;
+  memcpy(&d, p, 4);
+  return d;
+}
+
+int parse_eth_header(const unsigned char *packet, int size, eth_header *eth)
+{
+  if (!packet || !eth) {
+    return -2;
+  }
+  if (size < ETH_HEADER_LEN) {
+    fprintf(stderr, "Packet too short for eth\n");
+    return -1;
+  }
+  memcpy(eth->src, packet + 0, 6);
+  memcpy(eth->dst, packet + 6, 6);
+  eth->type = rbe16(packet + 12);
+
+  return 0;
+}
+
+/* used by the darwin loopback interface, at least */
+int parse_loop_header(const unsigned char *packet, int size, loop_header *loop)
+{
+  if (!packet || !loop) {
+    return -2;
+  }
+  if (size < LOOP_HEADER_LEN) {
+    fprintf(stderr, "Packet too short for loopback\n");
+    return -1;
+  }
+  /* protocol is in host byte order on osx. may be big endian on openbsd? */
+  loop->family = rne32(packet);
+
+  return 0;
+}
+
+int parse_ip_header(const unsigned char *packet, int size, ip_header *ip)
+{
+  if (!packet || !ip) {
+    return -2;
+  }
+  if (size < IP_HEADER_MIN) {
+    fprintf(stderr, "Packet too short for ip\n");
+    return -1;
+  }
+
+  ip->version = (packet[0] >> 4) & 0x0f;
+  if (ip->version != 4) {
+    fprintf(stderr, "unhandled ip version %d\n", ip->version);
+    return 1;
+  }
+
+  /* ipv4 header */
+  ip->header_size = 4 * (packet[0] & 0x0f);
+  ip->protocol = packet[9];
+  memcpy(ip->src, packet + 12, 4);
+  memcpy(ip->dst, packet + 16, 4);
+
+  if (size < ip->header_size) {
+    fprintf(stderr, "Packet too short for ipv4 with options\n");
+    return -1;
+  }
+
+  return 0;
+}
+
+int parse_udp_header(const unsigned char *packet, int size, udp_header *udp)
+{
+  if (!packet || !udp) {
+    return -2;
+  }
+  if (size < UDP_HEADER_LEN) {
+    fprintf(stderr, "Packet too short for udp\n");
+    return -1;
+  }
+
+  udp->src = rbe16(packet);
+  udp->dst = rbe16(packet + 2);
+  udp->size = rbe16(packet + 4);
+  udp->checksum = rbe16(packet + 6);
+
+  return 0;
+}
+
+
+int parse_rtp_header(const unsigned char *packet, int size, rtp_header *rtp)
+{
+  if (!packet || !rtp) {
+    return -2;
+  }
+  if (size < RTP_HEADER_MIN) {
+    fprintf(stderr, "Packet too short for rtp\n");
+    return -1;
+  }
+  rtp->version = (packet[0] >> 6) & 3;
+  rtp->pad = (packet[0] >> 5) & 1;
+  rtp->ext = (packet[0] >> 4) & 1;
+  rtp->cc = packet[0] & 7;
+  rtp->header_size = 12 + 4 * rtp->cc;
+  rtp->payload_size = size - rtp->header_size;
+
+  rtp->mark = (packet[1] >> 7) & 1;
+  rtp->type = (packet[1]) & 127;
+  rtp->seq  = rbe16(packet + 2);
+  rtp->time = rbe32(packet + 4);
+  rtp->ssrc = rbe32(packet + 8);
+  rtp->csrc = NULL;
+  if (size < rtp->header_size) {
+    fprintf(stderr, "Packet too short for RTP header\n");
+    return -1;
+  }
+
+  return 0;
+}
+
+int serialize_rtp_header(unsigned char *packet, int size, rtp_header *rtp)
+{
+  int i;
+
+  if (!packet || !rtp) {
+    return -2;
+  }
+  if (size < RTP_HEADER_MIN) {
+    fprintf(stderr, "Packet buffer too short for RTP\n");
+    return -1;
+  }
+  if (size < rtp->header_size) {
+    fprintf(stderr, "Packet buffer too short for declared RTP header size\n");
+    return -3;
+  }
+  packet[0] = ((rtp->version & 3) << 6) |
+              ((rtp->pad & 1) << 5) |
+              ((rtp->ext & 1) << 4) |
+              ((rtp->cc & 7));
+  packet[1] = ((rtp->mark & 1) << 7) |
+              ((rtp->type & 127));
+  be16(packet+2, rtp->seq);
+  be32(packet+4, rtp->time);
+  be32(packet+8, rtp->ssrc);
+  if (rtp->cc && rtp->csrc) {
+    for (i = 0; i < rtp->cc; i++) {
+      be32(packet + 12 + i*4, rtp->csrc[i]);
+    }
+  }
+
+  return 0;
+}
+
+int update_rtp_header(rtp_header *rtp)
+{
+  rtp->header_size = 12 + 4 * rtp->cc;
+  return 0;
+}
+
+#ifndef _WIN32
+int send_rtp_packet(int fd, struct sockaddr *sin,
+    rtp_header *rtp, const unsigned char *opus)
+{
+  update_rtp_header(rtp);
+  unsigned char *packet = malloc(rtp->header_size + rtp->payload_size);
+  int ret;
+
+  if (!packet) {
+    fprintf(stderr, "Couldn't allocate packet buffer\n");
+    return -1;
+  }
+  serialize_rtp_header(packet, rtp->header_size, rtp);
+  memcpy(packet + rtp->header_size, opus, rtp->payload_size);
+  ret = sendto(fd, packet, rtp->header_size + rtp->payload_size, 0,
+      sin, sizeof(*sin));
+  if (ret < 0) {
+    fprintf(stderr, "error sending: %s\n", strerror(errno));
+  }
+  free(packet);
+
+  return ret;
+}
+
+int rtp_send_file(const char *filename, const char *dest, int port)
+{
+  rtp_header rtp;
+  int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  struct sockaddr_in sin;
+  int optval = 0;
+  int ret;
+
+  if (fd < 0) {
+    fprintf(stderr, "Couldn't create socket\n");
+    return fd;
+  }
+  sin.sin_family = AF_INET;
+  sin.sin_port = htons(port);
+  if ((sin.sin_addr.s_addr = inet_addr(dest)) == INADDR_NONE) {
+    fprintf(stderr, "Invalid address %s\n", dest);
+    return -1;
+  }
+
+  ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
+  if (ret < 0) {
+    fprintf(stderr, "Couldn't set socket options\n");
+    return ret;
+  }
+
+  rtp.version = 2;
+  rtp.type = OPUS_PAYLOAD_TYPE;
+  rtp.pad = 0;
+  rtp.ext = 0;
+  rtp.cc = 0;
+  rtp.mark = 0;
+  rtp.seq = rand();
+  rtp.time = rand();
+  rtp.ssrc = rand();
+  rtp.csrc = NULL;
+  rtp.header_size = 0;
+  rtp.payload_size = 0;
+
+  fprintf(stderr, "Sending %s...\n", filename);
+  FILE *in = fopen(filename, "rb");
+  ogg_sync_state oy;
+  ogg_stream_state os;
+  ogg_page og;
+  ogg_packet op;
+  int headers = 0;
+  char *in_data;
+  const long in_size = 8192;
+  size_t in_read;
+
+  if (!in) {
+    fprintf(stderr, "Couldn't open input file '%s'\n", filename);
+    return -1;
+  }
+  ret = ogg_sync_init(&oy);
+  if (ret < 0) {
+    fprintf(stderr, "Couldn't initialize Ogg sync state\n");
+    return ret;
+  }
+  while (!feof(in)) {
+  in_data = ogg_sync_buffer(&oy, in_size);
+  if (!in_data) {
+    fprintf(stderr, "ogg_sync_buffer failed\n");
+    return -1;
+  }
+  in_read = fread(in_data, 1, in_size, in);
+  ret = ogg_sync_wrote(&oy, in_read);
+  if (ret < 0) {
+    fprintf(stderr, "ogg_sync_wrote failed\n");
+    return ret;
+  }
+  while (ogg_sync_pageout(&oy, &og) == 1) {
+    if (headers == 0) {
+      if (is_opus(&og)) {
+        /* this is the start of an Opus stream */
+        ret = ogg_stream_init(&os, ogg_page_serialno(&og));
+        if (ret < 0) {
+          fprintf(stderr, "ogg_stream_init failed\n");
+          return ret;
+        }
+        headers++;
+      } else if (!ogg_page_bos(&og)) {
+        /* We're past the header and haven't found an Opus stream.
+         * Time to give up. */
+        return 1;
+      } else {
+        /* try again */
+        continue;
+      }
+    }
+    /* submit the page for packetization */
+    ret = ogg_stream_pagein(&os, &og);
+    if (ret < 0) {
+      fprintf(stderr, "ogg_stream_pagein failed\n");
+      return ret;
+    }
+    /* read and process available packets */
+    while (ogg_stream_packetout(&os,&op) == 1) {
+      int samples;
+      /* skip header packets */
+      if (headers == 1 && op.bytes >= 19 && !memcmp(op.packet, "OpusHead", 8)) {
+        headers++;
+        continue;
+      }
+      if (headers == 2 && op.bytes >= 16 && !memcmp(op.packet, "OpusTags", 8)) {
+        headers++;
+        continue;
+      }
+      /* get packet duration */
+      samples = opus_samples(op.packet, op.bytes);
+      /* update the rtp header and send */
+      rtp.seq++;
+      rtp.time += samples;
+      rtp.payload_size = op.bytes;
+      fprintf(stderr, "rtp %d %d %d %3d ms %5d bytes\n",
+          rtp.type, rtp.seq, rtp.time, samples/48, rtp.payload_size);
+      send_rtp_packet(fd, (struct sockaddr *)&sin, &rtp, op.packet);
+      usleep(samples*1000/48);
+    }
+  }
+  }
+
+  if (headers > 0)
+    ogg_stream_clear(&os);
+  ogg_sync_clear(&oy);
+  fclose(in);
+  return 0;
+}
+#else /* _WIN32 */
+int rtp_send_file(const char *filename, const char *addr, int port)
+{
+  fprintf(stderr, "Cannot send '%s to %s:%d'. Socket support not available.\n",
+          filename, addr, port);
+  return -2;
+}
+#endif
+
+
+#ifdef HAVE_PCAP
+/* pcap 'got_packet' callback */
+void write_packet(u_char *args, const struct pcap_pkthdr *header,
+                  const u_char *data)
+{
+  state *params = (state *)args;
+  const unsigned char *packet;
+  int size;
+  eth_header eth;
+  loop_header loop;
+  ip_header ip;
+  udp_header udp;
+  rtp_header rtp;
+
+  fprintf(stderr, "Got %d byte packet (%d bytes captured)\n",
+          header->len, header->caplen);
+  packet = data;
+  size = header->caplen;
+
+  /* parse the link-layer header */
+  switch (params->linktype) {
+    case DLT_EN10MB:
+      if (parse_eth_header(packet, size, &eth)) {
+        fprintf(stderr, "error parsing eth header\n");
+        return;
+      }
+      fprintf(stderr, "  eth 0x%04x", eth.type);
+      fprintf(stderr, " %02x:%02x:%02x:%02x:%02x:%02x ->",
+              eth.src[0], eth.src[1], eth.src[2],
+              eth.src[3], eth.src[4], eth.src[5]);
+      fprintf(stderr, " %02x:%02x:%02x:%02x:%02x:%02x\n",
+              eth.dst[0], eth.dst[1], eth.dst[2],
+              eth.dst[3], eth.dst[4], eth.dst[5]);
+      if (eth.type != 0x0800) {
+        fprintf(stderr, "skipping packet: no IPv4\n");
+        return;
+      }
+      packet += ETH_HEADER_LEN;
+      size -= ETH_HEADER_LEN;
+      break;
+    case DLT_NULL:
+      if (parse_loop_header(packet, size, &loop)) {
+        fprintf(stderr, "error parsing loopback header\n");
+        return;
+      }
+      fprintf(stderr, " loopback family %d\n", loop.family);
+      if (loop.family != PF_INET) {
+        fprintf(stderr, "skipping packet: not IP\n");
+        return;
+      }
+      packet += LOOP_HEADER_LEN;
+      size -= LOOP_HEADER_LEN;
+      break;
+    default:
+      fprintf(stderr, "skipping packet: unrecognized linktype %d\n",
+          params->linktype);
+      return;
+  }
+
+  if (parse_ip_header(packet, size, &ip)) {
+    fprintf(stderr, "error parsing ip header\n");
+    return;
+  }
+  fprintf(stderr, " ipv%d protocol %d", ip.version, ip.protocol);
+  fprintf(stderr, " %d.%d.%d.%d ->",
+          ip.src[0], ip.src[1], ip.src[2], ip.src[3]);
+  fprintf(stderr, " %d.%d.%d.%d",
+          ip.dst[0], ip.dst[1], ip.dst[2], ip.dst[3]);
+  fprintf(stderr, " header %d bytes\n", ip.header_size);
+  if (ip.protocol != 17) {
+    fprintf(stderr, "skipping packet: not UDP\n");
+    return;
+  }
+  packet += ip.header_size;
+  size -= ip.header_size;
+
+  if (parse_udp_header(packet, size, &udp)) {
+    fprintf(stderr, "error parsing udp header\n");
+    return;
+  }
+  fprintf(stderr, "  udp %d bytes %d -> %d crc 0x%04x\n",
+          udp.size, udp.src, udp.dst, udp.checksum);
+  packet += UDP_HEADER_LEN;
+  size -= UDP_HEADER_LEN;
+
+  if (parse_rtp_header(packet, size, &rtp)) {
+    fprintf(stderr, "error parsing rtp header\n");
+    return;
+  }
+  fprintf(stderr, "  rtp 0x%08x %d %d %d",
+          rtp.ssrc, rtp.type, rtp.seq, rtp.time);
+  fprintf(stderr, "  v%d %s%s%s CC %d", rtp.version,
+          rtp.pad ? "P":".", rtp.ext ? "X":".",
+          rtp.mark ? "M":".", rtp.cc);
+  fprintf(stderr, " %5d bytes\n", rtp.payload_size);
+
+  packet += rtp.header_size;
+  size -= rtp.header_size;
+
+  if (size < 0) {
+    fprintf(stderr, "skipping short packet\n");
+    return;
+  }
+
+  if (rtp.seq < params->seq) {
+    fprintf(stderr, "skipping out-of-sequence packet\n");
+    return;
+  }
+  params->seq = rtp.seq;
+
+  if (rtp.type != OPUS_PAYLOAD_TYPE) {
+    fprintf(stderr, "skipping non-opus packet\n");
+    return;
+  }
+
+  /* write the payload to our opus file */
+  ogg_packet *op = op_from_pkt(packet, size);
+  op->packetno = rtp.seq;
+  params->granulepos += opus_samples(packet, size);
+  op->granulepos = params->granulepos;
+  ogg_stream_packetin(params->stream, op);
+  free(op);
+  ogg_write(params);
+
+  if (size < rtp.payload_size) {
+    fprintf(stderr, "!! truncated %d uncaptured bytes\n",
+            rtp.payload_size - size);
+  }
+}
+
+/* use libpcap to capture packets and write them to a file */
+int sniff(char *device)
+{
+  state *params;
+  pcap_t *pcap;
+  char errbuf[PCAP_ERRBUF_SIZE];
+  ogg_packet *op;
+
+  if (!device) {
+    device = "lo";
+  }
+
+  /* set up */
+  pcap = pcap_open_live(device, 9600, 0, 1000, errbuf);
+  if (pcap == NULL) {
+    fprintf(stderr, "Couldn't open device %s: %s\n", device, errbuf);
+    return(2);
+  }
+  params = malloc(sizeof(state));
+  if (!params) {
+    fprintf(stderr, "Couldn't allocate param struct.\n");
+    return -1;
+  }
+  params->linktype = pcap_datalink(pcap);
+  params->stream = malloc(sizeof(ogg_stream_state));
+  if (!params->stream) {
+    fprintf(stderr, "Couldn't allocate stream struct.\n");
+    free(params);
+    return -1;
+  }
+  if (ogg_stream_init(params->stream, rand()) < 0) {
+    fprintf(stderr, "Couldn't initialize Ogg stream state.\n");
+    free(params->stream);
+    free(params);
+    return -1;
+  }
+  params->out = fopen("rtpdump.opus", "wb");
+  if (!params->out) {
+    fprintf(stderr, "Couldn't open output file.\n");
+    free(params->stream);
+    free(params);
+    return -2;
+  }
+  params->seq = 0;
+  params->granulepos = 0;
+
+  /* write stream headers */
+  op = op_opushead();
+  ogg_stream_packetin(params->stream, op);
+  op_free(op);
+  op = op_opustags();
+  ogg_stream_packetin(params->stream, op);
+  op_free(op);
+  ogg_flush(params);
+
+  /* start capture loop */
+  fprintf(stderr, "Capturing packets\n");
+  pcap_loop(pcap, 300, write_packet, (u_char *)params);
+
+  /* write outstanding data */
+  ogg_flush(params);
+
+  /* clean up */
+  fclose(params->out);
+  ogg_stream_destroy(params->stream);
+  free(params);
+  pcap_close(pcap);
+
+  return 0;
+}
+#endif /* HAVE_PCAP */
+
+void opustools_version(void)
+{
+  printf("opusrtp %s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+  printf("Copyright (C) 2012 Xiph.Org Foundation\n");
+}
+
+void usage(char *exe)
+{
+  printf("Usage: %s [--sniff] <file.opus> [<file2.opus>]\n", exe);
+  printf("\n");
+  printf("Sends and receives Opus audio RTP streams.\n");
+  printf("\nGeneral Options:\n");
+  printf(" -h, --help           This help\n");
+  printf(" -V, --version        Version information\n");
+  printf(" -q, --quiet          Suppress status output\n");
+  printf(" -d, --destination    Destination address (default 127.0.0.1)\n");
+  printf(" -p, --port           Destination port (default 1234)\n");
+  printf(" --sniff              Sniff and record Opus RTP streams\n");
+  printf("\n");
+  printf("By default, the given file(s) will be sent over RTP.\n");
+}
+
+int main(int argc, char *argv[])
+{
+  int option, i;
+  const char *dest = "127.0.0.1";
+  int port = 1234;
+  struct option long_options[] = {
+    {"help", no_argument, NULL, 'h'},
+    {"version", no_argument, NULL, 'V'},
+    {"quiet", no_argument, NULL, 'q'},
+    {"destination", required_argument, NULL, 'd'},
+    {"port", required_argument, NULL, 'p'},
+    {"sniff", no_argument, NULL, 0},
+    {0, 0, 0, 0}
+  };
+
+  /* process command line arguments */
+  while ((option = getopt_long(argc, argv, "hVqd:p:", long_options, &i)) != -1) {
+    switch (option) {
+      case 0:
+        if (!strcmp(long_options[i].name, "sniff")) {
+#ifdef HAVE_PCAP
+          sniff("lo0");
+          return 0;
+#else
+          fprintf(stderr, "pcap support disabled, sorry.\n");
+          return 1;
+#endif
+        } else {
+          fprintf(stderr, "Unknown option - try %s --help.\n", argv[0]);
+          return -1;
+        }
+        break;
+      case 'V':
+        opustools_version();
+        return 0;
+      case 'q':
+        break;
+      case 'd':
+        if (optarg)
+            dest = optarg;
+        break;
+      case 'p':
+        if (optarg)
+            port = atoi(optarg);
+        break;
+      case 'h':
+        usage(argv[0]);
+        return 0;
+      case '?':
+      default:
+        usage(argv[0]);
+        return 1;
+    }
+  }
+
+  for (i = optind; i < argc; i++) {
+    rtp_send_file(argv[i], dest, port);
+  }
+
+  return 0;
+}
diff --git a/src/os_support.h b/src/os_support.h
new file mode 100644
index 0000000..cfa086a
--- /dev/null
+++ b/src/os_support.h
@@ -0,0 +1,167 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+      
+   File: os_support.h
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+   1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+   2. 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.
+
+   3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 OS_SUPPORT_H
+#define OS_SUPPORT_H
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef OS_SUPPORT_CUSTOM
+#include "os_support_custom.h"
+#endif
+
+/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_free 
+    NOTE: speex_alloc needs to CLEAR THE MEMORY */
+#ifndef OVERRIDE_SPEEX_ALLOC
+static inline void *speex_alloc (int size)
+{
+   /* WARNING: this is not equivalent to malloc(). If you want to use malloc() 
+      or your own allocator, YOU NEED TO CLEAR THE MEMORY ALLOCATED. Otherwise
+      you will experience strange bugs */
+   return calloc(size,1);
+}
+#endif
+
+/** Same as speex_alloc, except that the area is only needed inside a Speex call (might cause problem with wideband though) */
+#ifndef OVERRIDE_SPEEX_ALLOC_SCRATCH
+static inline void *speex_alloc_scratch (int size)
+{
+   /* Scratch space doesn't need to be cleared */
+   return calloc(size,1);
+}
+#endif
+
+/** Speex wrapper for realloc. To do your own dynamic allocation, all you need to do is replace this function, speex_alloc and speex_free */
+#ifndef OVERRIDE_SPEEX_REALLOC
+static inline void *speex_realloc (void *ptr, int size)
+{
+   return realloc(ptr, size);
+}
+#endif
+
+/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_alloc */
+#ifndef OVERRIDE_SPEEX_FREE
+static inline void speex_free (void *ptr)
+{
+   free(ptr);
+}
+#endif
+
+/** Same as speex_free, except that the area is only needed inside a Speex call (might cause problem with wideband though) */
+#ifndef OVERRIDE_SPEEX_FREE_SCRATCH
+static inline void speex_free_scratch (void *ptr)
+{
+   free(ptr);
+}
+#endif
+
+/** Copy n bytes of memory from src to dst. The 0* term provides compile-time type checking  */
+#ifndef OVERRIDE_SPEEX_COPY
+#define SPEEX_COPY(dst, src, n) (memcpy((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) ))
+#endif
+
+/** Copy n bytes of memory from src to dst, allowing overlapping regions. The 0* term 
+    provides compile-time type checking */
+#ifndef OVERRIDE_SPEEX_MOVE
+#define SPEEX_MOVE(dst, src, n) (memmove((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) ))
+#endif
+
+/** Set n bytes of memory to value of c, starting at address s */
+#ifndef OVERRIDE_SPEEX_MEMSET
+#define SPEEX_MEMSET(dst, c, n) (memset((dst), (c), (n)*sizeof(*(dst))))
+#endif
+
+
+#ifndef OVERRIDE_SPEEX_FATAL
+static inline void _speex_fatal(const char *str, const char *file, int line)
+{
+   fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str);
+   exit(1);
+}
+#endif
+
+#ifndef OVERRIDE_SPEEX_WARNING
+static inline void speex_warning(const char *str)
+{
+#ifndef DISABLE_WARNINGS
+   fprintf (stderr, "warning: %s\n", str);
+#endif
+}
+#endif
+
+#ifndef OVERRIDE_SPEEX_WARNING_INT
+static inline void speex_warning_int(const char *str, int val)
+{
+#ifndef DISABLE_WARNINGS
+   fprintf (stderr, "warning: %s %d\n", str, val);
+#endif
+}
+#endif
+
+#ifndef OVERRIDE_SPEEX_NOTIFY
+static inline void speex_notify(const char *str)
+{
+#ifndef DISABLE_NOTIFICATIONS
+   fprintf (stderr, "notification: %s\n", str);
+#endif
+}
+#endif
+
+#ifndef OVERRIDE_SPEEX_PUTC
+/** Speex wrapper for putc */
+static inline void _speex_putc(int ch, void *file)
+{
+   FILE *f = (FILE *)file;
+   fprintf(f, "%c", ch);
+}
+#endif
+
+#define speex_fatal(str) _speex_fatal(str, __FILE__, __LINE__);
+#define speex_assert(cond) {if (!(cond)) {speex_fatal("assertion failed: " #cond);}}
+
+#ifndef RELEASE
+static inline void print_vec(float *vec, int len, char *name)
+{
+   int i;
+   printf ("%s ", name);
+   for (i=0;i<len;i++)
+      printf (" %f", vec[i]);
+   printf ("\n");
+}
+#endif
+
+#endif
+
diff --git a/src/picture.c b/src/picture.c
new file mode 100644
index 0000000..bd12335
--- /dev/null
+++ b/src/picture.c
@@ -0,0 +1,499 @@
+/* Copyright (C)2007-2013 Xiph.Org Foundation
+   File: picture.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 <stdlib.h>
+#include <string.h>
+#include "picture.h"
+
+static const char BASE64_TABLE[64]={
+  'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+  'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+  'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+  'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
+};
+
+/*Utility function for base64 encoding METADATA_BLOCK_PICTURE tags.
+  Stores BASE64_LENGTH(len)+1 bytes in dst (including a terminating NUL).*/
+void base64_encode(char *dst, const char *src, int len){
+  unsigned s0;
+  unsigned s1;
+  unsigned s2;
+  int      ngroups;
+  int      i;
+  ngroups=len/3;
+  for(i=0;i<ngroups;i++){
+    s0=(unsigned char)src[3*i+0];
+    s1=(unsigned char)src[3*i+1];
+    s2=(unsigned char)src[3*i+2];
+    dst[4*i+0]=BASE64_TABLE[s0>>2];
+    dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4];
+    dst[4*i+2]=BASE64_TABLE[(s1&15)<<2|s2>>6];
+    dst[4*i+3]=BASE64_TABLE[s2&63];
+  }
+  len-=3*i;
+  if(len==1){
+    s0=(unsigned char)src[3*i+0];
+    dst[4*i+0]=BASE64_TABLE[s0>>2];
+    dst[4*i+1]=BASE64_TABLE[(s0&3)<<4];
+    dst[4*i+2]='=';
+    dst[4*i+3]='=';
+    i++;
+  }
+  else if(len==2){
+    s0=(unsigned char)src[3*i+0];
+    s1=(unsigned char)src[3*i+1];
+    dst[4*i+0]=BASE64_TABLE[s0>>2];
+    dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4];
+    dst[4*i+2]=BASE64_TABLE[(s1&15)<<2];
+    dst[4*i+3]='=';
+    i++;
+  }
+  dst[4*i]='\0';
+}
+
+/*A version of strncasecmp() that is guaranteed to only ignore the case of
+   ASCII characters.*/
+int oi_strncasecmp(const char *a, const char *b, int n){
+  int i;
+  for(i=0;i<n;i++){
+    int aval;
+    int bval;
+    int diff;
+    aval=a[i];
+    bval=b[i];
+    if(aval>='a'&&aval<='z') {
+      aval-='a'-'A';
+    }
+    if(bval>='a'&&bval<='z'){
+      bval-='a'-'A';
+    }
+    diff=aval-bval;
+    if(diff){
+      return diff;
+    }
+  }
+  return 0;
+}
+
+int is_jpeg(const unsigned char *buf, size_t length){
+  return length>=11&&memcmp(buf,"\xFF\xD8\xFF\xE0",4)==0
+   &&(buf[4]<<8|buf[5])>=16&&memcmp(buf+6,"JFIF",5)==0;
+}
+
+int is_png(const unsigned char *buf, size_t length){
+  return length>=8&&memcmp(buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0;
+}
+
+int is_gif(const unsigned char *buf, size_t length){
+  return length>=6
+   &&(memcmp(buf,"GIF87a",6)==0||memcmp(buf,"GIF89a",6)==0);
+}
+
+#define READ_U32_BE(buf) \
+    (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
+
+/*Tries to extract the width, height, bits per pixel, and palette size of a
+   PNG.
+  On failure, simply leaves its outputs unmodified.*/
+void extract_png_params(const unsigned char *data, size_t data_length,
+                        ogg_uint32_t *width, ogg_uint32_t *height,
+                        ogg_uint32_t *depth, ogg_uint32_t *colors,
+                        int *has_palette){
+  if(is_png(data,data_length)){
+    size_t offs;
+    offs=8;
+    while(data_length-offs>=12){
+      ogg_uint32_t chunk_len;
+      chunk_len=READ_U32_BE(data+offs);
+      if(chunk_len>data_length-(offs+12))break;
+      else if(chunk_len==13&&memcmp(data+offs+4,"IHDR",4)==0){
+        int color_type;
+        *width=READ_U32_BE(data+offs+8);
+        *height=READ_U32_BE(data+offs+12);
+        color_type=data[offs+17];
+        if(color_type==3){
+          *depth=24;
+          *has_palette=1;
+        }
+        else{
+          int sample_depth;
+          sample_depth=data[offs+16];
+          if(color_type==0)*depth=sample_depth;
+          else if(color_type==2)*depth=sample_depth*3;
+          else if(color_type==4)*depth=sample_depth*2;
+          else if(color_type==6)*depth=sample_depth*4;
+          *colors=0;
+          *has_palette=0;
+          break;
+        }
+      }
+      else if(*has_palette>0&&memcmp(data+offs+4,"PLTE",4)==0){
+        *colors=chunk_len/3;
+        break;
+      }
+      offs+=12+chunk_len;
+    }
+  }
+}
+
+/*Tries to extract the width, height, bits per pixel, and palette size of a
+   GIF.
+  On failure, simply leaves its outputs unmodified.*/
+void extract_gif_params(const unsigned char *data, size_t data_length,
+                        ogg_uint32_t *width, ogg_uint32_t *height,
+                        ogg_uint32_t *depth, ogg_uint32_t *colors,
+                        int *has_palette){
+  if(is_gif(data,data_length)&&data_length>=14){
+    *width=data[6]|data[7]<<8;
+    *height=data[8]|data[9]<<8;
+    /*libFLAC hard-codes the depth to 24.*/
+    *depth=24;
+    *colors=1<<((data[10]&7)+1);
+    *has_palette=1;
+  }
+}
+
+
+/*Tries to extract the width, height, bits per pixel, and palette size of a
+   JPEG.
+  On failure, simply leaves its outputs unmodified.*/
+void extract_jpeg_params(const unsigned char *data, size_t data_length,
+                         ogg_uint32_t *width, ogg_uint32_t *height,
+                         ogg_uint32_t *depth, ogg_uint32_t *colors,
+                         int *has_palette){
+  if(is_jpeg(data,data_length)){
+    size_t offs;
+    offs=2;
+    for(;;){
+      size_t segment_len;
+      int    marker;
+      while(offs<data_length&&data[offs]!=0xFF)offs++;
+      while(offs<data_length&&data[offs]==0xFF)offs++;
+      marker=data[offs];
+      offs++;
+      /*If we hit EOI* (end of image), or another SOI* (start of image),
+         or SOS (start of scan), then stop now.*/
+      if(offs>=data_length||(marker>=0xD8&&marker<=0xDA))break;
+      /*RST* (restart markers): skip (no segment length).*/
+      else if(marker>=0xD0&&marker<=0xD7)continue;
+      /*Read the length of the marker segment.*/
+      if(data_length-offs<2)break;
+      segment_len=data[offs]<<8|data[offs+1];
+      if(segment_len<2||data_length-offs<segment_len)break;
+      if(marker==0xC0||(marker>0xC0&&marker<0xD0&&(marker&3)!=0)){
+        /*Found a SOFn (start of frame) marker segment:*/
+        if(segment_len>=8){
+          *height=data[offs+3]<<8|data[offs+4];
+          *width=data[offs+5]<<8|data[offs+6];
+          *depth=data[offs+2]*data[offs+7];
+          *colors=0;
+          *has_palette=0;
+        }
+        break;
+      }
+      /*Other markers: skip the whole marker segment.*/
+      offs+=segment_len;
+    }
+  }
+}
+
+#define IMAX(a,b) ((a) > (b) ? (a) : (b))
+
+/*Parse a picture SPECIFICATION as given on the command-line.
+  spec: The specification.
+  error_message: Returns an error message on error.
+  seen_file_icons: Bit flags used to track if any pictures of type 1 or type 2
+   have already been added, to ensure only one is allowed.
+  Return: A Base64-encoded string suitable for use in a METADATA_BLOCK_PICTURE
+   tag.*/
+char *parse_picture_specification(const char *spec,
+                                  const char **error_message,
+                                  int *seen_file_icons){
+  FILE          *picture_file;
+  unsigned long  picture_type;
+  unsigned long  width;
+  unsigned long  height;
+  unsigned long  depth;
+  unsigned long  colors;
+  const char    *mime_type;
+  const char    *mime_type_end;
+  const char    *description;
+  const char    *description_end;
+  const char    *filename;
+  unsigned char *buf;
+  char          *out;
+  size_t         cbuf;
+  size_t         nbuf;
+  size_t         data_offset;
+  size_t         data_length;
+  size_t         b64_length;
+  int            is_url;
+  /*If a filename has a '|' in it, there's no way we can distinguish it from a
+     full specification just from the spec string.
+    Instead, try to open the file.
+    If it exists, the user probably meant the file.*/
+  picture_type=3;
+  width=height=depth=colors=0;
+  mime_type=mime_type_end=description=description_end=filename=spec;
+  is_url=0;
+  picture_file=fopen(filename,"rb");
+  if(picture_file==NULL&&strchr(spec,'|')){
+    const char *p;
+    char       *q;
+    /*We don't have a plain file, and there is a pipe character: assume it's
+       the full form of the specification.*/
+    picture_type=strtoul(spec,&q,10);
+    if(*q!='|'||picture_type>20){
+      *error_message="invalid picture type";
+      return NULL;
+    }
+    if(picture_type>=1&&picture_type<=2&&(*seen_file_icons&picture_type)){
+      *error_message=picture_type==1?
+       "only one picture of type 1 (32x32 icon) allowed":
+       "only one picture of type 2 (icon) allowed";
+      return NULL;
+    }
+    /*An empty field implies a default of 'Cover (front)'.*/
+    if(spec==q)picture_type=3;
+    mime_type=q+1;
+    mime_type_end=mime_type+strcspn(mime_type,"|");
+    if(*mime_type_end!='|'){
+      *error_message="invalid picture specification: not enough fields";
+      return NULL;
+    }
+    /*The mime type must be composed of ASCII printable characters 0x20-0x7E.*/
+    for(p=mime_type;p<mime_type_end;p++)if(*p<0x20||*p>0x7E){
+      *error_message="invalid characters in mime type";
+      return NULL;
+    }
+    is_url=mime_type_end-mime_type==3
+     &&strncmp("-->",mime_type,mime_type_end-mime_type)==0;
+    description=mime_type_end+1;
+    description_end=description+strcspn(description,"|");
+    if(*description_end!='|'){
+      *error_message="invalid picture specification: not enough fields";
+      return NULL;
+    }
+    p=description_end+1;
+    if(*p!='|'){
+      width=strtoul(p,&q,10);
+      if(*q!='x'){
+        *error_message=
+         "invalid picture specification: can't parse resolution/color field";
+        return NULL;
+      }
+      p=q+1;
+      height=strtoul(p,&q,10);
+      if(*q!='x'){
+        *error_message=
+         "invalid picture specification: can't parse resolution/color field";
+        return NULL;
+      }
+      p=q+1;
+      depth=strtoul(p,&q,10);
+      if(*q=='/'){
+        p=q+1;
+        colors=strtoul(p,&q,10);
+      }
+      if(*q!='|'){
+        *error_message=
+         "invalid picture specification: can't parse resolution/color field";
+        return NULL;
+      }
+      p=q;
+    }
+    filename=p+1;
+    if(!is_url)picture_file=fopen(filename,"rb");
+  }
+  /*Buffer size: 8 static 4-byte fields plus 2 dynamic fields, plus the
+     file/URL data.
+    We reserve at least 10 bytes for the mime type, in case we still need to
+     extract it from the file.*/
+  data_offset=32+(description_end-description)+IMAX(mime_type_end-mime_type,10);
+  buf=NULL;
+  if(is_url){
+    /*Easy case: just stick the URL at the end.
+      We don't do anything to verify it's a valid URL.*/
+    data_length=strlen(filename);
+    cbuf=nbuf=data_offset+data_length;
+    buf=(unsigned char *)malloc(cbuf);
+    memcpy(buf+data_offset,filename,data_length);
+  }
+  else{
+    ogg_uint32_t file_width;
+    ogg_uint32_t file_height;
+    ogg_uint32_t file_depth;
+    ogg_uint32_t file_colors;
+    int          has_palette;
+    /*Complicated case: we have a real file.
+      Read it in, attempt to parse the mime type and image dimensions if
+       necessary, and validate what the user passed in.*/
+    if(picture_file==NULL){
+      *error_message="error opening picture file";
+      return NULL;
+    }
+    nbuf=data_offset;
+    /*Add a reasonable starting image file size.*/
+    cbuf=data_offset+65536;
+    for(;;){
+      unsigned char *new_buf;
+      size_t         nread;
+      new_buf=realloc(buf,cbuf);
+      if(new_buf==NULL){
+        fclose(picture_file);
+        free(buf);
+        *error_message="insufficient memory";
+        return NULL;
+      }
+      buf=new_buf;
+      nread=fread(buf+nbuf,1,cbuf-nbuf,picture_file);
+      nbuf+=nread;
+      if(nbuf<cbuf){
+        int error;
+        error=ferror(picture_file);
+        fclose(picture_file);
+        if(error){
+          free(buf);
+          *error_message="error reading picture file";
+          return NULL;
+        }
+        break;
+      }
+      if(cbuf==0xFFFFFFFF){
+        fclose(picture_file);
+        free(buf);
+        *error_message="file too large";
+        return NULL;
+      }
+      else if(cbuf>0x7FFFFFFFU)cbuf=0xFFFFFFFFU;
+      else cbuf=cbuf<<1|1;
+    }
+    data_length=nbuf-data_offset;
+    /*If there was no mimetype, try to extract it from the file data.*/
+    if(mime_type_end==mime_type){
+      if(is_jpeg(buf+data_offset,data_length)){
+        mime_type="image/jpeg";
+        mime_type_end=mime_type+10;
+      }
+      else if(is_png(buf+data_offset,data_length)){
+        mime_type="image/png";
+        mime_type_end=mime_type+9;
+      }
+      else if(is_gif(buf+data_offset,data_length)){
+        mime_type="image/gif";
+        mime_type_end=mime_type+9;
+      }
+      else{
+        free(buf);
+        *error_message="unable to guess MIME type from file, "
+         "must set it explicitly";
+        return NULL;
+      }
+    }
+    /*Try to extract the image dimensions/color information from the file.*/
+    file_width=file_height=file_depth=file_colors=0;
+    has_palette=-1;
+    if(mime_type_end-mime_type==9
+     &&oi_strncasecmp("image/png",mime_type,mime_type_end-mime_type)==0){
+      extract_png_params(buf+data_offset,data_length,
+       &file_width,&file_height,&file_depth,&file_colors,&has_palette);
+    }
+    else if(mime_type_end-mime_type==9
+     &&oi_strncasecmp("image/gif",mime_type,mime_type_end-mime_type)==0){
+      extract_gif_params(buf+data_offset,data_length,
+       &file_width,&file_height,&file_depth,&file_colors,&has_palette);
+    }
+    else if(mime_type_end-mime_type==10
+     &&oi_strncasecmp("image/jpeg",mime_type,mime_type_end-mime_type)==0){
+      extract_jpeg_params(buf+data_offset,data_length,
+       &file_width,&file_height,&file_depth,&file_colors,&has_palette);
+    }
+    if(!width)width=file_width;
+    if(!height)height=file_height;
+    if(!depth)depth=file_depth;
+    if(!colors)colors=file_colors;
+    if((file_width&&width!=file_width)
+     ||(file_height&&height!=file_height)
+     ||(file_depth&&depth!=file_depth)
+     /*We use has_palette to ensure we also reject non-0 user color counts for
+        images we've positively identified as non-paletted.*/
+     ||(has_palette>=0&&colors!=file_colors)){
+      free(buf);
+      *error_message="invalid picture specification: "
+       "resolution/color field does not match file";
+      return NULL;
+    }
+  }
+  /*These fields MUST be set correctly OR all set to zero.
+    So if any of them (except colors, for which 0 is a valid value) are still
+     zero, clear the rest to zero.*/
+  if(width==0||height==0||depth==0)width=height=depth=colors=0;
+  if(picture_type==1&&(width!=32||height!=32
+   ||mime_type_end-mime_type!=9
+   ||oi_strncasecmp("image/png",mime_type,mime_type_end-mime_type)!=0)){
+    free(buf);
+    *error_message="pictures of type 1 MUST be 32x32 PNGs";
+    return NULL;
+  }
+  /*Build the METADATA_BLOCK_PICTURE buffer.
+    We do this backwards from data_offset, because we didn't necessarily know
+     how big the mime type string was before we read the data in.*/
+  data_offset-=4;
+  WRITE_U32_BE(buf+data_offset,(unsigned long)data_length);
+  data_offset-=4;
+  WRITE_U32_BE(buf+data_offset,colors);
+  data_offset-=4;
+  WRITE_U32_BE(buf+data_offset,depth);
+  data_offset-=4;
+  WRITE_U32_BE(buf+data_offset,height);
+  data_offset-=4;
+  WRITE_U32_BE(buf+data_offset,width);
+  data_offset-=description_end-description;
+  memcpy(buf+data_offset,description,description_end-description);
+  data_offset-=4;
+  WRITE_U32_BE(buf+data_offset,(unsigned long)(description_end-description));
+  data_offset-=mime_type_end-mime_type;
+  memcpy(buf+data_offset,mime_type,mime_type_end-mime_type);
+  data_offset-=4;
+  WRITE_U32_BE(buf+data_offset,(unsigned long)(mime_type_end-mime_type));
+  data_offset-=4;
+  WRITE_U32_BE(buf+data_offset,picture_type);
+  data_length=nbuf-data_offset;
+  b64_length=BASE64_LENGTH(data_length);
+  out=(char *)malloc(b64_length+1);
+  if(out!=NULL){
+    base64_encode(out,(char *)buf+data_offset,data_length);
+    if(picture_type>=1&&picture_type<=2)*seen_file_icons|=picture_type;
+  }
+  free(buf);
+  return out;
+}
diff --git a/src/picture.h b/src/picture.h
new file mode 100644
index 0000000..bd48d54
--- /dev/null
+++ b/src/picture.h
@@ -0,0 +1,50 @@
+#ifndef __PICTURE_H
+#define __PICTURE_H
+
+#include <ogg/ogg.h>
+
+typedef enum{
+  PIC_FORMAT_JPEG,
+  PIC_FORMAT_PNG,
+  PIC_FORMAT_GIF
+}picture_format;
+
+#define BASE64_LENGTH(len) (((len)+2)/3*4)
+
+/*Utility function for base64 encoding METADATA_BLOCK_PICTURE tags.
+  Stores BASE64_LENGTH(len)+1 bytes in dst (including a terminating NUL).*/
+void base64_encode(char *dst, const char *src, int len);
+
+int oi_strncasecmp(const char *a, const char *b, int n);
+
+int is_jpeg(const unsigned char *buf, size_t length);
+int is_png(const unsigned char *buf, size_t length);
+int is_gif(const unsigned char *buf, size_t length);
+
+void extract_png_params(const unsigned char *data, size_t data_length,
+                        ogg_uint32_t *width, ogg_uint32_t *height,
+                        ogg_uint32_t *depth, ogg_uint32_t *colors,
+                        int *has_palette);
+void extract_gif_params(const unsigned char *data, size_t data_length,
+                        ogg_uint32_t *width, ogg_uint32_t *height,
+                        ogg_uint32_t *depth, ogg_uint32_t *colors,
+                        int *has_palette);
+void extract_jpeg_params(const unsigned char *data, size_t data_length,
+                         ogg_uint32_t *width, ogg_uint32_t *height,
+                         ogg_uint32_t *depth, ogg_uint32_t *colors,
+                         int *has_palette);
+
+char *parse_picture_specification(const char *spec,
+                                  const char **error_message,
+                                  int *seen_file_icons);
+
+#define WRITE_U32_BE(buf, val) \
+  do{ \
+    (buf)[0]=(unsigned char)((val)>>24); \
+    (buf)[1]=(unsigned char)((val)>>16); \
+    (buf)[2]=(unsigned char)((val)>>8); \
+    (buf)[3]=(unsigned char)(val); \
+  } \
+  while(0);
+
+#endif /* __PICTURE_H */
diff --git a/src/resample.c b/src/resample.c
new file mode 100644
index 0000000..d957090
--- /dev/null
+++ b/src/resample.c
@@ -0,0 +1,1147 @@
+/* Copyright (C) 2007-2008 Jean-Marc Valin
+   Copyright (C) 2008      Thorvald Natvig
+      
+   File: resample.c
+   Arbitrary resampling code
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+   1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+   2. 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.
+
+   3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+/*
+   The design goals of this code are:
+      - Very fast algorithm
+      - SIMD-friendly algorithm
+      - Low memory requirement
+      - Good *perceptual* quality (and not best SNR)
+
+   Warning: This resampler is relatively new. Although I think I got rid of 
+   all the major bugs and I don't expect the API to change anymore, there
+   may be something I've missed. So use with caution.
+
+   This algorithm is based on this original resampling algorithm:
+   Smith, Julius O. Digital Audio Resampling Home Page
+   Center for Computer Research in Music and Acoustics (CCRMA), 
+   Stanford University, 2007.
+   Web published at http://www-ccrma.stanford.edu/~jos/resample/.
+
+   There is one main difference, though. This resampler uses cubic 
+   interpolation instead of linear interpolation in the above paper. This
+   makes the table much smaller and makes it possible to compute that table
+   on a per-stream basis. In turn, being able to tweak the table for each 
+   stream makes it possible to both reduce complexity on simple ratios 
+   (e.g. 2/3), and get rid of the rounding operations in the inner loop. 
+   The latter both reduces CPU time and makes the algorithm more SIMD-friendly.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define RESAMPLE_HUGEMEM 1
+
+#ifdef OUTSIDE_SPEEX
+#include <stdlib.h>
+static void *speex_alloc (int size) {return calloc(size,1);}
+static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);}
+static void speex_free (void *ptr) {free(ptr);}
+#include "speex_resampler.h"
+#include "arch.h"
+#else /* OUTSIDE_SPEEX */
+               
+#include "../include/speex/speex_resampler.h"
+#include "arch.h"
+#include "os_support.h"
+#endif /* OUTSIDE_SPEEX */
+
+#include "stack_alloc.h"
+#include <math.h>
+
+#ifndef M_PI
+#define M_PI 3.14159263
+#endif
+
+#ifdef FIXED_POINT
+#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))  
+#else
+#define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x))))  
+#endif
+               
+#define IMAX(a,b) ((a) > (b) ? (a) : (b))
+#define IMIN(a,b) ((a) < (b) ? (a) : (b))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#if defined(FLOATING_POINT) && defined(__SSE__)
+# include "resample_sse.h"
+#endif
+
+/* Numer of elements to allocate on the stack */
+#ifdef VAR_ARRAYS
+#define FIXED_STACK_ALLOC 8192
+#else
+#define FIXED_STACK_ALLOC 1024
+#endif
+
+typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *);
+
+struct SpeexResamplerState_ {
+   spx_uint32_t in_rate;
+   spx_uint32_t out_rate;
+   spx_uint32_t num_rate;
+   spx_uint32_t den_rate;
+   
+   int    quality;
+   spx_uint32_t nb_channels;
+   spx_uint32_t filt_len;
+   spx_uint32_t mem_alloc_size;
+   spx_uint32_t buffer_size;
+   int          int_advance;
+   int          frac_advance;
+   float  cutoff;
+   spx_uint32_t oversample;
+   int          initialised;
+   int          started;
+   
+   /* These are per-channel */
+   spx_int32_t  *last_sample;
+   spx_uint32_t *samp_frac_num;
+   spx_uint32_t *magic_samples;
+   
+   spx_word16_t *mem;
+   spx_word16_t *sinc_table;
+   spx_uint32_t sinc_table_length;
+   resampler_basic_func resampler_ptr;
+         
+   int    in_stride;
+   int    out_stride;
+} ;
+
+static double kaiser12_table[68] = {
+   0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076,
+   0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014,
+   0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601,
+   0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014,
+   0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490,
+   0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546,
+   0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178,
+   0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947,
+   0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058,
+   0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438,
+   0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734,
+   0.00001000, 0.00000000};
+/*
+static double kaiser12_table[36] = {
+   0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741,
+   0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762,
+   0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274,
+   0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466,
+   0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291,
+   0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000};
+*/
+static double kaiser10_table[36] = {
+   0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446,
+   0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347,
+   0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962,
+   0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451,
+   0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739,
+   0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000};
+
+static double kaiser8_table[36] = {
+   0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200,
+   0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126,
+   0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272,
+   0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758,
+   0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490,
+   0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000};
+   
+static double kaiser6_table[36] = {
+   0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003,
+   0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565,
+   0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561,
+   0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058,
+   0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600,
+   0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000};
+
+struct FuncDef {
+   double *table;
+   int oversample;
+};
+      
+static struct FuncDef _KAISER12 = {kaiser12_table, 64};
+#define KAISER12 (&_KAISER12)
+/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
+#define KAISER12 (&_KAISER12)*/
+static struct FuncDef _KAISER10 = {kaiser10_table, 32};
+#define KAISER10 (&_KAISER10)
+static struct FuncDef _KAISER8 = {kaiser8_table, 32};
+#define KAISER8 (&_KAISER8)
+static struct FuncDef _KAISER6 = {kaiser6_table, 32};
+#define KAISER6 (&_KAISER6)
+
+struct QualityMapping {
+   int base_length;
+   int oversample;
+   float downsample_bandwidth;
+   float upsample_bandwidth;
+   struct FuncDef *window_func;
+};
+
+
+/* This table maps conversion quality to internal parameters. There are two
+   reasons that explain why the up-sampling bandwidth is larger than the 
+   down-sampling bandwidth:
+   1) When up-sampling, we can assume that the spectrum is already attenuated
+      close to the Nyquist rate (from an A/D or a previous resampling filter)
+   2) Any aliasing that occurs very close to the Nyquist rate will be masked
+      by the sinusoids/noise just below the Nyquist rate (guaranteed only for
+      up-sampling).
+*/
+static const struct QualityMapping quality_map[11] = {
+   {  8,  4, 0.830f, 0.860f, KAISER6 }, /* Q0 */
+   { 16,  4, 0.850f, 0.880f, KAISER6 }, /* Q1 */
+   { 32,  4, 0.882f, 0.910f, KAISER6 }, /* Q2 */  /* 82.3% cutoff ( ~60 dB stop) 6  */
+   { 48,  8, 0.895f, 0.917f, KAISER8 }, /* Q3 */  /* 84.9% cutoff ( ~80 dB stop) 8  */
+   { 64,  8, 0.921f, 0.940f, KAISER8 }, /* Q4 */  /* 88.7% cutoff ( ~80 dB stop) 8  */
+   { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */  /* 89.1% cutoff (~100 dB stop) 10 */
+   { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */  /* 91.5% cutoff (~100 dB stop) 10 */
+   {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */  /* 93.1% cutoff (~100 dB stop) 10 */
+   {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */  /* 94.5% cutoff (~100 dB stop) 10 */
+   {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */  /* 95.5% cutoff (~100 dB stop) 10 */
+   {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */
+};
+/*8,24,40,56,80,104,128,160,200,256,320*/
+static double compute_func(float x, struct FuncDef *func)
+{
+   float y, frac;
+   double interp[4];
+   int ind; 
+   y = x*func->oversample;
+   ind = (int)floor(y);
+   frac = (y-ind);
+   /* CSE with handle the repeated powers */
+   interp[3] =  -0.1666666667*frac + 0.1666666667*(frac*frac*frac);
+   interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac);
+   /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
+   interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac);
+   /* Just to make sure we don't have rounding problems */
+   interp[1] = 1.f-interp[3]-interp[2]-interp[0];
+   
+   /*sum = frac*accum[1] + (1-frac)*accum[2];*/
+   return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3];
+}
+
+#if 0
+#include <stdio.h>
+int main(int argc, char **argv)
+{
+   int i;
+   for (i=0;i<256;i++)
+   {
+      printf ("%f\n", compute_func(i/256., KAISER12));
+   }
+   return 0;
+}
+#endif
+
+#ifdef FIXED_POINT
+/* The slow way of computing a sinc for the table. Should improve that some day */
+static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func)
+{
+   /*fprintf (stderr, "%f ", x);*/
+   float xx = x * cutoff;
+   if (fabs(x)<1e-6f)
+      return WORD2INT(32768.*cutoff);
+   else if (fabs(x) > .5f*N)
+      return 0;
+   /*FIXME: Can it really be any slower than this? */
+   return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func));
+}
+#else
+/* The slow way of computing a sinc for the table. Should improve that some day */
+static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func)
+{
+   /*fprintf (stderr, "%f ", x);*/
+   float xx = x * cutoff;
+   if (fabs(x)<1e-6)
+      return cutoff;
+   else if (fabs(x) > .5*N)
+      return 0;
+   /*FIXME: Can it really be any slower than this? */
+   return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func);
+}
+#endif
+
+#ifdef FIXED_POINT
+static void cubic_coef(spx_word16_t x, spx_word16_t interp[4])
+{
+   /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
+   but I know it's MMSE-optimal on a sinc */
+   spx_word16_t x2, x3;
+   x2 = MULT16_16_P15(x, x);
+   x3 = MULT16_16_P15(x, x2);
+   interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15);
+   interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1));
+   interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15);
+   /* Just to make sure we don't have rounding problems */
+   interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3];
+   if (interp[2]<32767)
+      interp[2]+=1;
+}
+#else
+static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4])
+{
+   /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
+   but I know it's MMSE-optimal on a sinc */
+   interp[0] =  -0.16667f*frac + 0.16667f*frac*frac*frac;
+   interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac;
+   /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
+   interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac;
+   /* Just to make sure we don't have rounding problems */
+   interp[2] = 1.-interp[0]-interp[1]-interp[3];
+}
+#endif
+
+static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+   const int N = st->filt_len;
+   int out_sample = 0;
+   int last_sample = st->last_sample[channel_index];
+   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+   const spx_word16_t *sinc_table = st->sinc_table;
+   const int out_stride = st->out_stride;
+   const int int_advance = st->int_advance;
+   const int frac_advance = st->frac_advance;
+   const spx_uint32_t den_rate = st->den_rate;
+   spx_word32_t sum;
+
+   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+   {
+      const spx_word16_t *sinct = & sinc_table[samp_frac_num*N];
+      const spx_word16_t *iptr = & in[last_sample];
+
+#ifndef OVERRIDE_INNER_PRODUCT_SINGLE
+      int j;
+      sum = 0;
+      for(j=0;j<N;j++) sum += MULT16_16(sinct[j], iptr[j]);
+
+/*    This code is slower on most DSPs which have only 2 accumulators.
+      Plus this this forces truncation to 32 bits and you lose the HW guard bits.
+      I think we can trust the compiler and let it vectorize and/or unroll itself.
+      spx_word32_t accum[4] = {0,0,0,0};
+      for(j=0;j<N;j+=4) {
+        accum[0] += MULT16_16(sinct[j], iptr[j]);
+        accum[1] += MULT16_16(sinct[j+1], iptr[j+1]);
+        accum[2] += MULT16_16(sinct[j+2], iptr[j+2]);
+        accum[3] += MULT16_16(sinct[j+3], iptr[j+3]);
+      }
+      sum = accum[0] + accum[1] + accum[2] + accum[3];
+*/
+#else
+      sum = inner_product_single(sinct, iptr, N);
+#endif
+
+      out[out_stride * out_sample++] = SATURATE32(PSHR32(sum, 15), 32767);
+      last_sample += int_advance;
+      samp_frac_num += frac_advance;
+      if (samp_frac_num >= den_rate)
+      {
+         samp_frac_num -= den_rate;
+         last_sample++;
+      }
+   }
+
+   st->last_sample[channel_index] = last_sample;
+   st->samp_frac_num[channel_index] = samp_frac_num;
+   return out_sample;
+}
+
+#ifdef FIXED_POINT
+#else
+/* This is the same as the previous function, except with a double-precision accumulator */
+static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+   const int N = st->filt_len;
+   int out_sample = 0;
+   int last_sample = st->last_sample[channel_index];
+   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+   const spx_word16_t *sinc_table = st->sinc_table;
+   const int out_stride = st->out_stride;
+   const int int_advance = st->int_advance;
+   const int frac_advance = st->frac_advance;
+   const spx_uint32_t den_rate = st->den_rate;
+   double sum;
+
+   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+   {
+      const spx_word16_t *sinct = & sinc_table[samp_frac_num*N];
+      const spx_word16_t *iptr = & in[last_sample];
+
+#ifndef OVERRIDE_INNER_PRODUCT_DOUBLE
+      int j;
+      double accum[4] = {0,0,0,0};
+
+      for(j=0;j<N;j+=4) {
+        accum[0] += sinct[j]*iptr[j];
+        accum[1] += sinct[j+1]*iptr[j+1];
+        accum[2] += sinct[j+2]*iptr[j+2];
+        accum[3] += sinct[j+3]*iptr[j+3];
+      }
+      sum = accum[0] + accum[1] + accum[2] + accum[3];
+#else
+      sum = inner_product_double(sinct, iptr, N);
+#endif
+
+      out[out_stride * out_sample++] = PSHR32(sum, 15);
+      last_sample += int_advance;
+      samp_frac_num += frac_advance;
+      if (samp_frac_num >= den_rate)
+      {
+         samp_frac_num -= den_rate;
+         last_sample++;
+      }
+   }
+
+   st->last_sample[channel_index] = last_sample;
+   st->samp_frac_num[channel_index] = samp_frac_num;
+   return out_sample;
+}
+#endif
+
+static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+   const int N = st->filt_len;
+   int out_sample = 0;
+   int last_sample = st->last_sample[channel_index];
+   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+   const int out_stride = st->out_stride;
+   const int int_advance = st->int_advance;
+   const int frac_advance = st->frac_advance;
+   const spx_uint32_t den_rate = st->den_rate;
+   spx_word32_t sum;
+
+   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+   {
+      const spx_word16_t *iptr = & in[last_sample];
+
+      const int offset = samp_frac_num*st->oversample/st->den_rate;
+#ifdef FIXED_POINT
+      const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate);
+#else
+      const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate;
+#endif
+      spx_word16_t interp[4];
+
+
+#ifndef OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
+      int j;
+      spx_word32_t accum[4] = {0,0,0,0};
+
+      for(j=0;j<N;j++) {
+        const spx_word16_t curr_in=iptr[j];
+        accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+        accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+        accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
+        accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+      }
+
+      cubic_coef(frac, interp);
+      sum = MULT16_32_Q15(interp[0],SHR32(accum[0], 1)) + MULT16_32_Q15(interp[1],SHR32(accum[1], 1)) + MULT16_32_Q15(interp[2],SHR32(accum[2], 1)) + MULT16_32_Q15(interp[3],SHR32(accum[3], 1));
+#else
+      cubic_coef(frac, interp);
+      sum = interpolate_product_single(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp);
+#endif
+      
+      out[out_stride * out_sample++] = SATURATE32(PSHR32(sum, 14), 32767);
+      last_sample += int_advance;
+      samp_frac_num += frac_advance;
+      if (samp_frac_num >= den_rate)
+      {
+         samp_frac_num -= den_rate;
+         last_sample++;
+      }
+   }
+
+   st->last_sample[channel_index] = last_sample;
+   st->samp_frac_num[channel_index] = samp_frac_num;
+   return out_sample;
+}
+
+#ifdef FIXED_POINT
+#else
+/* This is the same as the previous function, except with a double-precision accumulator */
+static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+   const int N = st->filt_len;
+   int out_sample = 0;
+   int last_sample = st->last_sample[channel_index];
+   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+   const int out_stride = st->out_stride;
+   const int int_advance = st->int_advance;
+   const int frac_advance = st->frac_advance;
+   const spx_uint32_t den_rate = st->den_rate;
+   spx_word32_t sum;
+
+   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+   {
+      const spx_word16_t *iptr = & in[last_sample];
+
+      const int offset = samp_frac_num*st->oversample/st->den_rate;
+#ifdef FIXED_POINT
+      const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate);
+#else
+      const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate;
+#endif
+      spx_word16_t interp[4];
+
+
+#ifndef OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
+      int j;
+      double accum[4] = {0,0,0,0};
+
+      for(j=0;j<N;j++) {
+        const double curr_in=iptr[j];
+        accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+        accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+        accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
+        accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+      }
+
+      cubic_coef(frac, interp);
+      sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
+#else
+      cubic_coef(frac, interp);
+      sum = interpolate_product_double(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp);
+#endif
+      
+      out[out_stride * out_sample++] = PSHR32(sum,15);
+      last_sample += int_advance;
+      samp_frac_num += frac_advance;
+      if (samp_frac_num >= den_rate)
+      {
+         samp_frac_num -= den_rate;
+         last_sample++;
+      }
+   }
+
+   st->last_sample[channel_index] = last_sample;
+   st->samp_frac_num[channel_index] = samp_frac_num;
+   return out_sample;
+}
+#endif
+
+static void update_filter(SpeexResamplerState *st)
+{
+   spx_uint32_t old_length;
+   
+   old_length = st->filt_len;
+   st->oversample = quality_map[st->quality].oversample;
+   st->filt_len = quality_map[st->quality].base_length;
+   
+   if (st->num_rate > st->den_rate)
+   {
+      /* down-sampling */
+      st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate;
+      /* FIXME: divide the numerator and denominator by a certain amount if they're too large */
+      st->filt_len = st->filt_len*st->num_rate / st->den_rate;
+      /* Round up to make sure we have a multiple of 8 */
+      st->filt_len = ((st->filt_len-1)&(~0x7))+8;
+      if (2*st->den_rate < st->num_rate)
+         st->oversample >>= 1;
+      if (4*st->den_rate < st->num_rate)
+         st->oversample >>= 1;
+      if (8*st->den_rate < st->num_rate)
+         st->oversample >>= 1;
+      if (16*st->den_rate < st->num_rate)
+         st->oversample >>= 1;
+      if (st->oversample < 1)
+         st->oversample = 1;
+   } else {
+      /* up-sampling */
+      st->cutoff = quality_map[st->quality].upsample_bandwidth;
+   }
+
+#ifdef RESAMPLE_HUGEMEM
+   if (st->den_rate <= 16*(st->oversample+8))
+#else
+   /* Choose the resampling type that requires the least amount of memory */
+   if (st->den_rate <= (st->oversample+8))
+#endif
+   {
+      spx_uint32_t i;
+      if (!st->sinc_table)
+         st->sinc_table = (spx_word16_t *)speex_alloc(st->filt_len*st->den_rate*sizeof(spx_word16_t));
+      else if (st->sinc_table_length < st->filt_len*st->den_rate)
+      {
+         st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,st->filt_len*st->den_rate*sizeof(spx_word16_t));
+         st->sinc_table_length = st->filt_len*st->den_rate;
+      }
+      for (i=0;i<st->den_rate;i++)
+      {
+         spx_int32_t j;
+         for (j=0;j<st->filt_len;j++)
+         {
+            st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func);
+         }
+      }
+#ifdef FIXED_POINT
+      st->resampler_ptr = resampler_basic_direct_single;
+#else
+      if (st->quality>8)
+         st->resampler_ptr = resampler_basic_direct_double;
+      else
+         st->resampler_ptr = resampler_basic_direct_single;
+#endif
+      /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/
+   } else {
+      spx_int32_t i;
+      if (!st->sinc_table)
+         st->sinc_table = (spx_word16_t *)speex_alloc((st->filt_len*st->oversample+8)*sizeof(spx_word16_t));
+      else if (st->sinc_table_length < st->filt_len*st->oversample+8)
+      {
+         st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,(st->filt_len*st->oversample+8)*sizeof(spx_word16_t));
+         st->sinc_table_length = st->filt_len*st->oversample+8;
+      }
+      for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++)
+         st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func);
+#ifdef FIXED_POINT
+      st->resampler_ptr = resampler_basic_interpolate_single;
+#else
+      if (st->quality>8)
+         st->resampler_ptr = resampler_basic_interpolate_double;
+      else
+         st->resampler_ptr = resampler_basic_interpolate_single;
+#endif
+      /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/
+   }
+   st->int_advance = st->num_rate/st->den_rate;
+   st->frac_advance = st->num_rate%st->den_rate;
+
+   
+   /* Here's the place where we update the filter memory to take into account
+      the change in filter length. It's probably the messiest part of the code
+      due to handling of lots of corner cases. */
+   if (!st->mem)
+   {
+      spx_uint32_t i;
+      st->mem_alloc_size = st->filt_len-1 + st->buffer_size;
+      st->mem = (spx_word16_t*)speex_alloc(st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t));
+      for (i=0;i<st->nb_channels*st->mem_alloc_size;i++)
+         st->mem[i] = 0;
+      /*speex_warning("init filter");*/
+   } else if (!st->started)
+   {
+      spx_uint32_t i;
+      st->mem_alloc_size = st->filt_len-1 + st->buffer_size;
+      st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t));
+      for (i=0;i<st->nb_channels*st->mem_alloc_size;i++)
+         st->mem[i] = 0;
+      /*speex_warning("reinit filter");*/
+   } else if (st->filt_len > old_length)
+   {
+      spx_int32_t i;
+      /* Increase the filter length */
+      /*speex_warning("increase filter size");*/
+      int old_alloc_size = st->mem_alloc_size;
+      if ((st->filt_len-1 + st->buffer_size) > st->mem_alloc_size)
+      {
+         st->mem_alloc_size = st->filt_len-1 + st->buffer_size;
+         st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t));
+      }
+      for (i=st->nb_channels-1;i>=0;i--)
+      {
+         spx_int32_t j;
+         spx_uint32_t olen = old_length;
+         /*if (st->magic_samples[i])*/
+         {
+            /* Try and remove the magic samples as if nothing had happened */
+            
+            /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
+            olen = old_length + 2*st->magic_samples[i];
+            for (j=old_length-2+st->magic_samples[i];j>=0;j--)
+               st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
+            for (j=0;j<st->magic_samples[i];j++)
+               st->mem[i*st->mem_alloc_size+j] = 0;
+            st->magic_samples[i] = 0;
+         }
+         if (st->filt_len > olen)
+         {
+            /* If the new filter length is still bigger than the "augmented" length */
+            /* Copy data going backward */
+            for (j=0;j<olen-1;j++)
+               st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
+            /* Then put zeros for lack of anything better */
+            for (;j<st->filt_len-1;j++)
+               st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
+            /* Adjust last_sample */
+            st->last_sample[i] += (st->filt_len - olen)/2;
+         } else {
+            /* Put back some of the magic! */
+            st->magic_samples[i] = (olen - st->filt_len)/2;
+            for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
+               st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
+         }
+      }
+   } else if (st->filt_len < old_length)
+   {
+      spx_uint32_t i;
+      /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic"
+         samples so they can be used directly as input the next time(s) */
+      for (i=0;i<st->nb_channels;i++)
+      {
+         spx_uint32_t j;
+         spx_uint32_t old_magic = st->magic_samples[i];
+         st->magic_samples[i] = (old_length - st->filt_len)/2;
+         /* We must copy some of the memory that's no longer used */
+         /* Copy data going backward */
+         for (j=0;j<st->filt_len-1+st->magic_samples[i]+old_magic;j++)
+            st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
+         st->magic_samples[i] += old_magic;
+      }
+   }
+
+}
+
+SPX_RESAMPLE_EXPORT SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
+{
+   return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err);
+}
+
+SPX_RESAMPLE_EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
+{
+   spx_uint32_t i;
+   SpeexResamplerState *st;
+   if (quality > 10 || quality < 0)
+   {
+      if (err)
+         *err = RESAMPLER_ERR_INVALID_ARG;
+      return NULL;
+   }
+   st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState));
+   st->initialised = 0;
+   st->started = 0;
+   st->in_rate = 0;
+   st->out_rate = 0;
+   st->num_rate = 0;
+   st->den_rate = 0;
+   st->quality = -1;
+   st->sinc_table_length = 0;
+   st->mem_alloc_size = 0;
+   st->filt_len = 0;
+   st->mem = 0;
+   st->resampler_ptr = 0;
+         
+   st->cutoff = 1.f;
+   st->nb_channels = nb_channels;
+   st->in_stride = 1;
+   st->out_stride = 1;
+   
+#ifdef FIXED_POINT
+   st->buffer_size = 160;
+#else
+   st->buffer_size = 160;
+#endif
+   
+   /* Per channel data */
+   st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(spx_int32_t));
+   st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t));
+   st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t));
+   for (i=0;i<nb_channels;i++)
+   {
+      st->last_sample[i] = 0;
+      st->magic_samples[i] = 0;
+      st->samp_frac_num[i] = 0;
+   }
+
+   speex_resampler_set_quality(st, quality);
+   speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate);
+
+   
+   update_filter(st);
+   
+   st->initialised = 1;
+   if (err)
+      *err = RESAMPLER_ERR_SUCCESS;
+
+   return st;
+}
+
+SPX_RESAMPLE_EXPORT void speex_resampler_destroy(SpeexResamplerState *st)
+{
+   speex_free(st->mem);
+   speex_free(st->sinc_table);
+   speex_free(st->last_sample);
+   speex_free(st->magic_samples);
+   speex_free(st->samp_frac_num);
+   speex_free(st);
+}
+
+static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+   int j=0;
+   const int N = st->filt_len;
+   int out_sample = 0;
+   spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size;
+   spx_uint32_t ilen;
+   
+   st->started = 1;
+   
+   /* Call the right resampler through the function ptr */
+   out_sample = st->resampler_ptr(st, channel_index, mem, in_len, out, out_len);
+   
+   if (st->last_sample[channel_index] < (spx_int32_t)*in_len)
+      *in_len = st->last_sample[channel_index];
+   *out_len = out_sample;
+   st->last_sample[channel_index] -= *in_len;
+   
+   ilen = *in_len;
+
+   for(j=0;j<N-1;++j)
+     mem[j] = mem[j+ilen];
+
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+static int speex_resampler_magic(SpeexResamplerState *st, spx_uint32_t channel_index, spx_word16_t **out, spx_uint32_t out_len) {
+   spx_uint32_t tmp_in_len = st->magic_samples[channel_index];
+   spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size;
+   const int N = st->filt_len;
+   
+   speex_resampler_process_native(st, channel_index, &tmp_in_len, *out, &out_len);
+
+   st->magic_samples[channel_index] -= tmp_in_len;
+   
+   /* If we couldn't process all "magic" input samples, save the rest for next time */
+   if (st->magic_samples[channel_index])
+   {
+      spx_uint32_t i;
+      for (i=0;i<st->magic_samples[channel_index];i++)
+         mem[N-1+i]=mem[N-1+i+tmp_in_len];
+   }
+   *out += out_len*st->out_stride;
+   return out_len;
+}
+
+#ifdef FIXED_POINT
+SPX_RESAMPLE_EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+#else
+SPX_RESAMPLE_EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+#endif
+{
+   int j;
+   spx_uint32_t ilen = *in_len;
+   spx_uint32_t olen = *out_len;
+   spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size;
+   const int filt_offs = st->filt_len - 1;
+   const spx_uint32_t xlen = st->mem_alloc_size - filt_offs;
+   const int istride = st->in_stride;
+
+   if (st->magic_samples[channel_index]) 
+      olen -= speex_resampler_magic(st, channel_index, &out, olen);
+   if (! st->magic_samples[channel_index]) {
+      while (ilen && olen) {
+        spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen;
+        spx_uint32_t ochunk = olen;
+ 
+        if (in) {
+           for(j=0;j<ichunk;++j)
+              x[j+filt_offs]=in[j*istride];
+        } else {
+          for(j=0;j<ichunk;++j)
+            x[j+filt_offs]=0;
+        }
+        speex_resampler_process_native(st, channel_index, &ichunk, out, &ochunk);
+        ilen -= ichunk;
+        olen -= ochunk;
+        out += ochunk * st->out_stride;
+        if (in)
+           in += ichunk * istride;
+      }
+   }
+   *in_len -= ilen;
+   *out_len -= olen;
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+#ifdef FIXED_POINT
+SPX_RESAMPLE_EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+#else
+SPX_RESAMPLE_EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+#endif
+{
+   int j;
+   const int istride_save = st->in_stride;
+   const int ostride_save = st->out_stride;
+   spx_uint32_t ilen = *in_len;
+   spx_uint32_t olen = *out_len;
+   spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size;
+   const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1);
+#ifdef VAR_ARRAYS
+   const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC;
+   VARDECL(spx_word16_t *ystack);
+   ALLOC(ystack, ylen, spx_word16_t);
+#else
+   const unsigned int ylen = FIXED_STACK_ALLOC;
+   spx_word16_t ystack[FIXED_STACK_ALLOC];
+#endif
+
+   st->out_stride = 1;
+   
+   while (ilen && olen) {
+     spx_word16_t *y = ystack;
+     spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen;
+     spx_uint32_t ochunk = (olen > ylen) ? ylen : olen;
+     spx_uint32_t omagic = 0;
+
+     if (st->magic_samples[channel_index]) {
+       omagic = speex_resampler_magic(st, channel_index, &y, ochunk);
+       ochunk -= omagic;
+       olen -= omagic;
+     }
+     if (! st->magic_samples[channel_index]) {
+       if (in) {
+         for(j=0;j<ichunk;++j)
+#ifdef FIXED_POINT
+           x[j+st->filt_len-1]=WORD2INT(in[j*istride_save]);
+#else
+           x[j+st->filt_len-1]=in[j*istride_save];
+#endif
+       } else {
+         for(j=0;j<ichunk;++j)
+           x[j+st->filt_len-1]=0;
+       }
+
+       speex_resampler_process_native(st, channel_index, &ichunk, y, &ochunk);
+     } else {
+       ichunk = 0;
+       ochunk = 0;
+     }
+
+     for (j=0;j<ochunk+omagic;++j)
+#ifdef FIXED_POINT
+        out[j*ostride_save] = ystack[j];
+#else
+        out[j*ostride_save] = WORD2INT(ystack[j]);
+#endif
+     
+     ilen -= ichunk;
+     olen -= ochunk;
+     out += (ochunk+omagic) * ostride_save;
+     if (in)
+       in += ichunk * istride_save;
+   }
+   st->out_stride = ostride_save;
+   *in_len -= ilen;
+   *out_len -= olen;
+
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+SPX_RESAMPLE_EXPORT int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+{
+   spx_uint32_t i;
+   int istride_save, ostride_save;
+   spx_uint32_t bak_out_len = *out_len;
+   spx_uint32_t bak_in_len = *in_len;
+   istride_save = st->in_stride;
+   ostride_save = st->out_stride;
+   st->in_stride = st->out_stride = st->nb_channels;
+   for (i=0;i<st->nb_channels;i++)
+   {
+      *out_len = bak_out_len;
+      *in_len = bak_in_len;
+      if (in != NULL)
+         speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len);
+      else
+         speex_resampler_process_float(st, i, NULL, in_len, out+i, out_len);
+   }
+   st->in_stride = istride_save;
+   st->out_stride = ostride_save;
+   return RESAMPLER_ERR_SUCCESS;
+}
+               
+SPX_RESAMPLE_EXPORT int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+{
+   spx_uint32_t i;
+   int istride_save, ostride_save;
+   spx_uint32_t bak_out_len = *out_len;
+   spx_uint32_t bak_in_len = *in_len;
+   istride_save = st->in_stride;
+   ostride_save = st->out_stride;
+   st->in_stride = st->out_stride = st->nb_channels;
+   for (i=0;i<st->nb_channels;i++)
+   {
+      *out_len = bak_out_len;
+      *in_len = bak_in_len;
+      if (in != NULL)
+         speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len);
+      else
+         speex_resampler_process_int(st, i, NULL, in_len, out+i, out_len);
+   }
+   st->in_stride = istride_save;
+   st->out_stride = ostride_save;
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+SPX_RESAMPLE_EXPORT int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate)
+{
+   return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate);
+}
+
+SPX_RESAMPLE_EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate)
+{
+   *in_rate = st->in_rate;
+   *out_rate = st->out_rate;
+}
+
+SPX_RESAMPLE_EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate)
+{
+   spx_uint32_t fact;
+   spx_uint32_t old_den;
+   spx_uint32_t i;
+   if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den)
+      return RESAMPLER_ERR_SUCCESS;
+   
+   old_den = st->den_rate;
+   st->in_rate = in_rate;
+   st->out_rate = out_rate;
+   st->num_rate = ratio_num;
+   st->den_rate = ratio_den;
+   /* FIXME: This is terribly inefficient, but who cares (at least for now)? */
+   for (fact=2;fact<=IMIN(st->num_rate, st->den_rate);fact++)
+   {
+      while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0))
+      {
+         st->num_rate /= fact;
+         st->den_rate /= fact;
+      }
+   }
+      
+   if (old_den > 0)
+   {
+      for (i=0;i<st->nb_channels;i++)
+      {
+         st->samp_frac_num[i]=st->samp_frac_num[i]*st->den_rate/old_den;
+         /* Safety net */
+         if (st->samp_frac_num[i] >= st->den_rate)
+            st->samp_frac_num[i] = st->den_rate-1;
+      }
+   }
+   
+   if (st->initialised)
+      update_filter(st);
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+SPX_RESAMPLE_EXPORT void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den)
+{
+   *ratio_num = st->num_rate;
+   *ratio_den = st->den_rate;
+}
+
+SPX_RESAMPLE_EXPORT int speex_resampler_set_quality(SpeexResamplerState *st, int quality)
+{
+   if (quality > 10 || quality < 0)
+      return RESAMPLER_ERR_INVALID_ARG;
+   if (st->quality == quality)
+      return RESAMPLER_ERR_SUCCESS;
+   st->quality = quality;
+   if (st->initialised)
+      update_filter(st);
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+SPX_RESAMPLE_EXPORT void speex_resampler_get_quality(SpeexResamplerState *st, int *quality)
+{
+   *quality = st->quality;
+}
+
+SPX_RESAMPLE_EXPORT void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride)
+{
+   st->in_stride = stride;
+}
+
+SPX_RESAMPLE_EXPORT void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride)
+{
+   *stride = st->in_stride;
+}
+
+SPX_RESAMPLE_EXPORT void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride)
+{
+   st->out_stride = stride;
+}
+
+SPX_RESAMPLE_EXPORT void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride)
+{
+   *stride = st->out_stride;
+}
+
+SPX_RESAMPLE_EXPORT int speex_resampler_get_input_latency(SpeexResamplerState *st)
+{
+  return st->filt_len / 2;
+}
+
+SPX_RESAMPLE_EXPORT int speex_resampler_get_output_latency(SpeexResamplerState *st)
+{
+  return ((st->filt_len / 2) * st->den_rate + (st->num_rate >> 1)) / st->num_rate;
+}
+
+SPX_RESAMPLE_EXPORT int speex_resampler_skip_zeros(SpeexResamplerState *st)
+{
+   spx_uint32_t i;
+   for (i=0;i<st->nb_channels;i++)
+      st->last_sample[i] = st->filt_len/2;
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+SPX_RESAMPLE_EXPORT int speex_resampler_reset_mem(SpeexResamplerState *st)
+{
+   spx_uint32_t i;
+   for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
+      st->mem[i] = 0;
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+SPX_RESAMPLE_EXPORT const char *speex_resampler_strerror(int err)
+{
+   switch (err)
+   {
+      case RESAMPLER_ERR_SUCCESS:
+         return "Success.";
+      case RESAMPLER_ERR_ALLOC_FAILED:
+         return "Memory allocation failed.";
+      case RESAMPLER_ERR_BAD_STATE:
+         return "Bad resampler state.";
+      case RESAMPLER_ERR_INVALID_ARG:
+         return "Invalid argument.";
+      case RESAMPLER_ERR_PTR_OVERLAP:
+         return "Input and output buffers overlap.";
+      default:
+         return "Unknown error. Bad error code or strange version mismatch.";
+   }
+}
diff --git a/src/resample_sse.h b/src/resample_sse.h
new file mode 100644
index 0000000..e5b3f55
--- /dev/null
+++ b/src/resample_sse.h
@@ -0,0 +1,152 @@
+/* Copyright (C) 2007-2008 Jean-Marc Valin
+ * Copyright (C) 2008 Thorvald Natvig
+ */
+/**
+   @file resample_sse.h
+   @brief Resampler functions (SSE version)
+*/
+/*
+   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.
+*/
+
+#include <xmmintrin.h>
+
+#define OVERRIDE_INNER_PRODUCT_SINGLE
+static inline float inner_product_single(const float *a, const float *b, unsigned int len)
+{
+   int i;
+   float ret;
+   if (1)
+   {
+      __m128 sum = _mm_setzero_ps();
+      for (i=0;i<len;i+=8)
+      {
+         sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i)));
+         sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4)));
+      }
+      sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
+      sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
+      _mm_store_ss(&ret, sum);
+   }
+   else
+   {
+      ret = 0;
+      for (i=0;i<len;i++) ret += a[i] * b[i];
+   }
+   return ret;
+}
+
+#define OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
+static inline float interpolate_product_single(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
+  int i;
+  float ret;
+  if (1)
+  {
+    __m128 sum = _mm_setzero_ps();
+    __m128 f = _mm_loadu_ps(frac);
+    for(i=0;i<len;i+=2)
+    {
+      sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample)));
+      sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample)));
+    }
+    sum = _mm_mul_ps(f, sum);
+    sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
+    sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
+    _mm_store_ss(&ret, sum);
+  }
+  else
+  {
+    float accum[4] = {0,0,0,0};
+    for(i=0;i<len;i++)
+    {
+      const float curr_in=a[i];
+      accum[0] += curr_in * b[i * oversample + 0];
+      accum[1] += curr_in * b[i * oversample + 1];
+      accum[2] += curr_in * b[i * oversample + 2];
+      accum[3] += curr_in * b[i * oversample + 3];
+    }
+    ret = accum[0] * frac[0] + accum[1] * frac[1] + accum[2] * frac[2] + accum[3] * frac[3];
+  }
+  return ret;
+}
+
+#ifdef __SSE2__
+#include <emmintrin.h>
+#define OVERRIDE_INNER_PRODUCT_DOUBLE
+
+static inline double inner_product_double(const float *a, const float *b, unsigned int len)
+{
+   int i;
+   double ret;
+   __m128d sum = _mm_setzero_pd();
+   __m128 t;
+   for (i=0;i<len;i+=8)
+   {
+      t = _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i));
+      sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
+      sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
+
+      t = _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4));
+      sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
+      sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
+   }
+   sum = _mm_add_sd(sum, (__m128d) _mm_movehl_ps((__m128) sum, (__m128) sum));
+   _mm_store_sd(&ret, sum);
+   return ret;
+}
+
+#define OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
+static inline double interpolate_product_double(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
+  int i;
+  double ret;
+  __m128d sum;
+  __m128d sum1 = _mm_setzero_pd();
+  __m128d sum2 = _mm_setzero_pd();
+  __m128 f = _mm_loadu_ps(frac);
+  __m128d f1 = _mm_cvtps_pd(f);
+  __m128d f2 = _mm_cvtps_pd(_mm_movehl_ps(f,f));
+  __m128 t;
+  for(i=0;i<len;i+=2)
+  {
+    t = _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample));
+    sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
+    sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
+
+    t = _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample));
+    sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
+    sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
+  }
+  sum1 = _mm_mul_pd(f1, sum1);
+  sum2 = _mm_mul_pd(f2, sum2);
+  sum = _mm_add_pd(sum1, sum2);
+  sum = _mm_add_sd(sum, (__m128d) _mm_movehl_ps((__m128) sum, (__m128) sum));
+  _mm_store_sd(&ret, sum);
+  return ret;
+}
+
+#endif
diff --git a/src/speex_resampler.h b/src/speex_resampler.h
new file mode 100644
index 0000000..4d5913f
--- /dev/null
+++ b/src/speex_resampler.h
@@ -0,0 +1,344 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+      
+   File: speex_resampler.h
+   Resampling code
+      
+   The design goals of this code are:
+      - Very fast algorithm
+      - Low memory requirement
+      - Good *perceptual* quality (and not best SNR)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+   1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+   2. 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.
+
+   3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 SPEEX_RESAMPLER_H
+#define SPEEX_RESAMPLER_H
+
+#ifdef OUTSIDE_SPEEX
+
+/********* WARNING: MENTAL SANITY ENDS HERE *************/
+
+/* If the resampler is defined outside of Speex, we change the symbol names so that 
+   there won't be any clash if linking with Speex later on. */
+
+/* #define RANDOM_PREFIX your software name here */
+#ifndef RANDOM_PREFIX
+#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes"
+#endif
+
+#define CAT_PREFIX2(a,b) a ## b
+#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b)
+      
+#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init)
+#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac)
+#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy)
+#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float)
+#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int)
+#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float)
+#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int)
+#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate)
+#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate)
+#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac)
+#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio)
+#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality)
+#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality)
+#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride)
+#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride)
+#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride)
+#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride)
+#define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency)
+#define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency)
+#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
+#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
+#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
+
+#define spx_int16_t short
+#define spx_int32_t int
+#define spx_uint16_t unsigned short
+#define spx_uint32_t unsigned int
+      
+#else /* OUTSIDE_SPEEX */
+
+#ifdef _BUILD_SPEEX
+# include "speex_types.h"
+#else
+# include <speex/speex_types.h>
+#endif
+
+#endif /* OUTSIDE_SPEEX */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPEEX_RESAMPLER_QUALITY_MAX 10
+#define SPEEX_RESAMPLER_QUALITY_MIN 0
+#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4
+#define SPEEX_RESAMPLER_QUALITY_VOIP 3
+#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
+
+enum {
+   RESAMPLER_ERR_SUCCESS         = 0,
+   RESAMPLER_ERR_ALLOC_FAILED    = 1,
+   RESAMPLER_ERR_BAD_STATE       = 2,
+   RESAMPLER_ERR_INVALID_ARG     = 3,
+   RESAMPLER_ERR_PTR_OVERLAP     = 4,
+   
+   RESAMPLER_ERR_MAX_ERROR
+};
+
+struct SpeexResamplerState_;
+typedef struct SpeexResamplerState_ SpeexResamplerState;
+
+/** Create a new resampler with integer input and output rates.
+ * @param nb_channels Number of channels to be processed
+ * @param in_rate Input sampling rate (integer number of Hz).
+ * @param out_rate Output sampling rate (integer number of Hz).
+ * @param quality Resampling quality between 0 and 10, where 0 has poor quality
+ * and 10 has very high quality.
+ * @return Newly created resampler state
+ * @retval NULL Error: not enough memory
+ */
+SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, 
+                                          spx_uint32_t in_rate, 
+                                          spx_uint32_t out_rate, 
+                                          int quality,
+                                          int *err);
+
+/** Create a new resampler with fractional input/output rates. The sampling 
+ * rate ratio is an arbitrary rational number with both the numerator and 
+ * denominator being 32-bit integers.
+ * @param nb_channels Number of channels to be processed
+ * @param ratio_num Numerator of the sampling rate ratio
+ * @param ratio_den Denominator of the sampling rate ratio
+ * @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
+ * @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
+ * @param quality Resampling quality between 0 and 10, where 0 has poor quality
+ * and 10 has very high quality.
+ * @return Newly created resampler state
+ * @retval NULL Error: not enough memory
+ */
+SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, 
+                                               spx_uint32_t ratio_num, 
+                                               spx_uint32_t ratio_den, 
+                                               spx_uint32_t in_rate, 
+                                               spx_uint32_t out_rate, 
+                                               int quality,
+                                               int *err);
+
+/** Destroy a resampler state.
+ * @param st Resampler state
+ */
+void speex_resampler_destroy(SpeexResamplerState *st);
+
+/** Resample a float array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param channel_index Index of the channel to process for the multi-channel 
+ * base (0 otherwise)
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the 
+ * number of samples processed
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written
+ */
+int speex_resampler_process_float(SpeexResamplerState *st, 
+                                   spx_uint32_t channel_index, 
+                                   const float *in, 
+                                   spx_uint32_t *in_len, 
+                                   float *out, 
+                                   spx_uint32_t *out_len);
+
+/** Resample an int array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param channel_index Index of the channel to process for the multi-channel 
+ * base (0 otherwise)
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written
+ */
+int speex_resampler_process_int(SpeexResamplerState *st, 
+                                 spx_uint32_t channel_index, 
+                                 const spx_int16_t *in, 
+                                 spx_uint32_t *in_len, 
+                                 spx_int16_t *out, 
+                                 spx_uint32_t *out_len);
+
+/** Resample an interleaved float array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed. This is all per-channel.
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written.
+ * This is all per-channel.
+ */
+int speex_resampler_process_interleaved_float(SpeexResamplerState *st, 
+                                               const float *in, 
+                                               spx_uint32_t *in_len, 
+                                               float *out, 
+                                               spx_uint32_t *out_len);
+
+/** Resample an interleaved int array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed. This is all per-channel.
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written.
+ * This is all per-channel.
+ */
+int speex_resampler_process_interleaved_int(SpeexResamplerState *st, 
+                                             const spx_int16_t *in, 
+                                             spx_uint32_t *in_len, 
+                                             spx_int16_t *out, 
+                                             spx_uint32_t *out_len);
+
+/** Set (change) the input/output sampling rates (integer value).
+ * @param st Resampler state
+ * @param in_rate Input sampling rate (integer number of Hz).
+ * @param out_rate Output sampling rate (integer number of Hz).
+ */
+int speex_resampler_set_rate(SpeexResamplerState *st, 
+                              spx_uint32_t in_rate, 
+                              spx_uint32_t out_rate);
+
+/** Get the current input/output sampling rates (integer value).
+ * @param st Resampler state
+ * @param in_rate Input sampling rate (integer number of Hz) copied.
+ * @param out_rate Output sampling rate (integer number of Hz) copied.
+ */
+void speex_resampler_get_rate(SpeexResamplerState *st, 
+                              spx_uint32_t *in_rate, 
+                              spx_uint32_t *out_rate);
+
+/** Set (change) the input/output sampling rates and resampling ratio 
+ * (fractional values in Hz supported).
+ * @param st Resampler state
+ * @param ratio_num Numerator of the sampling rate ratio
+ * @param ratio_den Denominator of the sampling rate ratio
+ * @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
+ * @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
+ */
+int speex_resampler_set_rate_frac(SpeexResamplerState *st, 
+                                   spx_uint32_t ratio_num, 
+                                   spx_uint32_t ratio_den, 
+                                   spx_uint32_t in_rate, 
+                                   spx_uint32_t out_rate);
+
+/** Get the current resampling ratio. This will be reduced to the least
+ * common denominator.
+ * @param st Resampler state
+ * @param ratio_num Numerator of the sampling rate ratio copied
+ * @param ratio_den Denominator of the sampling rate ratio copied
+ */
+void speex_resampler_get_ratio(SpeexResamplerState *st, 
+                               spx_uint32_t *ratio_num, 
+                               spx_uint32_t *ratio_den);
+
+/** Set (change) the conversion quality.
+ * @param st Resampler state
+ * @param quality Resampling quality between 0 and 10, where 0 has poor 
+ * quality and 10 has very high quality.
+ */
+int speex_resampler_set_quality(SpeexResamplerState *st, 
+                                 int quality);
+
+/** Get the conversion quality.
+ * @param st Resampler state
+ * @param quality Resampling quality between 0 and 10, where 0 has poor 
+ * quality and 10 has very high quality.
+ */
+void speex_resampler_get_quality(SpeexResamplerState *st, 
+                                 int *quality);
+
+/** Set (change) the input stride.
+ * @param st Resampler state
+ * @param stride Input stride
+ */
+void speex_resampler_set_input_stride(SpeexResamplerState *st, 
+                                      spx_uint32_t stride);
+
+/** Get the input stride.
+ * @param st Resampler state
+ * @param stride Input stride copied
+ */
+void speex_resampler_get_input_stride(SpeexResamplerState *st, 
+                                      spx_uint32_t *stride);
+
+/** Set (change) the output stride.
+ * @param st Resampler state
+ * @param stride Output stride
+ */
+void speex_resampler_set_output_stride(SpeexResamplerState *st, 
+                                      spx_uint32_t stride);
+
+/** Get the output stride.
+ * @param st Resampler state copied
+ * @param stride Output stride
+ */
+void speex_resampler_get_output_stride(SpeexResamplerState *st, 
+                                      spx_uint32_t *stride);
+
+/** Get the latency introduced by the resampler measured in input samples.
+ * @param st Resampler state
+ */
+int speex_resampler_get_input_latency(SpeexResamplerState *st);
+
+/** Get the latency introduced by the resampler measured in output samples.
+ * @param st Resampler state
+ */
+int speex_resampler_get_output_latency(SpeexResamplerState *st);
+
+/** Make sure that the first samples to go out of the resamplers don't have 
+ * leading zeros. This is only useful before starting to use a newly created 
+ * resampler. It is recommended to use that when resampling an audio file, as
+ * it will generate a file with the same length. For real-time processing,
+ * it is probably easier not to use this call (so that the output duration
+ * is the same for the first frame).
+ * @param st Resampler state
+ */
+int speex_resampler_skip_zeros(SpeexResamplerState *st);
+
+/** Reset a resampler so a new (unrelated) stream can be processed.
+ * @param st Resampler state
+ */
+int speex_resampler_reset_mem(SpeexResamplerState *st);
+
+/** Returns the English meaning for an error code
+ * @param err Error code
+ * @return English string
+ */
+const char *speex_resampler_strerror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/stack_alloc.h b/src/stack_alloc.h
new file mode 100644
index 0000000..4114afb
--- /dev/null
+++ b/src/stack_alloc.h
@@ -0,0 +1,116 @@
+/* Copyright (C) 2002 Jean-Marc Valin */
+/**
+   @file stack_alloc.h
+   @brief Temporary memory allocation on stack
+*/
+/*
+   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 STACK_ALLOC_H
+#define STACK_ALLOC_H
+
+#ifdef WIN32
+# include <malloc.h>
+# ifndef alloca
+#   define alloca(_x) _alloca(_x);
+# endif
+#else
+#ifdef HAVE_ALLOCA_H
+#  include <alloca.h>
+# else
+#  include <stdlib.h>
+# endif
+#endif
+
+/**
+ * @def ALIGN(stack, size)
+ *
+ * Aligns the stack to a 'size' boundary
+ *
+ * @param stack Stack
+ * @param size  New size boundary
+ */
+
+/**
+ * @def PUSH(stack, size, type)
+ *
+ * Allocates 'size' elements of type 'type' on the stack
+ *
+ * @param stack Stack
+ * @param size  Number of elements
+ * @param type  Type of element
+ */
+
+/**
+ * @def VARDECL(var)
+ *
+ * Declare variable on stack
+ *
+ * @param var Variable to declare
+ */
+
+/**
+ * @def ALLOC(var, size, type)
+ *
+ * Allocate 'size' elements of 'type' on stack
+ *
+ * @param var  Name of variable to allocate
+ * @param size Number of elements
+ * @param type Type of element
+ */
+
+#ifdef ENABLE_VALGRIND
+
+#include <valgrind/memcheck.h>
+
+#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
+
+#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
+
+#else
+
+#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
+
+#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
+
+#endif
+
+#if defined(VAR_ARRAYS)
+#define VARDECL(var)
+#define ALLOC(var, size, type) type var[size]
+#elif defined(USE_ALLOCA)
+#define VARDECL(var) var
+#define ALLOC(var, size, type) var = alloca(sizeof(type)*(size))
+#else
+#define VARDECL(var) var
+#define ALLOC(var, size, type) var = PUSH(stack, size, type)
+#endif
+
+
+#endif
diff --git a/src/wav_io.c b/src/wav_io.c
new file mode 100644
index 0000000..0db5b03
--- /dev/null
+++ b/src/wav_io.c
@@ -0,0 +1,141 @@
+/* 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 "wav_io.h"
+#include "opus_header.h"
+
+/* Adjust the stream->channel mapping to ensure the proper output order for
+   WAV files. */
+void adjust_wav_mapping(int mapping_family, int channels, unsigned char *stream_map)
+{
+   unsigned char new_stream_map[8];
+   int i;
+   /* If we aren't using one of the defined semantic channel maps, or we have
+      more channels than we know what to do with, use a default 1-1 mapping. */
+   if(mapping_family != 1 || channels > 8)
+      return;
+   for(i = 0; i < channels; i++)
+   {
+      new_stream_map[wav_permute_matrix[channels-1][i]] = stream_map[i];
+   }
+   memcpy(stream_map, new_stream_map, channels*sizeof(*stream_map));
+}
+
+static size_t fwrite_le32(opus_int32 i32, FILE *file)
+{
+   unsigned char buf[4];
+   buf[0]=(unsigned char)(i32&0xFF);
+   buf[1]=(unsigned char)(i32>>8&0xFF);
+   buf[2]=(unsigned char)(i32>>16&0xFF);
+   buf[3]=(unsigned char)(i32>>24&0xFF);
+   return fwrite(buf,4,1,file);
+}
+
+static size_t fwrite_le16(int i16, FILE *file)
+{
+   unsigned char buf[2];
+   buf[0]=(unsigned char)(i16&0xFF);
+   buf[1]=(unsigned char)(i16>>8&0xFF);
+   return fwrite(buf,2,1,file);
+}
+
+int write_wav_header(FILE *file, int rate, int mapping_family, int channels, int fp)
+{
+   int ret;
+   int extensible;
+
+   /* Multichannel files require a WAVEFORMATEXTENSIBLE header to declare the
+      proper channel meanings. */
+   extensible = mapping_family == 1 && 3 <= channels && channels <= 8;
+
+   /* >16 bit audio also requires WAVEFORMATEXTENSIBLE. */
+   extensible |= fp;
+
+   ret = fprintf (file, "RIFF") >= 0;
+   ret &= fwrite_le32 (0x7fffffff, file);
+
+   ret &= fprintf (file, "WAVEfmt ") >= 0;
+   ret &= fwrite_le32 (extensible ? 40 : 16, file);
+   ret &= fwrite_le16 (extensible ? 0xfffe : (fp?3:1), file);
+   ret &= fwrite_le16 (channels, file);
+   ret &= fwrite_le32 (rate, file);
+   ret &= fwrite_le32 ((fp?4:2)*channels*rate, file);
+   ret &= fwrite_le16 ((fp?4:2)*channels, file);
+   ret &= fwrite_le16 (fp?32:16, file);
+
+   if(extensible)
+   {
+      static const unsigned char ksdataformat_subtype_pcm[16]=
+      {
+        0x01, 0x00, 0x00, 0x00,
+        0x00, 0x00,
+        0x10, 0x00,
+        0x80, 0x00,
+        0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+      };
+      static const unsigned char ksdataformat_subtype_float[16]=
+      {
+        0x03, 0x00, 0x00, 0x00,
+        0x00, 0x00,
+        0x10, 0x00,
+        0x80, 0x00,
+        0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+      };
+      static const int wav_channel_masks[8] =
+      {
+         1,                      /* 1.0 mono */
+         1|2,                    /* 2.0 stereo */
+         1|2|4,                  /* 3.0 channel ('wide') stereo */
+         1|2|16|32,              /* 4.0 discrete quadrophonic */
+         1|2|4|16|32,            /* 5.0 */
+         1|2|4|8|16|32,          /* 5.1 */
+         1|2|4|8|256|512|1024,   /* 6.1 */
+         1|2|4|8|16|32|512|1024, /* 7.1 */
+      };
+      ret &= fwrite_le16 (22, file);
+      ret &= fwrite_le16 (fp?32:16, file);
+      ret &= fwrite_le32 (wav_channel_masks[channels-1], file);
+      if (!fp)
+      {
+         ret &= fwrite (ksdataformat_subtype_pcm, 16, 1, file);
+      } else {
+         ret &= fwrite (ksdataformat_subtype_float, 16, 1, file);
+      }
+   }
+
+   ret &= fprintf (file, "data") >= 0;
+   ret &= fwrite_le32 (0x7fffffff, file);
+
+   return !ret ? -1 : extensible ? 40 : 16;
+}
diff --git a/src/wav_io.h b/src/wav_io.h
new file mode 100644
index 0000000..d2f9242
--- /dev/null
+++ b/src/wav_io.h
@@ -0,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
+}
+
+void adjust_wav_mapping(int mapping_family, int channels, unsigned char *stream_map);
+
+int write_wav_header(FILE *file, int rate, int mapping_family, int channels, int fp);
+
+#endif
diff --git a/src/wave_out.c b/src/wave_out.c
new file mode 100644
index 0000000..75903b7
--- /dev/null
+++ b/src/wave_out.c
@@ -0,0 +1,223 @@
+/* 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 unsigned          ScheduledBlocks        = 0;
+static int               PlayedWaveHeadersCount = 0;          // free index
+static WAVEHDR*          PlayedWaveHeaders [MAX_WAVEBLOCKS];
+
+static int
+Box ( const char* msg )
+{
+	MessageBox ( NULL, msg, " "PACKAGE_NAME" "PACKAGE_VERSION": 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 )
+{
+        (void) hWave;
+        (void) dwInstance;
+        (void) 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_PTR)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 */
diff --git a/src/wave_out.h b/src/wave_out.h
new file mode 100644
index 0000000..74314b3
--- /dev/null
+++ b/src/wave_out.h
@@ -0,0 +1,60 @@
+/* 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

+

+//// constants /////////////////////////////////////////////////////

+

+#define SAMPLE_SIZE            16

+#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 */

diff --git a/update_version b/update_version
new file mode 100755
index 0000000..6086136
--- /dev/null
+++ b/update_version
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+# Creates and updates the package_version information used by configure.ac
+# (or other makefiles).  When run inside a git repository it will use the
+# version information that can be queried from it unless AUTO_UPDATE is set
+# to 'no'.  If no version is currently known it will be set to 'unknown'.
+#
+# If called with the argument 'release', the PACKAGE_VERSION will be updated
+# even if AUTO_UPDATE=no, but the value of AUTO_UPDATE shall be preserved.
+# This is used to force a version update whenever `make dist` is run.
+#
+# The exit status is 1 if package_version is not modified, else 0 is returned.
+#
+# This script should NOT be included in distributed tarballs, because if a
+# parent directory contains a git repository we do not want to accidentally
+# retrieve the version information from it instead.  Tarballs should ship
+# with only the package_version file.
+#
+# Ron <ron@debian.org>, 2012.
+
+SRCDIR=$(dirname $0)
+
+if [ -e "$SRCDIR/package_version" ]; then
+    . "$SRCDIR/package_version"
+fi
+
+if [ "$AUTO_UPDATE" = no ]; then
+    [ "$1" = release ] || exit 1
+else
+    AUTO_UPDATE=yes
+fi
+
+# We run `git status` before describe here to ensure that we don't get a false
+# -dirty from files that have been touched but are not actually altered in the
+# working dir.
+GIT_VERSION=$(cd "$SRCDIR" && git status > /dev/null 2>&1 \
+                           && git describe --tags --match 'v*' \
+                                           --always --dirty 2> /dev/null)
+GIT_VERSION=${GIT_VERSION#v}
+
+if [ -n "$GIT_VERSION" ]; then
+
+    [ "$GIT_VERSION" != "$PACKAGE_VERSION" ] || exit 1
+    PACKAGE_VERSION="$GIT_VERSION"
+
+elif [ -z "$PACKAGE_VERSION" ]; then
+    # No current package_version and no git ...
+    # We really shouldn't ever get here, because this script should only be
+    # included in the git repository, and should usually be export-ignored.
+    PACKAGE_VERSION="unknown"
+else
+    exit 1
+fi
+
+cat > "$SRCDIR/package_version" <<-EOF
+	# Automatically generated by update_version.
+	# This file may be sourced into a shell script or makefile.
+
+	# Set this to 'no' if you do not wish the version information
+	# to be checked and updated for every build.  Most people will
+	# never want to change this, it is an option for developers
+	# making frequent changes that they know will not be released.
+	AUTO_UPDATE=$AUTO_UPDATE
+
+	PACKAGE_VERSION="$PACKAGE_VERSION"
+EOF
diff --git a/win32/config.h b/win32/config.h
new file mode 100644
index 0000000..5a1e250
--- /dev/null
+++ b/win32/config.h
@@ -0,0 +1,21 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define OUTSIDE_SPEEX         1
+#define OPUSTOOLS             1
+
+#define inline __inline
+#define alloca _alloca
+#define getpid _getpid
+#define USE_ALLOCA            1
+#define FLOATING_POINT        1
+#define SPX_RESAMPLE_EXPORT
+#define __SSE__
+
+#define RANDOM_PREFIX foo
+
+#define PACKAGE_NAME "opus-tools"
+#include "version.h"
+
+
+#endif /* CONFIG_H */
diff --git a/win32/genversion.bat b/win32/genversion.bat
new file mode 100644
index 0000000..44ba29c
--- /dev/null
+++ b/win32/genversion.bat
@@ -0,0 +1,17 @@
+@echo off
+
+for /f %%v in ('git describe --tags --match "v*" --always') do set version=%%v
+
+set version_out=#define %2 "%version%"
+
+echo %version_out% > %1_temp
+
+echo n | comp %1_temp %1 > NUL 2> NUL
+
+if not errorlevel 1 goto exit
+
+copy /y %1_temp %1
+
+:exit
+
+del %1_temp
diff --git a/win32/unicode_support.c b/win32/unicode_support.c
new file mode 100644
index 0000000..f057ac0
--- /dev/null
+++ b/win32/unicode_support.c
@@ -0,0 +1,197 @@
+/* Copyright (c) 2004-2012 LoRd_MuldeR <mulder2@gmx.de>
+   File: unicode_support.c
+
+   This file was originally part of a patch included with LameXP,
+   released under the same license as the original audio tools.
+
+   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.
+*/
+#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
+
+#include "unicode_support.h"
+
+#include <windows.h>
+#include <io.h>
+
+static UINT g_old_output_cp = ((UINT)-1);
+
+char *utf16_to_utf8(const wchar_t *input)
+{
+	char *Buffer;
+	int BuffSize = 0, Result = 0;
+
+	BuffSize = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL);
+	Buffer = (char*) malloc(sizeof(char) * BuffSize);
+	if(Buffer)
+	{
+		Result = WideCharToMultiByte(CP_UTF8, 0, input, -1, Buffer, BuffSize, NULL, NULL);
+	}
+
+	return ((Result > 0) && (Result <= BuffSize)) ? Buffer : NULL;
+}
+
+char *utf16_to_ansi(const wchar_t *input)
+{
+	char *Buffer;
+	int BuffSize = 0, Result = 0;
+
+	BuffSize = WideCharToMultiByte(CP_ACP, 0, input, -1, NULL, 0, NULL, NULL);
+	Buffer = (char*) malloc(sizeof(char) * BuffSize);
+	if(Buffer)
+	{
+		Result = WideCharToMultiByte(CP_ACP, 0, input, -1, Buffer, BuffSize, NULL, NULL);
+	}
+
+	return ((Result > 0) && (Result <= BuffSize)) ? Buffer : NULL;
+}
+
+wchar_t *utf8_to_utf16(const char *input)
+{
+	wchar_t *Buffer;
+	int BuffSize = 0, Result = 0;
+
+	BuffSize = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0);
+	Buffer = (wchar_t*) malloc(sizeof(wchar_t) * BuffSize);
+	if(Buffer)
+	{
+		Result = MultiByteToWideChar(CP_UTF8, 0, input, -1, Buffer, BuffSize);
+	}
+
+	return ((Result > 0) && (Result <= BuffSize)) ? Buffer : NULL;
+}
+
+void init_commandline_arguments_utf8(int *argc, char ***argv)
+{
+	int i, nArgs;
+	LPWSTR *szArglist;
+
+	szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
+
+	if(NULL == szArglist)
+	{
+		fprintf(stderr, "\nFATAL: CommandLineToArgvW failed\n\n");
+		exit(-1);
+	}
+
+	*argv = (char**) malloc(sizeof(char*) * nArgs);
+	*argc = nArgs;
+
+	if(NULL == *argv)
+	{
+		fprintf(stderr, "\nFATAL: Malloc failed\n\n");
+		exit(-1);
+	}
+	
+	for(i = 0; i < nArgs; i++)
+	{
+		(*argv)[i] = utf16_to_utf8(szArglist[i]);
+		if(NULL == (*argv)[i])
+		{
+			fprintf(stderr, "\nFATAL: utf16_to_utf8 failed\n\n");
+			exit(-1);
+		}
+	}
+
+	LocalFree(szArglist);
+}
+
+void free_commandline_arguments_utf8(int *argc, char ***argv)
+{
+	int i = 0;
+	
+	if(*argv != NULL)
+	{
+		for(i = 0; i < *argc; i++)
+		{
+			if((*argv)[i] != NULL)
+			{
+				free((*argv)[i]);
+				(*argv)[i] = NULL;
+			}
+		}
+		free(*argv);
+		*argv = NULL;
+	}
+}
+
+FILE *fopen_utf8(const char *filename_utf8, const char *mode_utf8)
+{
+	FILE *ret = NULL;
+	wchar_t *filename_utf16 = utf8_to_utf16(filename_utf8);
+	wchar_t *mode_utf16 = utf8_to_utf16(mode_utf8);
+	
+	if(filename_utf16 && mode_utf16)
+	{
+		ret = _wfopen(filename_utf16, mode_utf16);
+	}
+
+	if(filename_utf16) free(filename_utf16);
+	if(mode_utf16) free(mode_utf16);
+
+	return ret;
+}
+
+int stat_utf8(const char *path_utf8, struct _stat *buf)
+{
+	int ret = -1;
+	
+	wchar_t *path_utf16 = utf8_to_utf16(path_utf8);
+	if(path_utf16)
+	{
+		ret = _wstat(path_utf16, buf);
+		free(path_utf16);
+	}
+	
+	return ret;
+}
+
+int unlink_utf8(const char *path_utf8)
+{
+	int ret = -1;
+	
+	wchar_t *path_utf16 = utf8_to_utf16(path_utf8);
+	if(path_utf16)
+	{
+		ret = _wunlink(path_utf16);
+		free(path_utf16);
+	}
+	
+	return ret;
+}
+
+void init_console_utf8(void)
+{
+	g_old_output_cp = GetConsoleOutputCP();
+	SetConsoleOutputCP(CP_UTF8);
+}
+
+void uninit_console_utf8(void)
+{
+	if(g_old_output_cp != ((UINT)-1))
+	{
+		SetConsoleOutputCP(g_old_output_cp);
+	}
+}
+
+#endif
\ No newline at end of file
diff --git a/win32/unicode_support.h b/win32/unicode_support.h
new file mode 100644
index 0000000..cf55719
--- /dev/null
+++ b/win32/unicode_support.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2004-2012 LoRd_MuldeR <mulder2@gmx.de>
+   File: unicode_support.h
+
+   This file was originally part of a patch included with LameXP,
+   released under the same license as the original audio tools.
+
+   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 UNICODE_SUPPORT_H_INCLUDED
+#define UNICODE_SUPPORT_H_INCLUDED
+
+#include <stdio.h>
+#include <sys/stat.h>
+
+#define WIN_UNICODE 1
+
+char *utf16_to_utf8(const wchar_t *input);
+char *utf16_to_ansi(const wchar_t *input);
+wchar_t *utf8_to_utf16(const char *input);
+void init_commandline_arguments_utf8(int *argc, char ***argv);
+void free_commandline_arguments_utf8(int *argc, char ***argv);
+FILE *fopen_utf8(const char *filename_utf8, const char *mode_utf8);
+int stat_utf8(const char *path_utf8, struct _stat *buf);
+int unlink_utf8(const char *path_utf8);
+void init_console_utf8(void);
+void uninit_console_utf8(void);
+
+#endif
\ No newline at end of file
diff --git a/win32/version.h b/win32/version.h
new file mode 100644
index 0000000..892cbc1
--- /dev/null
+++ b/win32/version.h
@@ -0,0 +1 @@
+#define PACKAGE_VERSION "v0.1.9"