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"