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, ð)) {
+ 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"