mirror of https://github.com/nu774/fdkaac.git
initial commit
This commit is contained in:
commit
48e2f01c56
|
@ -0,0 +1,17 @@
|
||||||
|
Copyright (C) 2013 nu774
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
|
@ -0,0 +1,370 @@
|
||||||
|
Installation Instructions
|
||||||
|
*************************
|
||||||
|
|
||||||
|
Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation,
|
||||||
|
Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification,
|
||||||
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved. This file is offered as-is,
|
||||||
|
without warranty of any kind.
|
||||||
|
|
||||||
|
Basic Installation
|
||||||
|
==================
|
||||||
|
|
||||||
|
Briefly, the shell commands `./configure; make; make install' should
|
||||||
|
configure, build, and install this package. The following
|
||||||
|
more-detailed instructions are generic; see the `README' file for
|
||||||
|
instructions specific to this package. Some packages provide this
|
||||||
|
`INSTALL' file but do not implement all of the features documented
|
||||||
|
below. The lack of an optional feature in a given package is not
|
||||||
|
necessarily a bug. More recommendations for GNU packages can be found
|
||||||
|
in *note Makefile Conventions: (standards)Makefile Conventions.
|
||||||
|
|
||||||
|
The `configure' shell script attempts to guess correct values for
|
||||||
|
various system-dependent variables used during compilation. It uses
|
||||||
|
those values to create a `Makefile' in each directory of the package.
|
||||||
|
It may also create one or more `.h' files containing system-dependent
|
||||||
|
definitions. Finally, it creates a shell script `config.status' that
|
||||||
|
you can run in the future to recreate the current configuration, and a
|
||||||
|
file `config.log' containing compiler output (useful mainly for
|
||||||
|
debugging `configure').
|
||||||
|
|
||||||
|
It can also use an optional file (typically called `config.cache'
|
||||||
|
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
||||||
|
the results of its tests to speed up reconfiguring. Caching is
|
||||||
|
disabled by default to prevent problems with accidental use of stale
|
||||||
|
cache files.
|
||||||
|
|
||||||
|
If you need to do unusual things to compile the package, please try
|
||||||
|
to figure out how `configure' could check whether to do them, and mail
|
||||||
|
diffs or instructions to the address given in the `README' so they can
|
||||||
|
be considered for the next release. If you are using the cache, and at
|
||||||
|
some point `config.cache' contains results you don't want to keep, you
|
||||||
|
may remove or edit it.
|
||||||
|
|
||||||
|
The file `configure.ac' (or `configure.in') is used to create
|
||||||
|
`configure' by a program called `autoconf'. You need `configure.ac' if
|
||||||
|
you want to change it or regenerate `configure' using a newer version
|
||||||
|
of `autoconf'.
|
||||||
|
|
||||||
|
The simplest way to compile this package is:
|
||||||
|
|
||||||
|
1. `cd' to the directory containing the package's source code and type
|
||||||
|
`./configure' to configure the package for your system.
|
||||||
|
|
||||||
|
Running `configure' might take a while. While running, it prints
|
||||||
|
some messages telling which features it is checking for.
|
||||||
|
|
||||||
|
2. Type `make' to compile the package.
|
||||||
|
|
||||||
|
3. Optionally, type `make check' to run any self-tests that come with
|
||||||
|
the package, generally using the just-built uninstalled binaries.
|
||||||
|
|
||||||
|
4. Type `make install' to install the programs and any data files and
|
||||||
|
documentation. When installing into a prefix owned by root, it is
|
||||||
|
recommended that the package be configured and built as a regular
|
||||||
|
user, and only the `make install' phase executed with root
|
||||||
|
privileges.
|
||||||
|
|
||||||
|
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
||||||
|
this time using the binaries in their final installed location.
|
||||||
|
This target does not install anything. Running this target as a
|
||||||
|
regular user, particularly if the prior `make install' required
|
||||||
|
root privileges, verifies that the installation completed
|
||||||
|
correctly.
|
||||||
|
|
||||||
|
6. You can remove the program binaries and object files from the
|
||||||
|
source code directory by typing `make clean'. To also remove the
|
||||||
|
files that `configure' created (so you can compile the package for
|
||||||
|
a different kind of computer), type `make distclean'. There is
|
||||||
|
also a `make maintainer-clean' target, but that is intended mainly
|
||||||
|
for the package's developers. If you use it, you may have to get
|
||||||
|
all sorts of other programs in order to regenerate files that came
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
7. Often, you can also type `make uninstall' to remove the installed
|
||||||
|
files again. In practice, not all packages have tested that
|
||||||
|
uninstallation works correctly, even though it is required by the
|
||||||
|
GNU Coding Standards.
|
||||||
|
|
||||||
|
8. Some packages, particularly those that use Automake, provide `make
|
||||||
|
distcheck', which can by used by developers to test that all other
|
||||||
|
targets like `make install' and `make uninstall' work correctly.
|
||||||
|
This target is generally not run by end users.
|
||||||
|
|
||||||
|
Compilers and Options
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Some systems require unusual options for compilation or linking that
|
||||||
|
the `configure' script does not know about. Run `./configure --help'
|
||||||
|
for details on some of the pertinent environment variables.
|
||||||
|
|
||||||
|
You can give `configure' initial values for configuration parameters
|
||||||
|
by setting variables in the command line or in the environment. Here
|
||||||
|
is an example:
|
||||||
|
|
||||||
|
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
||||||
|
|
||||||
|
*Note Defining Variables::, for more details.
|
||||||
|
|
||||||
|
Compiling For Multiple Architectures
|
||||||
|
====================================
|
||||||
|
|
||||||
|
You can compile the package for more than one kind of computer at the
|
||||||
|
same time, by placing the object files for each architecture in their
|
||||||
|
own directory. To do this, you can use GNU `make'. `cd' to the
|
||||||
|
directory where you want the object files and executables to go and run
|
||||||
|
the `configure' script. `configure' automatically checks for the
|
||||||
|
source code in the directory that `configure' is in and in `..'. This
|
||||||
|
is known as a "VPATH" build.
|
||||||
|
|
||||||
|
With a non-GNU `make', it is safer to compile the package for one
|
||||||
|
architecture at a time in the source code directory. After you have
|
||||||
|
installed the package for one architecture, use `make distclean' before
|
||||||
|
reconfiguring for another architecture.
|
||||||
|
|
||||||
|
On MacOS X 10.5 and later systems, you can create libraries and
|
||||||
|
executables that work on multiple system types--known as "fat" or
|
||||||
|
"universal" binaries--by specifying multiple `-arch' options to the
|
||||||
|
compiler but only a single `-arch' option to the preprocessor. Like
|
||||||
|
this:
|
||||||
|
|
||||||
|
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||||
|
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||||
|
CPP="gcc -E" CXXCPP="g++ -E"
|
||||||
|
|
||||||
|
This is not guaranteed to produce working output in all cases, you
|
||||||
|
may have to build one architecture at a time and combine the results
|
||||||
|
using the `lipo' tool if you have problems.
|
||||||
|
|
||||||
|
Installation Names
|
||||||
|
==================
|
||||||
|
|
||||||
|
By default, `make install' installs the package's commands under
|
||||||
|
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||||
|
can specify an installation prefix other than `/usr/local' by giving
|
||||||
|
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
||||||
|
absolute file name.
|
||||||
|
|
||||||
|
You can specify separate installation prefixes for
|
||||||
|
architecture-specific files and architecture-independent files. If you
|
||||||
|
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
||||||
|
PREFIX as the prefix for installing programs and libraries.
|
||||||
|
Documentation and other data files still use the regular prefix.
|
||||||
|
|
||||||
|
In addition, if you use an unusual directory layout you can give
|
||||||
|
options like `--bindir=DIR' to specify different values for particular
|
||||||
|
kinds of files. Run `configure --help' for a list of the directories
|
||||||
|
you can set and what kinds of files go in them. In general, the
|
||||||
|
default for these options is expressed in terms of `${prefix}', so that
|
||||||
|
specifying just `--prefix' will affect all of the other directory
|
||||||
|
specifications that were not explicitly provided.
|
||||||
|
|
||||||
|
The most portable way to affect installation locations is to pass the
|
||||||
|
correct locations to `configure'; however, many packages provide one or
|
||||||
|
both of the following shortcuts of passing variable assignments to the
|
||||||
|
`make install' command line to change installation locations without
|
||||||
|
having to reconfigure or recompile.
|
||||||
|
|
||||||
|
The first method involves providing an override variable for each
|
||||||
|
affected directory. For example, `make install
|
||||||
|
prefix=/alternate/directory' will choose an alternate location for all
|
||||||
|
directory configuration variables that were expressed in terms of
|
||||||
|
`${prefix}'. Any directories that were specified during `configure',
|
||||||
|
but not in terms of `${prefix}', must each be overridden at install
|
||||||
|
time for the entire installation to be relocated. The approach of
|
||||||
|
makefile variable overrides for each directory variable is required by
|
||||||
|
the GNU Coding Standards, and ideally causes no recompilation.
|
||||||
|
However, some platforms have known limitations with the semantics of
|
||||||
|
shared libraries that end up requiring recompilation when using this
|
||||||
|
method, particularly noticeable in packages that use GNU Libtool.
|
||||||
|
|
||||||
|
The second method involves providing the `DESTDIR' variable. For
|
||||||
|
example, `make install DESTDIR=/alternate/directory' will prepend
|
||||||
|
`/alternate/directory' before all installation names. The approach of
|
||||||
|
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
||||||
|
does not work on platforms that have drive letters. On the other hand,
|
||||||
|
it does better at avoiding recompilation issues, and works well even
|
||||||
|
when some directory options were not specified in terms of `${prefix}'
|
||||||
|
at `configure' time.
|
||||||
|
|
||||||
|
Optional Features
|
||||||
|
=================
|
||||||
|
|
||||||
|
If the package supports it, you can cause programs to be installed
|
||||||
|
with an extra prefix or suffix on their names by giving `configure' the
|
||||||
|
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||||
|
|
||||||
|
Some packages pay attention to `--enable-FEATURE' options to
|
||||||
|
`configure', where FEATURE indicates an optional part of the package.
|
||||||
|
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||||
|
is something like `gnu-as' or `x' (for the X Window System). The
|
||||||
|
`README' should mention any `--enable-' and `--with-' options that the
|
||||||
|
package recognizes.
|
||||||
|
|
||||||
|
For packages that use the X Window System, `configure' can usually
|
||||||
|
find the X include and library files automatically, but if it doesn't,
|
||||||
|
you can use the `configure' options `--x-includes=DIR' and
|
||||||
|
`--x-libraries=DIR' to specify their locations.
|
||||||
|
|
||||||
|
Some packages offer the ability to configure how verbose the
|
||||||
|
execution of `make' will be. For these packages, running `./configure
|
||||||
|
--enable-silent-rules' sets the default to minimal output, which can be
|
||||||
|
overridden with `make V=1'; while running `./configure
|
||||||
|
--disable-silent-rules' sets the default to verbose, which can be
|
||||||
|
overridden with `make V=0'.
|
||||||
|
|
||||||
|
Particular systems
|
||||||
|
==================
|
||||||
|
|
||||||
|
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
||||||
|
CC is not installed, it is recommended to use the following options in
|
||||||
|
order to use an ANSI C compiler:
|
||||||
|
|
||||||
|
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
||||||
|
|
||||||
|
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
||||||
|
|
||||||
|
HP-UX `make' updates targets which have the same time stamps as
|
||||||
|
their prerequisites, which makes it generally unusable when shipped
|
||||||
|
generated files such as `configure' are involved. Use GNU `make'
|
||||||
|
instead.
|
||||||
|
|
||||||
|
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
||||||
|
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
||||||
|
a workaround. If GNU CC is not installed, it is therefore recommended
|
||||||
|
to try
|
||||||
|
|
||||||
|
./configure CC="cc"
|
||||||
|
|
||||||
|
and if that doesn't work, try
|
||||||
|
|
||||||
|
./configure CC="cc -nodtk"
|
||||||
|
|
||||||
|
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
||||||
|
directory contains several dysfunctional programs; working variants of
|
||||||
|
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
||||||
|
in your `PATH', put it _after_ `/usr/bin'.
|
||||||
|
|
||||||
|
On Haiku, software installed for all users goes in `/boot/common',
|
||||||
|
not `/usr/local'. It is recommended to use the following options:
|
||||||
|
|
||||||
|
./configure --prefix=/boot/common
|
||||||
|
|
||||||
|
Specifying the System Type
|
||||||
|
==========================
|
||||||
|
|
||||||
|
There may be some features `configure' cannot figure out
|
||||||
|
automatically, but needs to determine by the type of machine the package
|
||||||
|
will run on. Usually, assuming the package is built to be run on the
|
||||||
|
_same_ architectures, `configure' can figure that out, but if it prints
|
||||||
|
a message saying it cannot guess the machine type, give it the
|
||||||
|
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||||
|
type, such as `sun4', or a canonical name which has the form:
|
||||||
|
|
||||||
|
CPU-COMPANY-SYSTEM
|
||||||
|
|
||||||
|
where SYSTEM can have one of these forms:
|
||||||
|
|
||||||
|
OS
|
||||||
|
KERNEL-OS
|
||||||
|
|
||||||
|
See the file `config.sub' for the possible values of each field. If
|
||||||
|
`config.sub' isn't included in this package, then this package doesn't
|
||||||
|
need to know the machine type.
|
||||||
|
|
||||||
|
If you are _building_ compiler tools for cross-compiling, you should
|
||||||
|
use the option `--target=TYPE' to select the type of system they will
|
||||||
|
produce code for.
|
||||||
|
|
||||||
|
If you want to _use_ a cross compiler, that generates code for a
|
||||||
|
platform different from the build platform, you should specify the
|
||||||
|
"host" platform (i.e., that on which the generated programs will
|
||||||
|
eventually be run) with `--host=TYPE'.
|
||||||
|
|
||||||
|
Sharing Defaults
|
||||||
|
================
|
||||||
|
|
||||||
|
If you want to set default values for `configure' scripts to share,
|
||||||
|
you can create a site shell script called `config.site' that gives
|
||||||
|
default values for variables like `CC', `cache_file', and `prefix'.
|
||||||
|
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||||
|
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||||
|
`CONFIG_SITE' environment variable to the location of the site script.
|
||||||
|
A warning: not all `configure' scripts look for a site script.
|
||||||
|
|
||||||
|
Defining Variables
|
||||||
|
==================
|
||||||
|
|
||||||
|
Variables not defined in a site shell script can be set in the
|
||||||
|
environment passed to `configure'. However, some packages may run
|
||||||
|
configure again during the build, and the customized values of these
|
||||||
|
variables may be lost. In order to avoid this problem, you should set
|
||||||
|
them in the `configure' command line, using `VAR=value'. For example:
|
||||||
|
|
||||||
|
./configure CC=/usr/local2/bin/gcc
|
||||||
|
|
||||||
|
causes the specified `gcc' to be used as the C compiler (unless it is
|
||||||
|
overridden in the site shell script).
|
||||||
|
|
||||||
|
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||||
|
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||||
|
|
||||||
|
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||||
|
|
||||||
|
`configure' Invocation
|
||||||
|
======================
|
||||||
|
|
||||||
|
`configure' recognizes the following options to control how it
|
||||||
|
operates.
|
||||||
|
|
||||||
|
`--help'
|
||||||
|
`-h'
|
||||||
|
Print a summary of all of the options to `configure', and exit.
|
||||||
|
|
||||||
|
`--help=short'
|
||||||
|
`--help=recursive'
|
||||||
|
Print a summary of the options unique to this package's
|
||||||
|
`configure', and exit. The `short' variant lists options used
|
||||||
|
only in the top level, while the `recursive' variant lists options
|
||||||
|
also present in any nested packages.
|
||||||
|
|
||||||
|
`--version'
|
||||||
|
`-V'
|
||||||
|
Print the version of Autoconf used to generate the `configure'
|
||||||
|
script, and exit.
|
||||||
|
|
||||||
|
`--cache-file=FILE'
|
||||||
|
Enable the cache: use and save the results of the tests in FILE,
|
||||||
|
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
||||||
|
disable caching.
|
||||||
|
|
||||||
|
`--config-cache'
|
||||||
|
`-C'
|
||||||
|
Alias for `--cache-file=config.cache'.
|
||||||
|
|
||||||
|
`--quiet'
|
||||||
|
`--silent'
|
||||||
|
`-q'
|
||||||
|
Do not print messages saying which checks are being made. To
|
||||||
|
suppress all normal output, redirect it to `/dev/null' (any error
|
||||||
|
messages will still be shown).
|
||||||
|
|
||||||
|
`--srcdir=DIR'
|
||||||
|
Look for the package's source code in directory DIR. Usually
|
||||||
|
`configure' can determine that directory automatically.
|
||||||
|
|
||||||
|
`--prefix=DIR'
|
||||||
|
Use DIR as the installation prefix. *note Installation Names::
|
||||||
|
for more details, including other options available for fine-tuning
|
||||||
|
the installation locations.
|
||||||
|
|
||||||
|
`--no-create'
|
||||||
|
`-n'
|
||||||
|
Run the configure checks, but stop before creating any output
|
||||||
|
files.
|
||||||
|
|
||||||
|
`configure' also accepts some other, not widely useful, options. Run
|
||||||
|
`configure --help' for more details.
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
|
EXTRA_DIST = COPYING
|
||||||
|
|
||||||
|
bin_PROGRAMS = fdkaac
|
||||||
|
|
||||||
|
fdkaac_SOURCES = \
|
||||||
|
src/aacenc.c \
|
||||||
|
src/lpcm.c \
|
||||||
|
src/m4af.c \
|
||||||
|
src/main.c \
|
||||||
|
src/progress.c \
|
||||||
|
src/wav_reader.c
|
||||||
|
|
||||||
|
fdkaac_LDADD = \
|
||||||
|
@LIBICONV@ -lm
|
||||||
|
|
||||||
|
if FDK_PLATFORM_POSIX
|
||||||
|
fdkaac_SOURCES += \
|
||||||
|
src/compat_posix.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
if FDK_PLATFORM_WIN32
|
||||||
|
fdkaac_SOURCES += \
|
||||||
|
src/compat_win32.c
|
||||||
|
endif
|
|
@ -0,0 +1,14 @@
|
||||||
|
==========================================================================
|
||||||
|
fdkaac - command line frontend encoder for libfdk-aac
|
||||||
|
==========================================================================
|
||||||
|
|
||||||
|
Prerequisites
|
||||||
|
-------------
|
||||||
|
You need libfdk-aac, GNU autoconf and automake, and C compiler.
|
||||||
|
On Posix environment, you will also need GNU gettext (for iconv.m4).
|
||||||
|
|
||||||
|
How to build
|
||||||
|
------------
|
||||||
|
$ autoreconf -i
|
||||||
|
$ ./configure
|
||||||
|
$ make
|
|
@ -0,0 +1,52 @@
|
||||||
|
m4_define([VERSION_H],m4_esyscmd([cat version.h]))
|
||||||
|
changequote({{,}})dnl
|
||||||
|
m4_define({{XX_VERSION}},m4_bregexp(VERSION_H,{{^const.*"\(.+\)";}},{{\1}}))
|
||||||
|
changequote([,])dnl
|
||||||
|
|
||||||
|
AC_INIT([fdkaac], [XX_VERSION], [honeycomb77@gmail.com])
|
||||||
|
AC_CONFIG_HEADERS([config.h])
|
||||||
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
|
|
||||||
|
AM_INIT_AUTOMAKE
|
||||||
|
|
||||||
|
AC_PROG_CC
|
||||||
|
|
||||||
|
AC_CHECK_HEADERS([getopt.h sys/time.h])
|
||||||
|
AC_CHECK_HEADERS([localcharset.h langinfo.h endian.h byteswap.h])
|
||||||
|
AC_CHECK_HEADERS([fdk-aac/aacenc_lib.h], ,
|
||||||
|
AC_MSG_ERROR([libfdk-aac is required]))
|
||||||
|
|
||||||
|
AC_C_INLINE
|
||||||
|
AC_C_BIGENDIAN
|
||||||
|
AC_TYPE_INT16_T
|
||||||
|
AC_TYPE_INT32_T
|
||||||
|
AC_TYPE_INT64_T
|
||||||
|
AC_TYPE_INT8_T
|
||||||
|
AC_TYPE_SIZE_T
|
||||||
|
AC_TYPE_UINT16_T
|
||||||
|
AC_TYPE_UINT32_T
|
||||||
|
AC_TYPE_UINT64_T
|
||||||
|
AC_TYPE_UINT8_T
|
||||||
|
AC_CHECK_TYPES([ptrdiff_t])
|
||||||
|
|
||||||
|
AC_SYS_LARGEFILE
|
||||||
|
AC_FUNC_FSEEKO
|
||||||
|
AC_CHECK_FUNCS([gettimeofday nl_langinfo strdup])
|
||||||
|
AC_SEARCH_LIBS([aacEncOpen],[fdk-aac])
|
||||||
|
|
||||||
|
AC_CANONICAL_HOST
|
||||||
|
|
||||||
|
X_PLATFORM=posix
|
||||||
|
case ${host} in
|
||||||
|
*-*-mingw*)
|
||||||
|
X_PLATFORM=win32
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
AM_ICONV
|
||||||
|
esac
|
||||||
|
AM_CONDITIONAL([FDK_PLATFORM_POSIX],[test "$X_PLATFORM" = "posix"])
|
||||||
|
AM_CONDITIONAL([FDK_PLATFORM_WIN32],[test "$X_PLATFORM" = "win32"])
|
||||||
|
|
||||||
|
AC_CONFIG_FILES([Makefile])
|
||||||
|
|
||||||
|
AC_OUTPUT
|
|
@ -0,0 +1,630 @@
|
||||||
|
/* $OpenBSD: getopt_long.c,v 1.21 2006/09/22 17:22:05 millert Exp $ */
|
||||||
|
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* Sponsored in part by the Defense Advanced Research Projects
|
||||||
|
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||||
|
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||||
|
*/
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
* by Dieter Baron and Thomas Klausner.
|
||||||
|
*
|
||||||
|
* 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. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the NetBSD
|
||||||
|
* Foundation, Inc. and its contributors.
|
||||||
|
* 4. Neither the name of The NetBSD 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 NETBSD FOUNDATION, INC. 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 0
|
||||||
|
#if defined(LIBC_SCCS) && !defined(lint)
|
||||||
|
static char *rcsid = "$OpenBSD: getopt_long.c,v 1.16 2004/02/04 18:17:25 millert Exp $";
|
||||||
|
#endif /* LIBC_SCCS and not lint */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "getopt.h"
|
||||||
|
|
||||||
|
#define GNU_COMPATIBLE /* Be more compatible, configure's use us! */
|
||||||
|
|
||||||
|
#if 0 /* we prefer to keep our getopt(3) */
|
||||||
|
#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int opterr = 1; /* if error message should be printed */
|
||||||
|
int optind = 1; /* index into parent argv vector */
|
||||||
|
int optopt = '?'; /* character checked for validity */
|
||||||
|
int optreset; /* reset getopt */
|
||||||
|
char *optarg; /* argument associated with option */
|
||||||
|
|
||||||
|
#define PRINT_ERROR ((opterr) && (*options != ':'))
|
||||||
|
|
||||||
|
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
|
||||||
|
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
|
||||||
|
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
|
||||||
|
|
||||||
|
/* return values */
|
||||||
|
#define BADCH (int)'?'
|
||||||
|
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
|
||||||
|
#define INORDER (int)1
|
||||||
|
|
||||||
|
#define EMSG ""
|
||||||
|
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
#define NO_PREFIX (-1)
|
||||||
|
#define D_PREFIX 0
|
||||||
|
#define DD_PREFIX 1
|
||||||
|
#define W_PREFIX 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int getopt_internal(int, char * const *, const char *,
|
||||||
|
const struct option *, int *, int);
|
||||||
|
static int parse_long_options(char * const *, const char *,
|
||||||
|
const struct option *, int *, int, int);
|
||||||
|
static int gcd(int, int);
|
||||||
|
static void permute_args(int, int, int, char * const *);
|
||||||
|
|
||||||
|
static char *place = EMSG; /* option letter processing */
|
||||||
|
|
||||||
|
/* XXX: set optreset to 1 rather than these two */
|
||||||
|
static int nonopt_start = -1; /* first non option argument (for permute) */
|
||||||
|
static int nonopt_end = -1; /* first option after non options (for permute) */
|
||||||
|
|
||||||
|
/* Error messages */
|
||||||
|
static const char recargchar[] = "option requires an argument -- %c";
|
||||||
|
static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
static int dash_prefix = NO_PREFIX;
|
||||||
|
static const char gnuoptchar[] = "invalid option -- %c";
|
||||||
|
|
||||||
|
static const char recargstring[] = "option `%s%s' requires an argument";
|
||||||
|
static const char ambig[] = "option `%s%.*s' is ambiguous";
|
||||||
|
static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
|
||||||
|
static const char illoptstring[] = "unrecognized option `%s%s'";
|
||||||
|
#else
|
||||||
|
static const char recargstring[] = "option requires an argument -- %s";
|
||||||
|
static const char ambig[] = "ambiguous option -- %.*s";
|
||||||
|
static const char noarg[] = "option doesn't take an argument -- %.*s";
|
||||||
|
static const char illoptstring[] = "unknown option -- %s";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
warnx(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(stderr, fmt, args);
|
||||||
|
putc('\n', stderr);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute the greatest common divisor of a and b.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
gcd(int a, int b)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
c = a % b;
|
||||||
|
while (c != 0) {
|
||||||
|
a = b;
|
||||||
|
b = c;
|
||||||
|
c = a % b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exchange the block from nonopt_start to nonopt_end with the block
|
||||||
|
* from nonopt_end to opt_end (keeping the same order of arguments
|
||||||
|
* in each block).
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
permute_args(int panonopt_start, int panonopt_end, int opt_end,
|
||||||
|
char * const *nargv)
|
||||||
|
{
|
||||||
|
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
|
||||||
|
char *swap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* compute lengths of blocks and number and size of cycles
|
||||||
|
*/
|
||||||
|
nnonopts = panonopt_end - panonopt_start;
|
||||||
|
nopts = opt_end - panonopt_end;
|
||||||
|
ncycle = gcd(nnonopts, nopts);
|
||||||
|
cyclelen = (opt_end - panonopt_start) / ncycle;
|
||||||
|
|
||||||
|
for (i = 0; i < ncycle; i++) {
|
||||||
|
cstart = panonopt_end+i;
|
||||||
|
pos = cstart;
|
||||||
|
for (j = 0; j < cyclelen; j++) {
|
||||||
|
if (pos >= panonopt_end)
|
||||||
|
pos -= nnonopts;
|
||||||
|
else
|
||||||
|
pos += nopts;
|
||||||
|
swap = nargv[pos];
|
||||||
|
/* LINTED const cast */
|
||||||
|
((char **) nargv)[pos] = nargv[cstart];
|
||||||
|
/* LINTED const cast */
|
||||||
|
((char **)nargv)[cstart] = swap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parse_long_options --
|
||||||
|
* Parse long options in argc/argv argument vector.
|
||||||
|
* Returns -1 if short_too is set and the option does not match long_options.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
parse_long_options(char * const *nargv, const char *options,
|
||||||
|
const struct option *long_options, int *idx, int short_too, int flags)
|
||||||
|
{
|
||||||
|
char *current_argv, *has_equal;
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
char *current_dash;
|
||||||
|
#endif
|
||||||
|
size_t current_argv_len;
|
||||||
|
int i, match, exact_match, second_partial_match;
|
||||||
|
|
||||||
|
current_argv = place;
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
switch (dash_prefix) {
|
||||||
|
case D_PREFIX:
|
||||||
|
current_dash = "-";
|
||||||
|
break;
|
||||||
|
case DD_PREFIX:
|
||||||
|
current_dash = "--";
|
||||||
|
break;
|
||||||
|
case W_PREFIX:
|
||||||
|
current_dash = "-W ";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
current_dash = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
match = -1;
|
||||||
|
exact_match = 0;
|
||||||
|
second_partial_match = 0;
|
||||||
|
|
||||||
|
optind++;
|
||||||
|
|
||||||
|
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
||||||
|
/* argument found (--option=arg) */
|
||||||
|
current_argv_len = has_equal - current_argv;
|
||||||
|
has_equal++;
|
||||||
|
} else
|
||||||
|
current_argv_len = strlen(current_argv);
|
||||||
|
|
||||||
|
for (i = 0; long_options[i].name; i++) {
|
||||||
|
/* find matching long option */
|
||||||
|
if (strncmp(current_argv, long_options[i].name,
|
||||||
|
current_argv_len))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strlen(long_options[i].name) == current_argv_len) {
|
||||||
|
/* exact match */
|
||||||
|
match = i;
|
||||||
|
exact_match = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If this is a known short option, don't allow
|
||||||
|
* a partial match of a single character.
|
||||||
|
*/
|
||||||
|
if (short_too && current_argv_len == 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (match == -1) /* first partial match */
|
||||||
|
match = i;
|
||||||
|
else if ((flags & FLAG_LONGONLY) ||
|
||||||
|
long_options[i].has_arg !=
|
||||||
|
long_options[match].has_arg ||
|
||||||
|
long_options[i].flag != long_options[match].flag ||
|
||||||
|
long_options[i].val != long_options[match].val)
|
||||||
|
second_partial_match = 1;
|
||||||
|
}
|
||||||
|
if (!exact_match && second_partial_match) {
|
||||||
|
/* ambiguous abbreviation */
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(ambig,
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
current_dash,
|
||||||
|
#endif
|
||||||
|
(int)current_argv_len,
|
||||||
|
current_argv);
|
||||||
|
optopt = 0;
|
||||||
|
return (BADCH);
|
||||||
|
}
|
||||||
|
if (match != -1) { /* option found */
|
||||||
|
if (long_options[match].has_arg == no_argument
|
||||||
|
&& has_equal) {
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(noarg,
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
current_dash,
|
||||||
|
#endif
|
||||||
|
(int)current_argv_len,
|
||||||
|
current_argv);
|
||||||
|
/*
|
||||||
|
* XXX: GNU sets optopt to val regardless of flag
|
||||||
|
*/
|
||||||
|
if (long_options[match].flag == NULL)
|
||||||
|
optopt = long_options[match].val;
|
||||||
|
else
|
||||||
|
optopt = 0;
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
return (BADCH);
|
||||||
|
#else
|
||||||
|
return (BADARG);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (long_options[match].has_arg == required_argument ||
|
||||||
|
long_options[match].has_arg == optional_argument) {
|
||||||
|
if (has_equal)
|
||||||
|
optarg = has_equal;
|
||||||
|
else if (long_options[match].has_arg ==
|
||||||
|
required_argument) {
|
||||||
|
/*
|
||||||
|
* optional argument doesn't use next nargv
|
||||||
|
*/
|
||||||
|
optarg = nargv[optind++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((long_options[match].has_arg == required_argument)
|
||||||
|
&& (optarg == NULL)) {
|
||||||
|
/*
|
||||||
|
* Missing argument; leading ':' indicates no error
|
||||||
|
* should be generated.
|
||||||
|
*/
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(recargstring,
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
current_dash,
|
||||||
|
#endif
|
||||||
|
current_argv);
|
||||||
|
/*
|
||||||
|
* XXX: GNU sets optopt to val regardless of flag
|
||||||
|
*/
|
||||||
|
if (long_options[match].flag == NULL)
|
||||||
|
optopt = long_options[match].val;
|
||||||
|
else
|
||||||
|
optopt = 0;
|
||||||
|
--optind;
|
||||||
|
return (BADARG);
|
||||||
|
}
|
||||||
|
} else { /* unknown option */
|
||||||
|
if (short_too) {
|
||||||
|
--optind;
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(illoptstring,
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
current_dash,
|
||||||
|
#endif
|
||||||
|
current_argv);
|
||||||
|
optopt = 0;
|
||||||
|
return (BADCH);
|
||||||
|
}
|
||||||
|
if (idx)
|
||||||
|
*idx = match;
|
||||||
|
if (long_options[match].flag) {
|
||||||
|
*long_options[match].flag = long_options[match].val;
|
||||||
|
return (0);
|
||||||
|
} else
|
||||||
|
return (long_options[match].val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getopt_internal --
|
||||||
|
* Parse argc/argv argument vector. Called by user level routines.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
getopt_internal(int nargc, char * const *nargv, const char *options,
|
||||||
|
const struct option *long_options, int *idx, int flags)
|
||||||
|
{
|
||||||
|
char *oli; /* option letter list index */
|
||||||
|
int optchar, short_too;
|
||||||
|
int posixly_correct; /* no static, can be changed on the fly */
|
||||||
|
|
||||||
|
if (options == NULL)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable GNU extensions if POSIXLY_CORRECT is set or options
|
||||||
|
* string begins with a '+'.
|
||||||
|
*/
|
||||||
|
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
if (*options == '-')
|
||||||
|
flags |= FLAG_ALLARGS;
|
||||||
|
else if (posixly_correct || *options == '+')
|
||||||
|
flags &= ~FLAG_PERMUTE;
|
||||||
|
#else
|
||||||
|
if (posixly_correct || *options == '+')
|
||||||
|
flags &= ~FLAG_PERMUTE;
|
||||||
|
else if (*options == '-')
|
||||||
|
flags |= FLAG_ALLARGS;
|
||||||
|
#endif
|
||||||
|
if (*options == '+' || *options == '-')
|
||||||
|
options++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX Some GNU programs (like cvs) set optind to 0 instead of
|
||||||
|
* XXX using optreset. Work around this braindamage.
|
||||||
|
*/
|
||||||
|
if (optind == 0)
|
||||||
|
optind = optreset = 1;
|
||||||
|
|
||||||
|
optarg = NULL;
|
||||||
|
if (optreset)
|
||||||
|
nonopt_start = nonopt_end = -1;
|
||||||
|
start:
|
||||||
|
if (optreset || !*place) { /* update scanning pointer */
|
||||||
|
optreset = 0;
|
||||||
|
if (optind >= nargc) { /* end of argument vector */
|
||||||
|
place = EMSG;
|
||||||
|
if (nonopt_end != -1) {
|
||||||
|
/* do permutation, if we have to */
|
||||||
|
permute_args(nonopt_start, nonopt_end,
|
||||||
|
optind, nargv);
|
||||||
|
optind -= nonopt_end - nonopt_start;
|
||||||
|
}
|
||||||
|
else if (nonopt_start != -1) {
|
||||||
|
/*
|
||||||
|
* If we skipped non-options, set optind
|
||||||
|
* to the first of them.
|
||||||
|
*/
|
||||||
|
optind = nonopt_start;
|
||||||
|
}
|
||||||
|
nonopt_start = nonopt_end = -1;
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if (*(place = nargv[optind]) != '-' ||
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
place[1] == '\0') {
|
||||||
|
#else
|
||||||
|
(place[1] == '\0' && strchr(options, '-') == NULL)) {
|
||||||
|
#endif
|
||||||
|
place = EMSG; /* found non-option */
|
||||||
|
if (flags & FLAG_ALLARGS) {
|
||||||
|
/*
|
||||||
|
* GNU extension:
|
||||||
|
* return non-option as argument to option 1
|
||||||
|
*/
|
||||||
|
optarg = nargv[optind++];
|
||||||
|
return (INORDER);
|
||||||
|
}
|
||||||
|
if (!(flags & FLAG_PERMUTE)) {
|
||||||
|
/*
|
||||||
|
* If no permutation wanted, stop parsing
|
||||||
|
* at first non-option.
|
||||||
|
*/
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
/* do permutation */
|
||||||
|
if (nonopt_start == -1)
|
||||||
|
nonopt_start = optind;
|
||||||
|
else if (nonopt_end != -1) {
|
||||||
|
permute_args(nonopt_start, nonopt_end,
|
||||||
|
optind, nargv);
|
||||||
|
nonopt_start = optind -
|
||||||
|
(nonopt_end - nonopt_start);
|
||||||
|
nonopt_end = -1;
|
||||||
|
}
|
||||||
|
optind++;
|
||||||
|
/* process next argument */
|
||||||
|
goto start;
|
||||||
|
}
|
||||||
|
if (nonopt_start != -1 && nonopt_end == -1)
|
||||||
|
nonopt_end = optind;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have "-" do nothing, if "--" we are done.
|
||||||
|
*/
|
||||||
|
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
|
||||||
|
optind++;
|
||||||
|
place = EMSG;
|
||||||
|
/*
|
||||||
|
* We found an option (--), so if we skipped
|
||||||
|
* non-options, we have to permute.
|
||||||
|
*/
|
||||||
|
if (nonopt_end != -1) {
|
||||||
|
permute_args(nonopt_start, nonopt_end,
|
||||||
|
optind, nargv);
|
||||||
|
optind -= nonopt_end - nonopt_start;
|
||||||
|
}
|
||||||
|
nonopt_start = nonopt_end = -1;
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check long options if:
|
||||||
|
* 1) we were passed some
|
||||||
|
* 2) the arg is not just "-"
|
||||||
|
* 3) either the arg starts with -- we are getopt_long_only()
|
||||||
|
*/
|
||||||
|
if (long_options != NULL && place != nargv[optind] &&
|
||||||
|
(*place == '-' || (flags & FLAG_LONGONLY))) {
|
||||||
|
short_too = 0;
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
dash_prefix = D_PREFIX;
|
||||||
|
#endif
|
||||||
|
if (*place == '-') {
|
||||||
|
place++; /* --foo long option */
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
dash_prefix = DD_PREFIX;
|
||||||
|
#endif
|
||||||
|
} else if (*place != ':' && strchr(options, *place) != NULL)
|
||||||
|
short_too = 1; /* could be short option too */
|
||||||
|
|
||||||
|
optchar = parse_long_options(nargv, options, long_options,
|
||||||
|
idx, short_too, flags);
|
||||||
|
if (optchar != -1) {
|
||||||
|
place = EMSG;
|
||||||
|
return (optchar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((optchar = (int)*place++) == (int)':' ||
|
||||||
|
(optchar == (int)'-' && *place != '\0') ||
|
||||||
|
(oli = strchr(options, optchar)) == NULL) {
|
||||||
|
/*
|
||||||
|
* If the user specified "-" and '-' isn't listed in
|
||||||
|
* options, return -1 (non-option) as per POSIX.
|
||||||
|
* Otherwise, it is an unknown option character (or ':').
|
||||||
|
*/
|
||||||
|
if (optchar == (int)'-' && *place == '\0')
|
||||||
|
return (-1);
|
||||||
|
if (!*place)
|
||||||
|
++optind;
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(posixly_correct ? illoptchar : gnuoptchar,
|
||||||
|
optchar);
|
||||||
|
#else
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(illoptchar, optchar);
|
||||||
|
#endif
|
||||||
|
optopt = optchar;
|
||||||
|
return (BADCH);
|
||||||
|
}
|
||||||
|
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
|
||||||
|
/* -W long-option */
|
||||||
|
if (*place) /* no space */
|
||||||
|
/* NOTHING */;
|
||||||
|
else if (++optind >= nargc) { /* no arg */
|
||||||
|
place = EMSG;
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(recargchar, optchar);
|
||||||
|
optopt = optchar;
|
||||||
|
return (BADARG);
|
||||||
|
} else /* white space */
|
||||||
|
place = nargv[optind];
|
||||||
|
#ifdef GNU_COMPATIBLE
|
||||||
|
dash_prefix = W_PREFIX;
|
||||||
|
#endif
|
||||||
|
optchar = parse_long_options(nargv, options, long_options,
|
||||||
|
idx, 0, flags);
|
||||||
|
place = EMSG;
|
||||||
|
return (optchar);
|
||||||
|
}
|
||||||
|
if (*++oli != ':') { /* doesn't take argument */
|
||||||
|
if (!*place)
|
||||||
|
++optind;
|
||||||
|
} else { /* takes (optional) argument */
|
||||||
|
optarg = NULL;
|
||||||
|
if (*place) /* no white space */
|
||||||
|
optarg = place;
|
||||||
|
else if (oli[1] != ':') { /* arg not optional */
|
||||||
|
if (++optind >= nargc) { /* no arg */
|
||||||
|
place = EMSG;
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(recargchar, optchar);
|
||||||
|
optopt = optchar;
|
||||||
|
return (BADARG);
|
||||||
|
} else
|
||||||
|
optarg = nargv[optind];
|
||||||
|
}
|
||||||
|
place = EMSG;
|
||||||
|
++optind;
|
||||||
|
}
|
||||||
|
/* dump back option letter */
|
||||||
|
return (optchar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getopt --
|
||||||
|
* Parse argc/argv argument vector.
|
||||||
|
*
|
||||||
|
* [eventually this will replace the BSD getopt]
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getopt(int nargc, char * const *nargv, const char *options)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't pass FLAG_PERMUTE to getopt_internal() since
|
||||||
|
* the BSD getopt(3) (unlike GNU) has never done this.
|
||||||
|
*
|
||||||
|
* Furthermore, since many privileged programs call getopt()
|
||||||
|
* before dropping privileges it makes sense to keep things
|
||||||
|
* as simple (and bug-free) as possible.
|
||||||
|
*/
|
||||||
|
return (getopt_internal(nargc, nargv, options, NULL, NULL, FLAG_PERMUTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getopt_long --
|
||||||
|
* Parse argc/argv argument vector.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getopt_long(int nargc, char * const *nargv, const char *options,
|
||||||
|
const struct option *long_options, int *idx)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||||
|
FLAG_PERMUTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getopt_long_only --
|
||||||
|
* Parse argc/argv argument vector.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||||
|
const struct option *long_options, int *idx)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||||
|
FLAG_PERMUTE|FLAG_LONGONLY));
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */
|
||||||
|
/* $FreeBSD: src/include/getopt.h,v 1.6.34.1 2010/12/21 17:10:29 kensmith Exp $ */
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
* by Dieter Baron and Thomas Klausner.
|
||||||
|
*
|
||||||
|
* 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. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the NetBSD
|
||||||
|
* Foundation, Inc. and its contributors.
|
||||||
|
* 4. Neither the name of The NetBSD 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 NETBSD FOUNDATION, INC. 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 _GETOPT_H_
|
||||||
|
#define _GETOPT_H_
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension.
|
||||||
|
* getopt() is declared here too for GNU programs.
|
||||||
|
*/
|
||||||
|
#define no_argument 0
|
||||||
|
#define required_argument 1
|
||||||
|
#define optional_argument 2
|
||||||
|
|
||||||
|
struct option {
|
||||||
|
/* name of long option */
|
||||||
|
const char *name;
|
||||||
|
/*
|
||||||
|
* one of no_argument, required_argument, and optional_argument:
|
||||||
|
* whether option takes an argument
|
||||||
|
*/
|
||||||
|
int has_arg;
|
||||||
|
/* if not NULL, set *flag to val when option found */
|
||||||
|
int *flag;
|
||||||
|
/* if flag not NULL, value to set *flag to; else return value */
|
||||||
|
int val;
|
||||||
|
};
|
||||||
|
|
||||||
|
int getopt_long(int, char * const *, const char *,
|
||||||
|
const struct option *, int *);
|
||||||
|
int getopt_long_only(int, char * const *, const char *,
|
||||||
|
const struct option *, int *);
|
||||||
|
#ifndef _GETOPT_DECLARED
|
||||||
|
#define _GETOPT_DECLARED
|
||||||
|
int getopt(int, char * const [], const char *);
|
||||||
|
|
||||||
|
extern char *optarg; /* getopt(3) external variables */
|
||||||
|
extern int optind, opterr, optopt;
|
||||||
|
#endif
|
||||||
|
#ifndef _OPTRESET_DECLARED
|
||||||
|
#define _OPTRESET_DECLARED
|
||||||
|
extern int optreset; /* getopt(3) external variable */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !_GETOPT_H_ */
|
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
#if HAVE_STDINT_H
|
||||||
|
# include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "aacenc.h"
|
||||||
|
|
||||||
|
int aacenc_is_sbr_active(const aacenc_param_t *params)
|
||||||
|
{
|
||||||
|
switch (params->profile) {
|
||||||
|
case AOT_SBR: case AOT_PS: case AOT_MP2_SBR: case AOT_MP2_PS:
|
||||||
|
case AOT_DABPLUS_SBR: case AOT_DABPLUS_PS:
|
||||||
|
case AOT_DRM_SBR: case AOT_DRM_MPEG_PS:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (params->profile == AOT_ER_AAC_ELD && params->lowdelay_sbr)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int aacenc_channel_mode(const pcm_sample_description_t *format)
|
||||||
|
{
|
||||||
|
uint32_t chanmask = format->channel_mask;
|
||||||
|
|
||||||
|
if (format->channels_per_frame > 6)
|
||||||
|
return 0;
|
||||||
|
if (!chanmask) {
|
||||||
|
static uint32_t defaults[] = { 0x4, 0x3, 0x7, 0, 0x37, 0x3f };
|
||||||
|
chanmask = defaults[format->channels_per_frame - 1];
|
||||||
|
}
|
||||||
|
switch (chanmask) {
|
||||||
|
case 0x3: return MODE_2;
|
||||||
|
case 0x4: return MODE_1;
|
||||||
|
case 0x7: return MODE_1_2;
|
||||||
|
case 0x37: return MODE_1_2_2;
|
||||||
|
case 0x3f: return MODE_1_2_2_1;
|
||||||
|
case 0x107: return MODE_1_2_1;
|
||||||
|
case 0x607: return MODE_1_2_2;
|
||||||
|
case 0x60f: return MODE_1_2_2_1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int aacenc_init(HANDLE_AACENCODER *encoder, const aacenc_param_t *params,
|
||||||
|
const pcm_sample_description_t *format,
|
||||||
|
AACENC_InfoStruct *info)
|
||||||
|
{
|
||||||
|
int channel_mode;
|
||||||
|
int aot;
|
||||||
|
|
||||||
|
*encoder = 0;
|
||||||
|
if ((channel_mode = aacenc_channel_mode(format)) == 0) {
|
||||||
|
fprintf(stderr, "ERROR: unsupported channel layout\n");
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
if (aacEncOpen(encoder, 0, 0) != AACENC_OK) {
|
||||||
|
fprintf(stderr, "ERROR: aacEncOpen() failed\n");
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
aot = (params->profile ? params->profile : AOT_AAC_LC);
|
||||||
|
if (aacEncoder_SetParam(*encoder, AACENC_AOT, aot) != AACENC_OK) {
|
||||||
|
fprintf(stderr, "ERROR: unsupported profile\n");
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
if (params->bitrate_mode == 0)
|
||||||
|
aacEncoder_SetParam(*encoder, AACENC_BITRATE, params->bitrate);
|
||||||
|
else if (aacEncoder_SetParam(*encoder, AACENC_BITRATEMODE,
|
||||||
|
params->bitrate_mode) != AACENC_OK) {
|
||||||
|
fprintf(stderr, "ERROR: unsupported bitrate mode\n");
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
if (aacEncoder_SetParam(*encoder, AACENC_SAMPLERATE,
|
||||||
|
format->sample_rate) != AACENC_OK) {
|
||||||
|
fprintf(stderr, "ERROR: unsupported sample rate\n");
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
aacEncoder_SetParam(*encoder, AACENC_CHANNELMODE, channel_mode);
|
||||||
|
aacEncoder_SetParam(*encoder, AACENC_BANDWIDTH, params->bandwidth);
|
||||||
|
aacEncoder_SetParam(*encoder, AACENC_CHANNELORDER, 1);
|
||||||
|
aacEncoder_SetParam(*encoder, AACENC_AFTERBURNER, !!params->afterburner);
|
||||||
|
|
||||||
|
if (aot == AOT_ER_AAC_ELD && params->lowdelay_sbr)
|
||||||
|
aacEncoder_SetParam(*encoder, AACENC_SBR_MODE, 1);
|
||||||
|
|
||||||
|
if (aacEncoder_SetParam(*encoder, AACENC_TRANSMUX,
|
||||||
|
params->transport_format) != AACENC_OK) {
|
||||||
|
fprintf(stderr, "ERROR: unsupported transport format\n");
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
if (aacEncoder_SetParam(*encoder, AACENC_SIGNALING_MODE,
|
||||||
|
params->sbr_signaling) != AACENC_OK) {
|
||||||
|
fprintf(stderr, "ERROR: unsupported transport format\n");
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
if (params->adts_crc_check)
|
||||||
|
aacEncoder_SetParam(*encoder, AACENC_PROTECTION, 1);
|
||||||
|
if (params->header_period)
|
||||||
|
aacEncoder_SetParam(*encoder, AACENC_HEADER_PERIOD,
|
||||||
|
params->header_period);
|
||||||
|
|
||||||
|
if (aacEncEncode(*encoder, 0, 0, 0, 0) != AACENC_OK) {
|
||||||
|
fprintf(stderr, "ERROR: encoder initialization failed\n");
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
if (aacEncInfo(*encoder, info) != AACENC_OK) {
|
||||||
|
fprintf(stderr, "ERROR: cannot retrieve encoder info\n");
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
FAIL:
|
||||||
|
if (encoder)
|
||||||
|
aacEncClose(encoder);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int aac_encode_frame(HANDLE_AACENCODER encoder,
|
||||||
|
const pcm_sample_description_t *format,
|
||||||
|
const int16_t *input, unsigned iframes,
|
||||||
|
uint8_t **output, uint32_t *olen, uint32_t *osize)
|
||||||
|
{
|
||||||
|
uint32_t ilen = iframes * format->channels_per_frame;
|
||||||
|
AACENC_BufDesc ibdesc = { 0 }, obdesc = { 0 };
|
||||||
|
AACENC_InArgs iargs = { 0 };
|
||||||
|
AACENC_OutArgs oargs = { 0 };
|
||||||
|
void *ibufs[] = { (void*)input };
|
||||||
|
void *obufs[1];
|
||||||
|
INT ibuf_ids[] = { IN_AUDIO_DATA };
|
||||||
|
INT obuf_ids[] = { OUT_BITSTREAM_DATA };
|
||||||
|
INT ibuf_sizes[] = { ilen * sizeof(int16_t) };
|
||||||
|
INT obuf_sizes[1];
|
||||||
|
INT ibuf_el_sizes[] = { sizeof(int16_t) };
|
||||||
|
INT obuf_el_sizes[] = { 1 };
|
||||||
|
AACENC_ERROR err;
|
||||||
|
unsigned channel_mode, obytes;
|
||||||
|
|
||||||
|
channel_mode = aacEncoder_GetParam(encoder, AACENC_CHANNELMODE);
|
||||||
|
obytes = 6144 / 8 * channel_mode;
|
||||||
|
if (!*output || *osize < obytes) {
|
||||||
|
*osize = obytes;
|
||||||
|
*output = realloc(*output, obytes);
|
||||||
|
}
|
||||||
|
obufs[0] = *output;
|
||||||
|
obuf_sizes[0] = obytes;
|
||||||
|
|
||||||
|
iargs.numInSamples = ilen ? ilen : -1; /* -1 for signaling EOF */
|
||||||
|
ibdesc.numBufs = 1;
|
||||||
|
ibdesc.bufs = ibufs;
|
||||||
|
ibdesc.bufferIdentifiers = ibuf_ids;
|
||||||
|
ibdesc.bufSizes = ibuf_sizes;
|
||||||
|
ibdesc.bufElSizes = ibuf_el_sizes;
|
||||||
|
obdesc.numBufs = 1;
|
||||||
|
obdesc.bufs = obufs;
|
||||||
|
obdesc.bufferIdentifiers = obuf_ids;
|
||||||
|
obdesc.bufSizes = obuf_sizes;
|
||||||
|
obdesc.bufElSizes = obuf_el_sizes;
|
||||||
|
|
||||||
|
err = aacEncEncode(encoder, &ibdesc, &obdesc, &iargs, &oargs);
|
||||||
|
if (err != AACENC_ENCODE_EOF && err != AACENC_OK) {
|
||||||
|
fprintf(stderr, "ERROR: aacEncEncode() failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*olen = oargs.numOutBytes;
|
||||||
|
return oargs.numInSamples;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#ifndef AACENC_H
|
||||||
|
#define AACENC_H
|
||||||
|
|
||||||
|
#include <fdk-aac/aacenc_lib.h>
|
||||||
|
#include "lpcm.h"
|
||||||
|
|
||||||
|
#define AACENC_PARAMS \
|
||||||
|
unsigned profile; \
|
||||||
|
unsigned bitrate; \
|
||||||
|
unsigned bitrate_mode; \
|
||||||
|
unsigned bandwidth; \
|
||||||
|
unsigned afterburner; \
|
||||||
|
unsigned lowdelay_sbr; \
|
||||||
|
unsigned sbr_signaling; \
|
||||||
|
unsigned transport_format; \
|
||||||
|
unsigned adts_crc_check; \
|
||||||
|
unsigned header_period;
|
||||||
|
|
||||||
|
typedef struct aacenc_param_t {
|
||||||
|
AACENC_PARAMS
|
||||||
|
} aacenc_param_t;
|
||||||
|
|
||||||
|
int aacenc_is_sbr_active(const aacenc_param_t *params);
|
||||||
|
|
||||||
|
int aacenc_init(HANDLE_AACENCODER *encoder, const aacenc_param_t *params,
|
||||||
|
const pcm_sample_description_t *format,
|
||||||
|
AACENC_InfoStruct *info);
|
||||||
|
|
||||||
|
int aac_encode_frame(HANDLE_AACENCODER encoder,
|
||||||
|
const pcm_sample_description_t *format,
|
||||||
|
const int16_t *input, unsigned iframes,
|
||||||
|
uint8_t **output, uint32_t *olen, uint32_t *osize);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#ifndef COMPAT_H
|
||||||
|
#define COMPAT_H
|
||||||
|
|
||||||
|
#ifndef HAVE_FSEEKO
|
||||||
|
# if _MSC_VER >= 1400
|
||||||
|
# define fseeko _fseeki64
|
||||||
|
# define ftello _ftelli64
|
||||||
|
# else
|
||||||
|
# define fseeko fseek
|
||||||
|
# define ftello ftell
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int64_t aacenc_timer(void);
|
||||||
|
FILE *aacenc_fopen(const char *name, const char *mode);
|
||||||
|
void aacenc_getmainargs(int *argc, char ***argv);
|
||||||
|
char *aacenc_to_utf8(const char *s);
|
||||||
|
int aacenc_fprintf(FILE *fp, const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
#if HAVE_STDINT_H
|
||||||
|
# include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
int64_t aacenc_timer(void)
|
||||||
|
{
|
||||||
|
struct timeval tv = { 0 };
|
||||||
|
gettimeofday(&tv, 0);
|
||||||
|
return (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *aacenc_fopen(const char *name, const char *mode)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
if (strcmp(name, "-") == 0)
|
||||||
|
fp = (mode[0] == 'r') ? stdin : stdout;
|
||||||
|
else
|
||||||
|
fp = fopen(name, mode);
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void aacenc_getmainargs(int *argc, char ***argv)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int aacenc_fprintf(FILE *fp, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int cnt;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
cnt = vfprintf(fp, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_ICONV
|
||||||
|
char *aacenc_to_utf8(const char *s)
|
||||||
|
{
|
||||||
|
return strdup(s);
|
||||||
|
}
|
||||||
|
#else /* HAVE_ICONV */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <iconv.h>
|
||||||
|
|
||||||
|
#if HAVE_LOCALCHARSET_H
|
||||||
|
#include <localcharset.h>
|
||||||
|
#elif HAVE_LANGINFO_H
|
||||||
|
#include <langinfo.h>
|
||||||
|
static const char *locale_charset(void)
|
||||||
|
{
|
||||||
|
return nl_langinfo(CODESET);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static const char *locale_charset(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static
|
||||||
|
int utf8_from_charset(const char *charset, const char *from, char **to)
|
||||||
|
{
|
||||||
|
iconv_t cd;
|
||||||
|
size_t fromlen, obsize, ibleft, obleft;
|
||||||
|
char *ip, *op;
|
||||||
|
|
||||||
|
cd = iconv_open("UTF-8", charset);
|
||||||
|
if (cd == (iconv_t)-1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
fromlen = strlen(from);
|
||||||
|
ibleft = fromlen;
|
||||||
|
obsize = 2;
|
||||||
|
obleft = obsize - 1;
|
||||||
|
*to = malloc(obsize);
|
||||||
|
ip = (char *)from;
|
||||||
|
op = *to;
|
||||||
|
|
||||||
|
while (ibleft > 0) {
|
||||||
|
if (iconv(cd, &ip, &ibleft, &op, &obleft) != (size_t)-1)
|
||||||
|
break;
|
||||||
|
else {
|
||||||
|
if (errno == E2BIG || obleft == 0) {
|
||||||
|
ptrdiff_t offset = op - *to;
|
||||||
|
obsize *= 2;
|
||||||
|
*to = realloc(*to, obsize);
|
||||||
|
op = *to + offset;
|
||||||
|
obleft = obsize - offset - 1;
|
||||||
|
}
|
||||||
|
if (errno == EILSEQ) {
|
||||||
|
++ip;
|
||||||
|
--ibleft;
|
||||||
|
*op++ = '?';
|
||||||
|
--obleft;
|
||||||
|
}
|
||||||
|
if (errno != E2BIG && errno != EILSEQ)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iconv_close(cd);
|
||||||
|
*op = 0;
|
||||||
|
if (fromlen > 0 && op == *to) {
|
||||||
|
free(op);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *aacenc_to_utf8(const char *s)
|
||||||
|
{
|
||||||
|
char *result;
|
||||||
|
const char *charset;
|
||||||
|
|
||||||
|
if ((charset = locale_charset()) == 0)
|
||||||
|
charset = "US-ASCII";
|
||||||
|
if (utf8_from_charset(charset, s, &result) < 0)
|
||||||
|
result = strdup(s);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif /* HAVE_ICONV */
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
#if HAVE_STDINT_H
|
||||||
|
# include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/timeb.h>
|
||||||
|
#include "compat.h"
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int newmode;
|
||||||
|
} _startupinfo;
|
||||||
|
|
||||||
|
extern
|
||||||
|
int __wgetmainargs(int *, wchar_t ***, wchar_t ***, int, _startupinfo *);
|
||||||
|
|
||||||
|
int64_t aacenc_timer(void)
|
||||||
|
{
|
||||||
|
struct __timeb64 tv;
|
||||||
|
_ftime64(&tv);
|
||||||
|
return (int64_t)tv.time * 1000 + tv.millitm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int codepage_decode_wchar(int codepage, const char *from, wchar_t **to)
|
||||||
|
{
|
||||||
|
int nc = MultiByteToWideChar(codepage, 0, from, -1, 0, 0);
|
||||||
|
if (nc == 0)
|
||||||
|
return -1;
|
||||||
|
*to = malloc(nc * sizeof(wchar_t));
|
||||||
|
MultiByteToWideChar(codepage, 0, from, -1, *to, nc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int codepage_encode_wchar(int codepage, const wchar_t *from, char **to)
|
||||||
|
{
|
||||||
|
int nc = WideCharToMultiByte(codepage, 0, from, -1, 0, 0, 0, 0);
|
||||||
|
if (nc == 0)
|
||||||
|
return -1;
|
||||||
|
*to = malloc(nc);
|
||||||
|
WideCharToMultiByte(codepage, 0, from, -1, *to, nc, 0, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *aacenc_fopen(const char *name, const char *mode)
|
||||||
|
{
|
||||||
|
wchar_t *wname, *wmode;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (strcmp(name, "-") == 0) {
|
||||||
|
fp = (mode[0] == 'r') ? stdin : stdout;
|
||||||
|
_setmode(_fileno(fp), _O_BINARY);
|
||||||
|
} else {
|
||||||
|
codepage_decode_wchar(CP_UTF8, name, &wname);
|
||||||
|
codepage_decode_wchar(CP_UTF8, mode, &wmode);
|
||||||
|
fp = _wfopen(wname, wmode);
|
||||||
|
free(wname);
|
||||||
|
free(wmode);
|
||||||
|
}
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void aacenc_getmainargs(int *argc, char ***argv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
wchar_t **wargv, **envp;
|
||||||
|
_startupinfo si = { 0 };
|
||||||
|
|
||||||
|
__wgetmainargs(argc, &wargv, &envp, 1, &si);
|
||||||
|
*argv = malloc((*argc + 1) * sizeof(char*));
|
||||||
|
for (i = 0; i < *argc; ++i)
|
||||||
|
codepage_encode_wchar(CP_UTF8, wargv[i], &(*argv)[i]);
|
||||||
|
(*argv)[*argc] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *aacenc_to_utf8(const char *s)
|
||||||
|
{
|
||||||
|
return _strdup(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
int aacenc_fprintf(FILE *fp, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int cnt;
|
||||||
|
HANDLE fh = (HANDLE)_get_osfhandle(_fileno(fp));
|
||||||
|
|
||||||
|
if (GetFileType(fh) != FILE_TYPE_CHAR) {
|
||||||
|
va_start(ap, fmt);
|
||||||
|
cnt = vfprintf(fp, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
} else {
|
||||||
|
char *s;
|
||||||
|
wchar_t *ws;
|
||||||
|
DWORD nw;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
cnt = _vscprintf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
s = malloc(cnt + 1);
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
cnt = _vsnprintf(s, cnt + 1, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
codepage_decode_wchar(CP_UTF8, s, &ws);
|
||||||
|
free(s);
|
||||||
|
fflush(fp);
|
||||||
|
WriteConsoleW(fh, ws, cnt, &nw, 0);
|
||||||
|
free(ws);
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
#if HAVE_STDINT_H
|
||||||
|
# include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "lpcm.h"
|
||||||
|
#include "m4af_endian.h"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# define inline __inline
|
||||||
|
# ifdef _M_IX86
|
||||||
|
inline int lrint(double x)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
_asm {
|
||||||
|
fld x
|
||||||
|
fistp n
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
# include <emmintrin.h>
|
||||||
|
inline int lrint(double x)
|
||||||
|
{
|
||||||
|
return _mm_cvtsd_si32(_mm_load_sd(&x));
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline int pcm_clip(int n, int min_value, int max_value)
|
||||||
|
{
|
||||||
|
if (n < min_value)
|
||||||
|
return min_value;
|
||||||
|
else if (n > max_value)
|
||||||
|
return max_value;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
inline float pcm_i2f(int32_t n)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
int32_t ivalue;
|
||||||
|
float fvalue;
|
||||||
|
} u;
|
||||||
|
u.ivalue = n;
|
||||||
|
return u.fvalue;
|
||||||
|
}
|
||||||
|
inline double pcm_i2d(int64_t n)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
int64_t ivalue;
|
||||||
|
double fvalue;
|
||||||
|
} u;
|
||||||
|
u.ivalue = n;
|
||||||
|
return u.fvalue;
|
||||||
|
}
|
||||||
|
inline int16_t pcm_quantize_s32(int32_t n)
|
||||||
|
{
|
||||||
|
n = ((n >> 15) + 1) >> 1;
|
||||||
|
return (n == 0x8000) ? 0x7fff : n;
|
||||||
|
}
|
||||||
|
inline int16_t pcm_quantize_f64(double v)
|
||||||
|
{
|
||||||
|
return pcm_clip(lrint(v * 32768.0), -32768, 32767);
|
||||||
|
}
|
||||||
|
inline int16_t pcm_s8_to_s16(int8_t n)
|
||||||
|
{
|
||||||
|
return n << 8;
|
||||||
|
}
|
||||||
|
inline int16_t pcm_u8_to_s16(uint8_t n)
|
||||||
|
{
|
||||||
|
return (n << 8) ^ 0x8000;
|
||||||
|
}
|
||||||
|
inline int16_t pcm_s16le_to_s16(int16_t n)
|
||||||
|
{
|
||||||
|
return m4af_ltoh16(n);
|
||||||
|
}
|
||||||
|
inline int16_t pcm_s16be_to_s16(int16_t n)
|
||||||
|
{
|
||||||
|
return m4af_btoh16(n);
|
||||||
|
}
|
||||||
|
inline int16_t pcm_u16le_to_s16(uint16_t n)
|
||||||
|
{
|
||||||
|
return m4af_ltoh16(n) ^ 0x8000;
|
||||||
|
}
|
||||||
|
inline int16_t pcm_u16be_to_s16(uint16_t n)
|
||||||
|
{
|
||||||
|
return m4af_btoh16(n) ^ 0x8000;
|
||||||
|
}
|
||||||
|
inline int32_t pcm_s24le_to_s32(uint8_t *p)
|
||||||
|
{
|
||||||
|
return p[0]<<8 | p[1]<<16 | p[2]<<24;
|
||||||
|
}
|
||||||
|
inline int32_t pcm_s24be_to_s32(uint8_t *p)
|
||||||
|
{
|
||||||
|
return p[0]<<24 | p[1]<<16 | p[2]<<8;
|
||||||
|
}
|
||||||
|
inline int32_t pcm_u24le_to_s32(uint8_t *p)
|
||||||
|
{
|
||||||
|
return pcm_s24le_to_s32(p) ^ 0x80000000;
|
||||||
|
}
|
||||||
|
inline int32_t pcm_u24be_to_s32(uint8_t *p)
|
||||||
|
{
|
||||||
|
return pcm_s24be_to_s32(p) ^ 0x80000000;
|
||||||
|
}
|
||||||
|
inline int16_t pcm_s24le_to_s16(uint8_t *p)
|
||||||
|
{
|
||||||
|
return pcm_quantize_s32(pcm_s24le_to_s32(p));
|
||||||
|
}
|
||||||
|
inline int16_t pcm_s24be_to_s16(uint8_t *p)
|
||||||
|
{
|
||||||
|
return pcm_quantize_s32(pcm_s24be_to_s32(p));
|
||||||
|
}
|
||||||
|
inline int16_t pcm_u24le_to_s16(uint8_t *p)
|
||||||
|
{
|
||||||
|
return pcm_quantize_s32(pcm_u24le_to_s32(p));
|
||||||
|
}
|
||||||
|
inline int16_t pcm_u24be_to_s16(uint8_t *p)
|
||||||
|
{
|
||||||
|
return pcm_quantize_s32(pcm_u24be_to_s32(p));
|
||||||
|
}
|
||||||
|
inline int16_t pcm_s32le_to_s16(int32_t n)
|
||||||
|
{
|
||||||
|
return pcm_quantize_s32(m4af_ltoh32(n));
|
||||||
|
}
|
||||||
|
inline int16_t pcm_s32be_to_s16(int32_t n)
|
||||||
|
{
|
||||||
|
return pcm_quantize_s32(m4af_btoh32(n));
|
||||||
|
}
|
||||||
|
inline int16_t pcm_u32le_to_s16(int32_t n)
|
||||||
|
{
|
||||||
|
return pcm_quantize_s32(m4af_ltoh32(n) ^ 0x80000000);
|
||||||
|
}
|
||||||
|
inline int16_t pcm_u32be_to_s16(int32_t n)
|
||||||
|
{
|
||||||
|
return pcm_quantize_s32(m4af_btoh32(n) ^ 0x80000000);
|
||||||
|
}
|
||||||
|
inline int16_t pcm_f32le_to_s16(int32_t n)
|
||||||
|
{
|
||||||
|
return pcm_quantize_f64(pcm_i2f(m4af_ltoh32(n)));
|
||||||
|
}
|
||||||
|
inline int16_t pcm_f32be_to_s16(int32_t n)
|
||||||
|
{
|
||||||
|
return pcm_quantize_f64(pcm_i2f(m4af_btoh32(n)));
|
||||||
|
}
|
||||||
|
inline int16_t pcm_f64le_to_s16(int64_t n)
|
||||||
|
{
|
||||||
|
return pcm_quantize_f64(pcm_i2d(m4af_ltoh64(n)));
|
||||||
|
}
|
||||||
|
inline int16_t pcm_f64be_to_s16(int64_t n)
|
||||||
|
{
|
||||||
|
return pcm_quantize_f64(pcm_i2d(m4af_btoh64(n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcm_convert_to_native_sint16(const pcm_sample_description_t *format,
|
||||||
|
const void *input, uint32_t nframes,
|
||||||
|
int16_t **result, uint32_t *osize)
|
||||||
|
{
|
||||||
|
#define CONVERT(type, conv) \
|
||||||
|
do { \
|
||||||
|
unsigned i; \
|
||||||
|
type *ip = (type *)input; \
|
||||||
|
for (i = 0; i < count; ++i) { \
|
||||||
|
(*result)[i] = conv(ip[i]); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define CONVERT_BYTES(conv) \
|
||||||
|
do { \
|
||||||
|
unsigned i, bytes_per_channel; \
|
||||||
|
uint8_t *ip = (uint8_t *)input; \
|
||||||
|
bytes_per_channel = PCM_BYTES_PER_CHANNEL(format); \
|
||||||
|
for (i = 0; i < count; ++i) { \
|
||||||
|
(*result)[i] = conv(ip); \
|
||||||
|
ip += bytes_per_channel; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
uint32_t count = nframes * format->channels_per_frame;
|
||||||
|
if (!count)
|
||||||
|
return 0;
|
||||||
|
if (!*result || *osize < count) {
|
||||||
|
*osize = count;
|
||||||
|
*result = realloc(*result, count * sizeof(int16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (PCM_BYTES_PER_CHANNEL(format) | format->sample_type<<4) {
|
||||||
|
case 1 | PCM_TYPE_SINT<<4:
|
||||||
|
CONVERT(int8_t, pcm_s8_to_s16); break;
|
||||||
|
case 1 | PCM_TYPE_UINT<<4:
|
||||||
|
CONVERT(uint8_t, pcm_u8_to_s16); break;
|
||||||
|
case 2 | PCM_TYPE_SINT<<4:
|
||||||
|
CONVERT(int16_t, pcm_s16le_to_s16); break;
|
||||||
|
case 2 | PCM_TYPE_UINT<<4:
|
||||||
|
CONVERT(uint16_t, pcm_u16le_to_s16); break;
|
||||||
|
case 2 | PCM_TYPE_SINT_BE<<4:
|
||||||
|
CONVERT(int16_t, pcm_s16be_to_s16); break;
|
||||||
|
case 2 | PCM_TYPE_UINT_BE<<4:
|
||||||
|
CONVERT(int16_t, pcm_u16be_to_s16); break;
|
||||||
|
case 3 | PCM_TYPE_SINT<<4:
|
||||||
|
CONVERT_BYTES(pcm_s24le_to_s16); break;
|
||||||
|
case 3 | PCM_TYPE_UINT<<4:
|
||||||
|
CONVERT_BYTES(pcm_u24le_to_s16); break;
|
||||||
|
case 3 | PCM_TYPE_SINT_BE<<4:
|
||||||
|
CONVERT_BYTES(pcm_s24be_to_s16); break;
|
||||||
|
case 3 | PCM_TYPE_UINT_BE<<4:
|
||||||
|
CONVERT_BYTES(pcm_u24be_to_s16); break;
|
||||||
|
case 4 | PCM_TYPE_SINT<<4:
|
||||||
|
CONVERT(int32_t, pcm_s32le_to_s16); break;
|
||||||
|
case 4 | PCM_TYPE_UINT<<4:
|
||||||
|
CONVERT(uint32_t, pcm_u32le_to_s16); break;
|
||||||
|
case 4 | PCM_TYPE_FLOAT<<4:
|
||||||
|
CONVERT(int32_t, pcm_f32le_to_s16); break;
|
||||||
|
case 4 | PCM_TYPE_SINT_BE<<4:
|
||||||
|
CONVERT(int32_t, pcm_s32be_to_s16); break;
|
||||||
|
case 4 | PCM_TYPE_UINT_BE<<4:
|
||||||
|
CONVERT(uint32_t, pcm_u32be_to_s16); break;
|
||||||
|
case 4 | PCM_TYPE_FLOAT_BE<<4:
|
||||||
|
CONVERT(int32_t, pcm_f32be_to_s16); break;
|
||||||
|
case 8 | PCM_TYPE_FLOAT<<4:
|
||||||
|
CONVERT(int64_t, pcm_f64le_to_s16); break;
|
||||||
|
case 8 | PCM_TYPE_FLOAT_BE<<4:
|
||||||
|
CONVERT(int64_t, pcm_f64be_to_s16); break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#ifndef LPCM_H
|
||||||
|
#define LPCM_H
|
||||||
|
|
||||||
|
enum pcm_type {
|
||||||
|
PCM_TYPE_UNKNOWN = 0,
|
||||||
|
PCM_TYPE_SINT = 1,
|
||||||
|
PCM_TYPE_UINT = 2,
|
||||||
|
PCM_TYPE_FLOAT = 4,
|
||||||
|
PCM_TYPE_SINT_BE = (8|1),
|
||||||
|
PCM_TYPE_UINT_BE = (8|2),
|
||||||
|
PCM_TYPE_FLOAT_BE = (8|4),
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct pcm_sample_description_t {
|
||||||
|
enum pcm_type sample_type;
|
||||||
|
uint32_t sample_rate;
|
||||||
|
uint8_t bits_per_channel;
|
||||||
|
uint8_t bytes_per_frame;
|
||||||
|
uint8_t channels_per_frame;
|
||||||
|
uint32_t channel_mask;
|
||||||
|
} pcm_sample_description_t;
|
||||||
|
|
||||||
|
#define PCM_IS_SINT(desc) ((desc)->sample_type & 1)
|
||||||
|
#define PCM_IS_UINT(desc) ((desc)->sample_type & 2)
|
||||||
|
#define PCM_IS_FLOAT(desc) ((desc)->sample_type & 4)
|
||||||
|
#define PCM_IS_BIG_ENDIAN(desc) ((desc)->sample_type & 8)
|
||||||
|
#define PCM_BYTES_PER_CHANNEL(desc) \
|
||||||
|
((desc)->bytes_per_frame / (desc)->channels_per_frame)
|
||||||
|
|
||||||
|
int pcm_convert_to_native_sint16(const pcm_sample_description_t *format,
|
||||||
|
const void *input, uint32_t nframes,
|
||||||
|
int16_t **result, uint32_t *osize);
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#ifndef M4AF_H
|
||||||
|
#define M4AF_H
|
||||||
|
|
||||||
|
#define M4AF_FOURCC(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d))
|
||||||
|
|
||||||
|
enum m4af_error_code {
|
||||||
|
M4AF_IO_ERROR = 1,
|
||||||
|
M4AF_NO_MEMORY,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum m4af_itmf_tag {
|
||||||
|
M4AF_TAG_TITLE = M4AF_FOURCC('\xa9','n','a','m'),
|
||||||
|
M4AF_TAG_ARTIST = M4AF_FOURCC('\xa9','A','R','T'),
|
||||||
|
M4AF_TAG_ALBUM = M4AF_FOURCC('\xa9','a','l','b'),
|
||||||
|
M4AF_TAG_GENRE = M4AF_FOURCC('\xa9','g','e','n'),
|
||||||
|
M4AF_TAG_DATE = M4AF_FOURCC('\xa9','d','a','y'),
|
||||||
|
M4AF_TAG_COMPOSER = M4AF_FOURCC('\xa9','w','r','t'),
|
||||||
|
M4AF_TAG_GROUPING = M4AF_FOURCC('\xa9','g','r','p'),
|
||||||
|
M4AF_TAG_COMMENT = M4AF_FOURCC('\xa9','c','m','t'),
|
||||||
|
M4AF_TAG_LYRICS = M4AF_FOURCC('\xa9','l','y','r'),
|
||||||
|
M4AF_TAG_TOOL = M4AF_FOURCC('\xa9','t','o','o'),
|
||||||
|
M4AF_TAG_ALBUM_ARTIST = M4AF_FOURCC('a','A','R','T'),
|
||||||
|
M4AF_TAG_TRACK = M4AF_FOURCC('t','r','k','n'),
|
||||||
|
M4AF_TAG_DISK = M4AF_FOURCC('d','i','s','k'),
|
||||||
|
M4AF_TAG_GENRE_ID3 = M4AF_FOURCC('g','n','r','e'),
|
||||||
|
M4AF_TAG_TEMPO = M4AF_FOURCC('t','m','p','o'),
|
||||||
|
M4AF_TAG_DESCRIPTION = M4AF_FOURCC('d','e','s','c'),
|
||||||
|
M4AF_TAG_LONG_DESCRIPTION = M4AF_FOURCC('l','d','e','s'),
|
||||||
|
M4AF_TAG_COPYRIGHT = M4AF_FOURCC('c','p','r','t'),
|
||||||
|
M4AF_TAG_COMPILATION = M4AF_FOURCC('c','p','i','l'),
|
||||||
|
M4AF_TAG_ARTWORK = M4AF_FOURCC('c','o','v','r'),
|
||||||
|
};
|
||||||
|
|
||||||
|
enum m4af_itmf_type_code {
|
||||||
|
M4AF_IMPLICIT = 0,
|
||||||
|
M4AF_UTF8 = 1,
|
||||||
|
M4AF_GIF = 12,
|
||||||
|
M4AF_JPEG = 13,
|
||||||
|
M4AF_PNG = 14,
|
||||||
|
M4AF_INTEGER = 21,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum m4af_codec_type {
|
||||||
|
M4AF_CODEC_MP4A = M4AF_FOURCC('m','p','4','a'),
|
||||||
|
M4AF_CODEC_ALAC = M4AF_FOURCC('a','l','a','c'),
|
||||||
|
M4AF_CODEC_TEXT = M4AF_FOURCC('t','e','x','t'),
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*m4af_write_callback)(void *cookie, const void *data,
|
||||||
|
uint32_t size);
|
||||||
|
typedef int (*m4af_seek_callback)(void *cookie, int64_t off, int whence);
|
||||||
|
typedef int64_t (*m4af_tell_callback)(void *cookie);
|
||||||
|
|
||||||
|
typedef struct m4af_io_callbacks_t {
|
||||||
|
m4af_write_callback write;
|
||||||
|
m4af_seek_callback seek;
|
||||||
|
m4af_tell_callback tell;
|
||||||
|
} m4af_io_callbacks_t;
|
||||||
|
|
||||||
|
typedef struct m4af_writer_t m4af_writer_t;
|
||||||
|
|
||||||
|
m4af_writer_t *m4af_create(uint32_t codec, uint32_t timescale,
|
||||||
|
m4af_io_callbacks_t *io, void *io_cookie);
|
||||||
|
|
||||||
|
void m4af_teardown(m4af_writer_t **ctx);
|
||||||
|
|
||||||
|
int m4af_begin_write(m4af_writer_t *ctx);
|
||||||
|
|
||||||
|
int m4af_finalize(m4af_writer_t *ctx);
|
||||||
|
|
||||||
|
/* can be called before m4af_write_sample() */
|
||||||
|
void m4af_set_fixed_frame_duration(m4af_writer_t *ctx, int track_idx,
|
||||||
|
uint32_t length);
|
||||||
|
|
||||||
|
/* can be called between mfa4_begin_write() and m4af_finalize() */
|
||||||
|
int m4af_write_sample(m4af_writer_t *ctx, int track_idx, const void *data,
|
||||||
|
uint32_t size, uint32_t duration);
|
||||||
|
|
||||||
|
|
||||||
|
/* the following can be called at anytime before m4af_finalize() */
|
||||||
|
|
||||||
|
int m4af_decoder_specific_info(m4af_writer_t *ctx, int track_idx,
|
||||||
|
uint8_t *data, uint32_t size);
|
||||||
|
|
||||||
|
void m4af_set_priming(m4af_writer_t *ctx, int track_idx,
|
||||||
|
uint32_t encoder_delay, uint32_t padding);
|
||||||
|
|
||||||
|
int m4af_add_itmf_long_tag(m4af_writer_t *ctx, const char *name,
|
||||||
|
const char *data);
|
||||||
|
|
||||||
|
int m4af_add_itmf_short_tag(m4af_writer_t *ctx, uint32_t type,
|
||||||
|
uint32_t type_code, const void *data,
|
||||||
|
uint32_t data_size);
|
||||||
|
|
||||||
|
int m4af_add_itmf_string_tag(m4af_writer_t *ctx, uint32_t type,
|
||||||
|
const char *data);
|
||||||
|
|
||||||
|
int m4af_add_itmf_int8_tag(m4af_writer_t *ctx, uint32_t type, int value);
|
||||||
|
|
||||||
|
int m4af_add_itmf_int16_tag(m4af_writer_t *ctx, uint32_t type, int value);
|
||||||
|
|
||||||
|
int m4af_add_itmf_int32_tag(m4af_writer_t *ctx, uint32_t type, int value);
|
||||||
|
|
||||||
|
int m4af_add_itmf_track_tag(m4af_writer_t *ctx, int track, int total);
|
||||||
|
|
||||||
|
int m4af_add_itmf_disk_tag(m4af_writer_t *ctx, int disk, int total);
|
||||||
|
|
||||||
|
int m4af_add_itmf_genre_tag(m4af_writer_t *ctx, int genre);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#ifndef M4AF_ENDIAN_H
|
||||||
|
#define M4AF_ENDIAN_H
|
||||||
|
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
#if HAVE_STDINT_H
|
||||||
|
# include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_ENDIAN_H
|
||||||
|
# include <endian.h>
|
||||||
|
# define m4af_htob16(x) htobe16(x)
|
||||||
|
# define m4af_htob32(x) htobe32(x)
|
||||||
|
# define m4af_htob64(x) htobe64(x)
|
||||||
|
# define m4af_btoh16(x) be16toh(x)
|
||||||
|
# define m4af_btoh32(x) be32toh(x)
|
||||||
|
# define m4af_btoh64(x) be64toh(x)
|
||||||
|
# define m4af_htol16(x) htole16(x)
|
||||||
|
# define m4af_htol32(x) htole32(x)
|
||||||
|
# define m4af_htol64(x) htole64(x)
|
||||||
|
# define m4af_ltoh16(x) le16toh(x)
|
||||||
|
# define m4af_ltoh32(x) le32toh(x)
|
||||||
|
# define m4af_ltoh64(x) le64toh(x)
|
||||||
|
#elif WORDS_BIGENDIAN
|
||||||
|
# define m4af_htob16(x) (x)
|
||||||
|
# define m4af_htob32(x) (x)
|
||||||
|
# define m4af_htob64(x) (x)
|
||||||
|
# define m4af_btoh16(x) (x)
|
||||||
|
# define m4af_btoh32(x) (x)
|
||||||
|
# define m4af_btoh64(x) (x)
|
||||||
|
# define m4af_ltoh16(x) m4af_swap16(x)
|
||||||
|
# define m4af_ltoh32(x) m4af_swap32(x)
|
||||||
|
# define m4af_ltoh64(x) m4af_swap64(x)
|
||||||
|
# define m4af_htol16(x) m4af_swap16(x)
|
||||||
|
# define m4af_htol32(x) m4af_swap32(x)
|
||||||
|
# define m4af_htol64(x) m4af_swap64(x)
|
||||||
|
#else
|
||||||
|
# define m4af_htob16(x) m4af_swap16(x)
|
||||||
|
# define m4af_htob32(x) m4af_swap32(x)
|
||||||
|
# define m4af_htob64(x) m4af_swap64(x)
|
||||||
|
# define m4af_btoh16(x) m4af_swap16(x)
|
||||||
|
# define m4af_btoh32(x) m4af_swap32(x)
|
||||||
|
# define m4af_btoh64(x) m4af_swap64(x)
|
||||||
|
# define m4af_ltoh16(x) (x)
|
||||||
|
# define m4af_ltoh32(x) (x)
|
||||||
|
# define m4af_ltoh64(x) (x)
|
||||||
|
# define m4af_htol16(x) (x)
|
||||||
|
# define m4af_htol32(x) (x)
|
||||||
|
# define m4af_htol64(x) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if _MSC_VER >= 1400
|
||||||
|
# include <stdlib.h>
|
||||||
|
# define m4af_swap16(x) _byteswap_ushort(x)
|
||||||
|
# define m4af_swap32(x) _byteswap_ulong(x)
|
||||||
|
# define m4af_swap64(x) _byteswap_uint64(x)
|
||||||
|
#elif HAVE_BYTESWAP_H
|
||||||
|
# include <byteswap.h>
|
||||||
|
# define m4af_swap16(x) bswap_16(x)
|
||||||
|
# define m4af_swap32(x) bswap_32(x)
|
||||||
|
# define m4af_swap64(x) bswap_64(x)
|
||||||
|
#else
|
||||||
|
static inline uint16_t m4af_swap16(uint16_t x)
|
||||||
|
{
|
||||||
|
return (x >> 8) | (x << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t m4af_swap32(uint32_t x)
|
||||||
|
{
|
||||||
|
return (m4af_htob16(x) << 16) | m4af_htob16(x >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t m4af_swap64(uint64_t x)
|
||||||
|
{
|
||||||
|
return ((uint64_t)m4af_htob32(x) << 32) | m4af_htob32(x >> 32);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* M4AF_ENDIAN_H */
|
||||||
|
|
|
@ -0,0 +1,522 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
#if HAVE_STDINT_H
|
||||||
|
# include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include "compat.h"
|
||||||
|
#include "wav_reader.h"
|
||||||
|
#include "aacenc.h"
|
||||||
|
#include "m4af.h"
|
||||||
|
#include "progress.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
#define PROGNAME "fdkaac"
|
||||||
|
|
||||||
|
static
|
||||||
|
int read_callback(void *cookie, void *data, uint32_t size)
|
||||||
|
{
|
||||||
|
return fread(data, 1, size, (FILE*)cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int write_callback(void *cookie, const void *data, uint32_t size)
|
||||||
|
{
|
||||||
|
return fwrite(data, 1, size, (FILE*)cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int seek_callback(void *cookie, int64_t off, int whence)
|
||||||
|
{
|
||||||
|
return fseeko((FILE*)cookie, off, whence);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int64_t tell_callback(void *cookie)
|
||||||
|
{
|
||||||
|
return ftello((FILE*)cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void usage(void)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
PROGNAME " %s\n"
|
||||||
|
"Usage: " PROGNAME " [options] input_file\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -h, --help Print this help message\n"
|
||||||
|
" -p, --profile <n> Profile (audio object type)\n"
|
||||||
|
" 2: MPEG-4 AAC LC (default)\n"
|
||||||
|
" 5: MPEG-4 HE-AAC (SBR)\n"
|
||||||
|
" 29: MPEG-4 HE-AAC v2 (SBR+PS)\n"
|
||||||
|
" 23: MPEG-4 AAC LD\n"
|
||||||
|
" 39: MPEG-4 AAC ELD\n"
|
||||||
|
" 129: MPEG-2 AAC LC\n"
|
||||||
|
" 132: MPEG-2 HE-AAC (SBR)\n"
|
||||||
|
" 156: MPEG-2 HE-AAC v2 (SBR+PS)\n"
|
||||||
|
" -b, --bitrate <n> Bitrate in bits per seconds (for CBR)\n"
|
||||||
|
" -m, --bitrate-mode <n> Bitrate configuration\n"
|
||||||
|
" 0: CBR (default)\n"
|
||||||
|
" 1-5: VBR\n"
|
||||||
|
" (VBR mode is not officially supported, and\n"
|
||||||
|
" works only on a certain combination of\n"
|
||||||
|
" parameter settings, sample rate, and\n"
|
||||||
|
" channel configuration)\n"
|
||||||
|
" -w, --bandwidth <n> Frequency bandwidth in Hz (AAC LC only)\n"
|
||||||
|
" -a, --afterurner <n> Afterburner\n"
|
||||||
|
" 0: Off\n"
|
||||||
|
" 1: On(default)\n"
|
||||||
|
" -L, --lowdelay-sbr Enable ELD-SBR (AAC ELD only)\n"
|
||||||
|
" -s, --sbr-signaling <n> SBR signaling mode\n"
|
||||||
|
" 0: Implicit, backward compatible(default)\n"
|
||||||
|
" 1: Explicit SBR and implicit PS\n"
|
||||||
|
" 2: Explicit hierarchical signaling\n"
|
||||||
|
" -f, --transport-format <n> Transport format\n"
|
||||||
|
" 0: RAW (default, muxed into M4A)\n"
|
||||||
|
" 1: ADIF\n"
|
||||||
|
" 2: ADTS\n"
|
||||||
|
" 6: LATM MCP=1\n"
|
||||||
|
" 7: LATM MCP=0\n"
|
||||||
|
" 10: LOAS/LATM (LATM within LOAS)\n"
|
||||||
|
" -c, --adts-crc-check Add CRC protection on ADTS header\n"
|
||||||
|
" -h, --header-period <n> StreamMuxConfig/PCE repetition period in\n"
|
||||||
|
" transport layer\n"
|
||||||
|
"\n"
|
||||||
|
" -o <filename> Output filename\n"
|
||||||
|
" --ignore-length Ignore length of WAV header\n"
|
||||||
|
"\n"
|
||||||
|
"Tagging options:\n"
|
||||||
|
" --title <string>\n"
|
||||||
|
" --artist <string>\n"
|
||||||
|
" --album <string>\n"
|
||||||
|
" --genre <string>\n"
|
||||||
|
" --date <string>\n"
|
||||||
|
" --composer <string>\n"
|
||||||
|
" --grouping <string>\n"
|
||||||
|
" --comment <string>\n"
|
||||||
|
" --album-artist <string>\n"
|
||||||
|
" --track <number[/total]>\n"
|
||||||
|
" --disk <number[/total]>\n"
|
||||||
|
" --tempo <n>\n"
|
||||||
|
, fdkaac_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct aacenc_tag_entry_t {
|
||||||
|
uint32_t tag;
|
||||||
|
const char *data;
|
||||||
|
} aacenc_tag_entry_t;
|
||||||
|
|
||||||
|
typedef struct aacenc_param_ex_t {
|
||||||
|
AACENC_PARAMS
|
||||||
|
|
||||||
|
char *input_filename;
|
||||||
|
char *output_filename;
|
||||||
|
unsigned ignore_length;
|
||||||
|
|
||||||
|
aacenc_tag_entry_t *tag_table;
|
||||||
|
unsigned tag_count;
|
||||||
|
unsigned tag_table_capacity;
|
||||||
|
} aacenc_param_ex_t;
|
||||||
|
|
||||||
|
static
|
||||||
|
int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
|
||||||
|
{
|
||||||
|
int ch;
|
||||||
|
unsigned n;
|
||||||
|
aacenc_tag_entry_t *tag;
|
||||||
|
|
||||||
|
static struct option long_options[] = {
|
||||||
|
{ "help", no_argument, 0, 'h' },
|
||||||
|
{ "profile", required_argument, 0, 'p' },
|
||||||
|
{ "bitrate", required_argument, 0, 'b' },
|
||||||
|
{ "biterate-mode", required_argument, 0, 'm' },
|
||||||
|
{ "bandwidth", required_argument, 0, 'w' },
|
||||||
|
{ "afterburner", required_argument, 0, 'a' },
|
||||||
|
{ "lowdelay-sbr", no_argument, 0, 'L' },
|
||||||
|
{ "sbr-signaling", required_argument, 0, 's' },
|
||||||
|
{ "transport-format", required_argument, 0, 'f' },
|
||||||
|
{ "adts-crc-check", no_argument, 0, 'c' },
|
||||||
|
{ "header-period", required_argument, 0, 'P' },
|
||||||
|
|
||||||
|
{ "ignore-length", no_argument, 0, 'I' },
|
||||||
|
|
||||||
|
{ "title", required_argument, 0, M4AF_TAG_TITLE },
|
||||||
|
{ "artist", required_argument, 0, M4AF_TAG_ARTIST },
|
||||||
|
{ "album", required_argument, 0, M4AF_TAG_ALBUM },
|
||||||
|
{ "genre", required_argument, 0, M4AF_TAG_GENRE },
|
||||||
|
{ "date", required_argument, 0, M4AF_TAG_DATE },
|
||||||
|
{ "composer", required_argument, 0, M4AF_TAG_COMPOSER },
|
||||||
|
{ "grouping", required_argument, 0, M4AF_TAG_GROUPING },
|
||||||
|
{ "comment", required_argument, 0, M4AF_TAG_COMMENT },
|
||||||
|
{ "album-artist", required_argument, 0, M4AF_TAG_ALBUM_ARTIST },
|
||||||
|
{ "track", required_argument, 0, M4AF_TAG_TRACK },
|
||||||
|
{ "disk", required_argument, 0, M4AF_TAG_DISK },
|
||||||
|
{ "tempo", required_argument, 0, M4AF_TAG_TEMPO },
|
||||||
|
};
|
||||||
|
params->afterburner = 1;
|
||||||
|
|
||||||
|
aacenc_getmainargs(&argc, &argv);
|
||||||
|
while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:cP:Io:",
|
||||||
|
long_options, 0)) != EOF) {
|
||||||
|
switch (ch) {
|
||||||
|
case 'h':
|
||||||
|
return usage(), -1;
|
||||||
|
case 'p':
|
||||||
|
if (sscanf(optarg, "%u", &n) != 1) {
|
||||||
|
fprintf(stderr, "invalid arg for profile\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
params->profile = n;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
if (sscanf(optarg, "%u", &n) != 1) {
|
||||||
|
fprintf(stderr, "invalid arg for bitrate\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
params->bitrate = n;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
if (sscanf(optarg, "%u", &n) != 1 || n > 5) {
|
||||||
|
fprintf(stderr, "invalid arg for bitrate-mode\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
params->bitrate_mode = n;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
if (sscanf(optarg, "%u", &n) != 1) {
|
||||||
|
fprintf(stderr, "invalid arg for bandwidth\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
params->bandwidth = n;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
if (sscanf(optarg, "%u", &n) != 1 || n > 1) {
|
||||||
|
fprintf(stderr, "invalid arg for afterburner\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
params->afterburner = n;
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
params->lowdelay_sbr = 1;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if (sscanf(optarg, "%u", &n) != 1 || n > 2) {
|
||||||
|
fprintf(stderr, "invalid arg for sbr-signaling\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
params->sbr_signaling = n;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (sscanf(optarg, "%u", &n) != 1) {
|
||||||
|
fprintf(stderr, "invalid arg for transport-format\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
params->transport_format = n;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
params->adts_crc_check = 1;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
if (sscanf(optarg, "%u", &n) != 1) {
|
||||||
|
fprintf(stderr, "invalid arg for header-period\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
params->header_period = n;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
params->output_filename = optarg;
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
params->ignore_length = 1;
|
||||||
|
break;
|
||||||
|
case M4AF_TAG_TITLE:
|
||||||
|
case M4AF_TAG_ARTIST:
|
||||||
|
case M4AF_TAG_ALBUM:
|
||||||
|
case M4AF_TAG_GENRE:
|
||||||
|
case M4AF_TAG_DATE:
|
||||||
|
case M4AF_TAG_COMPOSER:
|
||||||
|
case M4AF_TAG_GROUPING:
|
||||||
|
case M4AF_TAG_COMMENT:
|
||||||
|
case M4AF_TAG_ALBUM_ARTIST:
|
||||||
|
case M4AF_TAG_TRACK:
|
||||||
|
case M4AF_TAG_DISK:
|
||||||
|
case M4AF_TAG_TEMPO:
|
||||||
|
if (params->tag_count == params->tag_table_capacity) {
|
||||||
|
unsigned newsize = params->tag_table_capacity;
|
||||||
|
newsize = newsize ? newsize * 2 : 1;
|
||||||
|
params->tag_table =
|
||||||
|
realloc(params->tag_table,
|
||||||
|
newsize * sizeof(aacenc_tag_entry_t));
|
||||||
|
params->tag_table_capacity = newsize;
|
||||||
|
}
|
||||||
|
tag = params->tag_table + params->tag_count;
|
||||||
|
tag->tag = ch;
|
||||||
|
tag->data = optarg;
|
||||||
|
params->tag_count++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return usage(), -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argc == optind)
|
||||||
|
return usage(), -1;
|
||||||
|
if (!params->bitrate && !params->bitrate_mode) {
|
||||||
|
fprintf(stderr, "bitrate or bitrate-mode is mandatory\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (params->output_filename && !strcmp(params->output_filename, "-") &&
|
||||||
|
!params->transport_format) {
|
||||||
|
fprintf(stderr, "stdout streaming is not available on M4A output\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (params->bitrate && params->bitrate < 10000)
|
||||||
|
params->bitrate *= 1000;
|
||||||
|
params->input_filename = argv[optind];
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
int write_sample(FILE *ofp, m4af_writer_t *m4af,
|
||||||
|
const void *data, uint32_t size, uint32_t duration)
|
||||||
|
{
|
||||||
|
if (!m4af) {
|
||||||
|
if (fwrite(data, 1, size, ofp) < 0) {
|
||||||
|
fprintf(stderr, "ERROR: fwrite(): %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (m4af_write_sample(m4af, 0, data, size, duration) < 0) {
|
||||||
|
fprintf(stderr, "ERROR: failed to write m4a sample\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int encode(wav_reader_t *wavf, HANDLE_AACENCODER encoder,
|
||||||
|
uint32_t frame_length, FILE *ofp, m4af_writer_t *m4af)
|
||||||
|
{
|
||||||
|
uint8_t *ibuf = 0;
|
||||||
|
int16_t *pcmbuf = 0;
|
||||||
|
uint32_t pcmsize = 0;
|
||||||
|
uint8_t *obuf = 0;
|
||||||
|
uint32_t olen;
|
||||||
|
uint32_t osize = 0;
|
||||||
|
int nread = 1;
|
||||||
|
int consumed;
|
||||||
|
int rc = -1;
|
||||||
|
int frames_written = 0;
|
||||||
|
aacenc_progress_t progress = { 0 };
|
||||||
|
const pcm_sample_description_t *format = wav_get_format(wavf);
|
||||||
|
|
||||||
|
ibuf = malloc(frame_length * format->bytes_per_frame);
|
||||||
|
aacenc_progress_init(&progress, wav_get_length(wavf), format->sample_rate);
|
||||||
|
do {
|
||||||
|
if (nread) {
|
||||||
|
if ((nread = wav_read_frames(wavf, ibuf, frame_length)) < 0) {
|
||||||
|
fprintf(stderr, "ERROR: read failed\n");
|
||||||
|
goto END;
|
||||||
|
} else if (nread > 0) {
|
||||||
|
if (pcm_convert_to_native_sint16(format, ibuf, nread,
|
||||||
|
&pcmbuf, &pcmsize) < 0) {
|
||||||
|
fprintf(stderr, "ERROR: unsupported sample format\n");
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aacenc_progress_update(&progress, wav_get_position(wavf),
|
||||||
|
format->sample_rate * 2);
|
||||||
|
}
|
||||||
|
if ((consumed = aac_encode_frame(encoder, format, pcmbuf, nread,
|
||||||
|
&obuf, &olen, &osize)) < 0)
|
||||||
|
goto END;
|
||||||
|
if (olen > 0) {
|
||||||
|
if (write_sample(ofp, m4af, obuf, olen, frame_length) < 0)
|
||||||
|
goto END;
|
||||||
|
++frames_written;
|
||||||
|
}
|
||||||
|
} while (nread > 0 || olen > 0);
|
||||||
|
aacenc_progress_finish(&progress, wav_get_position(wavf));
|
||||||
|
rc = frames_written;
|
||||||
|
END:
|
||||||
|
if (ibuf) free(ibuf);
|
||||||
|
if (pcmbuf) free(pcmbuf);
|
||||||
|
if (obuf) free(obuf);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int finalize_m4a(m4af_writer_t *m4af, const aacenc_param_ex_t *params,
|
||||||
|
HANDLE_AACENCODER encoder)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
aacenc_tag_entry_t *tag = params->tag_table;
|
||||||
|
|
||||||
|
for (i = 0; i < params->tag_count; ++i, ++tag) {
|
||||||
|
switch (tag->tag) {
|
||||||
|
case M4AF_TAG_TRACK:
|
||||||
|
{
|
||||||
|
unsigned m, n = 0;
|
||||||
|
if (sscanf(tag->data, "%u/%u", &m, &n) >= 1)
|
||||||
|
m4af_add_itmf_track_tag(m4af, m, n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case M4AF_TAG_DISK:
|
||||||
|
{
|
||||||
|
unsigned m, n = 0;
|
||||||
|
if (sscanf(tag->data, "%u/%u", &m, &n) >= 1)
|
||||||
|
m4af_add_itmf_disk_tag(m4af, m, n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case M4AF_TAG_TEMPO:
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
if (sscanf(tag->data, "%u", &n) == 1)
|
||||||
|
m4af_add_itmf_int16_tag(m4af, tag->tag, n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
char *u8 = aacenc_to_utf8(tag->data);
|
||||||
|
m4af_add_itmf_string_tag(m4af, tag->tag, u8);
|
||||||
|
free(u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char tool_info[256];
|
||||||
|
char *p = tool_info;
|
||||||
|
LIB_INFO *lib_info = 0;
|
||||||
|
|
||||||
|
p += sprintf(p, PROGNAME " %s, ", fdkaac_version);
|
||||||
|
|
||||||
|
lib_info = malloc(FDK_MODULE_LAST * sizeof(LIB_INFO));
|
||||||
|
/* XXX: aacEncGetLibInfo() seems buggy and sometimes fails */
|
||||||
|
if (aacEncGetLibInfo(lib_info) == AACENC_OK) {
|
||||||
|
for (i = 0; i < FDK_MODULE_LAST; ++i)
|
||||||
|
if (lib_info[i].module_id == FDK_AACENC)
|
||||||
|
break;
|
||||||
|
p += sprintf(p, "libfdk-aac %s, ", lib_info[i].versionStr);
|
||||||
|
}
|
||||||
|
free(lib_info);
|
||||||
|
if (params->bitrate_mode)
|
||||||
|
sprintf(p, "VBR mode %d", params->bitrate_mode);
|
||||||
|
else
|
||||||
|
sprintf(p, "CBR %dkbps", params->bitrate / 1000);
|
||||||
|
|
||||||
|
m4af_add_itmf_string_tag(m4af, M4AF_TAG_TOOL, tool_info);
|
||||||
|
}
|
||||||
|
if (m4af_finalize(m4af) < 0) {
|
||||||
|
fprintf(stderr, "ERROR: failed to finalize m4a\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
wav_io_context_t wav_io = { read_callback, seek_callback };
|
||||||
|
m4af_io_callbacks_t m4af_io = {
|
||||||
|
write_callback, seek_callback, tell_callback };
|
||||||
|
aacenc_param_ex_t params = { 0 };
|
||||||
|
|
||||||
|
int result = 2;
|
||||||
|
FILE *ifp = 0;
|
||||||
|
FILE *ofp = 0;
|
||||||
|
char *output_filename = 0;
|
||||||
|
wav_reader_t *wavf = 0;
|
||||||
|
HANDLE_AACENCODER encoder = 0;
|
||||||
|
AACENC_InfoStruct aacinfo = { 0 };
|
||||||
|
m4af_writer_t *m4af = 0;
|
||||||
|
const pcm_sample_description_t *sample_format;
|
||||||
|
int downsampled_timescale = 0;
|
||||||
|
int frame_count = 0;
|
||||||
|
|
||||||
|
setlocale(LC_CTYPE, "");
|
||||||
|
setbuf(stderr, 0);
|
||||||
|
|
||||||
|
if (parse_options(argc, argv, ¶ms) < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if ((ifp = aacenc_fopen(params.input_filename, "rb")) == 0) {
|
||||||
|
aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.input_filename,
|
||||||
|
strerror(errno));
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifp == stdin)
|
||||||
|
wav_io.seek = 0;
|
||||||
|
|
||||||
|
if ((wavf = wav_open(&wav_io, ifp, params.ignore_length)) == 0) {
|
||||||
|
fprintf(stderr, "ERROR: broken / unsupported input file\n");
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
sample_format = wav_get_format(wavf);
|
||||||
|
|
||||||
|
if (aacenc_init(&encoder, (aacenc_param_t*)¶ms, sample_format,
|
||||||
|
&aacinfo) < 0)
|
||||||
|
goto END;
|
||||||
|
|
||||||
|
if (!params.output_filename) {
|
||||||
|
size_t ilen = strlen(params.input_filename);
|
||||||
|
const char *ext = strrchr(params.input_filename, '.');
|
||||||
|
if (ext) ilen = ext - params.input_filename;
|
||||||
|
output_filename = malloc(ilen + 5);
|
||||||
|
sprintf(output_filename, "%.*s%s", ilen, params.input_filename,
|
||||||
|
params.transport_format ? ".aac" : ".m4a");
|
||||||
|
params.output_filename = output_filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ofp = aacenc_fopen(params.output_filename, "wb")) == 0) {
|
||||||
|
aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename,
|
||||||
|
strerror(errno));
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
if (!params.transport_format) {
|
||||||
|
uint32_t scale;
|
||||||
|
unsigned framelen = aacinfo.frameLength;
|
||||||
|
int sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)¶ms);
|
||||||
|
int sig_mode = aacEncoder_GetParam(encoder, AACENC_SIGNALING_MODE);
|
||||||
|
if (sbr_mode && !sig_mode)
|
||||||
|
downsampled_timescale = 1;
|
||||||
|
scale = sample_format->sample_rate >> downsampled_timescale;
|
||||||
|
if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io, ofp)) < 0)
|
||||||
|
goto END;
|
||||||
|
m4af_decoder_specific_info(m4af, 0, aacinfo.confBuf, aacinfo.confSize);
|
||||||
|
m4af_set_fixed_frame_duration(m4af, 0,
|
||||||
|
framelen >> downsampled_timescale);
|
||||||
|
m4af_begin_write(m4af);
|
||||||
|
}
|
||||||
|
frame_count = encode(wavf, encoder, aacinfo.frameLength, ofp, m4af);
|
||||||
|
if (frame_count < 0)
|
||||||
|
goto END;
|
||||||
|
if (m4af) {
|
||||||
|
uint32_t delay = aacinfo.encoderDelay;
|
||||||
|
int64_t frames_read = wav_get_position(wavf);
|
||||||
|
uint32_t padding = frame_count * aacinfo.frameLength
|
||||||
|
- frames_read - aacinfo.encoderDelay;
|
||||||
|
m4af_set_priming(m4af, 0, delay >> downsampled_timescale,
|
||||||
|
padding >> downsampled_timescale);
|
||||||
|
if (finalize_m4a(m4af, ¶ms, encoder) < 0)
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
result = 0;
|
||||||
|
END:
|
||||||
|
if (wavf) wav_teardown(&wavf);
|
||||||
|
if (ifp) fclose(ifp);
|
||||||
|
if (m4af) m4af_teardown(&m4af);
|
||||||
|
if (ofp) fclose(ofp);
|
||||||
|
if (encoder) aacEncClose(&encoder);
|
||||||
|
if (output_filename) free(output_filename);
|
||||||
|
if (params.tag_table) free(params.tag_table);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#if HAVE_STDINT_H
|
||||||
|
# include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#if HAVE_INTTYPES_H
|
||||||
|
# include <inttypes.h>
|
||||||
|
#elif defined _MSC_VER
|
||||||
|
# define PRId64 "I64d"
|
||||||
|
#endif
|
||||||
|
#include "compat.h"
|
||||||
|
#include "progress.h"
|
||||||
|
|
||||||
|
static
|
||||||
|
void seconds_to_hms(double seconds, int *h, int *m, int *s, int *millis)
|
||||||
|
{
|
||||||
|
*h = (int)(seconds / 3600.0);
|
||||||
|
seconds -= *h * 3600;
|
||||||
|
*m = (int)(seconds / 60.0);
|
||||||
|
seconds -= *m * 60;
|
||||||
|
*s = (int)seconds;
|
||||||
|
*millis = (int)((seconds - *s) * 1000.0 + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void print_seconds(FILE *fp, double seconds)
|
||||||
|
{
|
||||||
|
int h, m, s, millis;
|
||||||
|
seconds_to_hms(seconds, &h, &m, &s, &millis);
|
||||||
|
if (h)
|
||||||
|
fprintf(stderr, "%d:%02d:%02d.%03d", h, m, s, millis);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%02d:%02d.%03d", m, s, millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
void aacenc_progress_init(aacenc_progress_t *progress, int64_t total,
|
||||||
|
int32_t timescale)
|
||||||
|
{
|
||||||
|
progress->start = aacenc_timer();
|
||||||
|
progress->timescale = timescale;
|
||||||
|
progress->total = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
void aacenc_progress_update(aacenc_progress_t *progress, int64_t current,
|
||||||
|
int period)
|
||||||
|
{
|
||||||
|
int percent = 100.0 * current / progress->total + .5;
|
||||||
|
double seconds = current / progress->timescale;
|
||||||
|
double ellapsed = (aacenc_timer() - progress->start) / 1000.0;
|
||||||
|
double eta = ellapsed * (progress->total / (double)current - 1.0);
|
||||||
|
double speed = ellapsed ? seconds / ellapsed : 0.0;
|
||||||
|
if (current < progress->processed + period)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (progress->total == INT64_MAX) {
|
||||||
|
putc('\r', stderr);
|
||||||
|
print_seconds(stderr, seconds);
|
||||||
|
fprintf(stderr, " (%.0fx) ", speed);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "\r[%d%%] ", percent);
|
||||||
|
print_seconds(stderr, seconds);
|
||||||
|
putc('/', stderr);
|
||||||
|
print_seconds(stderr, progress->total / progress->timescale);
|
||||||
|
fprintf(stderr, " (%.0fx), ETA ", speed);
|
||||||
|
print_seconds(stderr, eta);
|
||||||
|
fputs(" ", stderr);
|
||||||
|
}
|
||||||
|
progress->processed = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
void aacenc_progress_finish(aacenc_progress_t *progress, int64_t current)
|
||||||
|
{
|
||||||
|
double ellapsed = (aacenc_timer() - progress->start) / 1000.0;
|
||||||
|
aacenc_progress_update(progress, current, 0);
|
||||||
|
if (progress->total == INT64_MAX)
|
||||||
|
fprintf(stderr, "\n%" PRId64 "samples processed in ", current);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "\n%" PRId64 "/%" PRId64 " samples processed in ",
|
||||||
|
current, progress->total);
|
||||||
|
print_seconds(stderr, ellapsed);
|
||||||
|
putc('\n', stderr);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#ifndef PROGRESS_H
|
||||||
|
#define PROGRESS_H
|
||||||
|
|
||||||
|
typedef struct aacenc_progress_t {
|
||||||
|
double start;
|
||||||
|
double timescale;
|
||||||
|
int64_t total;
|
||||||
|
int64_t processed;
|
||||||
|
} aacenc_progress_t;
|
||||||
|
|
||||||
|
void aacenc_progress_init(aacenc_progress_t *progress, int64_t total,
|
||||||
|
int32_t timescale);
|
||||||
|
void aacenc_progress_update(aacenc_progress_t *progress, int64_t current,
|
||||||
|
int period);
|
||||||
|
void aacenc_progress_finish(aacenc_progress_t *progress, int64_t current);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,333 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_STDINT_H
|
||||||
|
# include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include "wav_reader.h"
|
||||||
|
#include "m4af_endian.h"
|
||||||
|
|
||||||
|
#define RIFF_FOURCC(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
|
||||||
|
|
||||||
|
#define TRY_IO(expr) \
|
||||||
|
do { \
|
||||||
|
if (expr) \
|
||||||
|
goto FAIL; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT_FORMAT(ctx, expr) \
|
||||||
|
do { \
|
||||||
|
if (!expr) { \
|
||||||
|
if (!ctx->last_error) \
|
||||||
|
ctx->last_error = WAV_INVALID_FORMAT; \
|
||||||
|
goto FAIL;\
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
struct wav_reader_t {
|
||||||
|
pcm_sample_description_t sample_format;
|
||||||
|
int64_t length;
|
||||||
|
int64_t position;
|
||||||
|
int ignore_length;
|
||||||
|
int last_error;
|
||||||
|
wav_io_context_t io;
|
||||||
|
void *io_cookie;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t WAV_GUID_PCM[] = {
|
||||||
|
1, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71
|
||||||
|
};
|
||||||
|
static const uint8_t WAV_GUID_FLOAT[] = {
|
||||||
|
3, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71
|
||||||
|
};
|
||||||
|
|
||||||
|
const pcm_sample_description_t *wav_get_format(wav_reader_t *reader)
|
||||||
|
{
|
||||||
|
return &reader->sample_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t wav_get_length(wav_reader_t *reader)
|
||||||
|
{
|
||||||
|
return reader->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t wav_get_position(wav_reader_t *reader)
|
||||||
|
{
|
||||||
|
return reader->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wav_teardown(wav_reader_t **reader)
|
||||||
|
{
|
||||||
|
free(*reader);
|
||||||
|
*reader = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int riff_read(wav_reader_t *reader, void *buffer, uint32_t size)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
uint32_t count = 0;
|
||||||
|
|
||||||
|
if (reader->last_error)
|
||||||
|
return -1;
|
||||||
|
do {
|
||||||
|
rc = reader->io.read(reader->io_cookie, buffer, size - count);
|
||||||
|
if (rc > 0)
|
||||||
|
count += rc;
|
||||||
|
else if (rc < 0)
|
||||||
|
reader->last_error = WAV_IO_ERROR;
|
||||||
|
} while (rc > 0 && count < size);
|
||||||
|
return count > 0 ? count : rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int riff_skip(wav_reader_t *reader, uint32_t count)
|
||||||
|
{
|
||||||
|
char buff[8192];
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (reader->last_error)
|
||||||
|
return -1;
|
||||||
|
if (count == 0)
|
||||||
|
return 0;
|
||||||
|
if (reader->io.seek &&
|
||||||
|
reader->io.seek(reader->io_cookie, count, SEEK_CUR) >= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if ((rc = riff_read(reader, buff, count > 8192 ? 8192 : count)) > 0)
|
||||||
|
count -= rc;
|
||||||
|
} while (rc > 0 && count > 0);
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
reader->last_error = WAV_IO_ERROR;
|
||||||
|
return reader->last_error ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int riff_read16(wav_reader_t *reader, uint16_t *value)
|
||||||
|
{
|
||||||
|
TRY_IO(riff_read(reader, value, 2) != 2);
|
||||||
|
*value = m4af_ltoh16(*value);
|
||||||
|
return 0;
|
||||||
|
FAIL:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int riff_read32(wav_reader_t *reader, uint32_t *value)
|
||||||
|
{
|
||||||
|
TRY_IO(riff_read(reader, value, 4) != 4);
|
||||||
|
*value = m4af_ltoh32(*value);
|
||||||
|
return 0;
|
||||||
|
FAIL:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int riff_read64(wav_reader_t *reader, uint64_t *value)
|
||||||
|
{
|
||||||
|
TRY_IO(riff_read(reader, value, 8) != 8);
|
||||||
|
*value = m4af_ltoh64(*value);
|
||||||
|
return 0;
|
||||||
|
FAIL:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int riff_scan(wav_reader_t *reader, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
int c, count = 0;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
while ((c = *fmt++)) {
|
||||||
|
switch (c) {
|
||||||
|
case 'S':
|
||||||
|
TRY_IO(riff_read16(reader, va_arg(ap, uint16_t*)));
|
||||||
|
++count;
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
TRY_IO(riff_read32(reader, va_arg(ap, uint32_t*)));
|
||||||
|
++count;
|
||||||
|
break;
|
||||||
|
case 'Q':
|
||||||
|
TRY_IO(riff_read64(reader, va_arg(ap, uint64_t*)));
|
||||||
|
++count;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FAIL:
|
||||||
|
va_end(ap);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
uint32_t riff_next_chunk(wav_reader_t *reader, uint32_t *chunk_size)
|
||||||
|
{
|
||||||
|
uint32_t fcc;
|
||||||
|
if (riff_scan(reader, "LL", &fcc, chunk_size) == 2)
|
||||||
|
return fcc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wav_read_frames(wav_reader_t *reader, void *buffer, unsigned nframes)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
unsigned nbytes;
|
||||||
|
|
||||||
|
if (!reader->ignore_length && nframes > reader->length - reader->position)
|
||||||
|
nframes = reader->length - reader->position;
|
||||||
|
nbytes = nframes * reader->sample_format.bytes_per_frame;
|
||||||
|
if (nbytes) {
|
||||||
|
if ((rc = riff_read(reader, buffer, nbytes)) < 0)
|
||||||
|
return -1;
|
||||||
|
nframes = rc / reader->sample_format.bytes_per_frame;
|
||||||
|
reader->position += nframes;
|
||||||
|
}
|
||||||
|
return nframes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int riff_ds64(wav_reader_t *reader, int64_t *length)
|
||||||
|
{
|
||||||
|
uint32_t fcc, chunk_size, table_size;
|
||||||
|
uint64_t riff_size, sample_count;
|
||||||
|
|
||||||
|
fcc = riff_next_chunk(reader, &chunk_size);
|
||||||
|
ASSERT_FORMAT(reader,
|
||||||
|
fcc == RIFF_FOURCC('d','s','6','4') && chunk_size >= 28);
|
||||||
|
TRY_IO(riff_scan(reader, "QQQL",
|
||||||
|
&riff_size, length, &sample_count, &table_size) != 4);
|
||||||
|
if (table_size == 0)
|
||||||
|
return 0;
|
||||||
|
reader->last_error = WAV_UNSUPPORTED_FORMAT;
|
||||||
|
FAIL:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int wav_fmt(wav_reader_t *reader, uint32_t size)
|
||||||
|
{
|
||||||
|
uint16_t wFormatTag, nChannels, nBlockAlign, wBitsPerSample, cbSize;
|
||||||
|
uint32_t nSamplesPerSec, nAvgBytesPerSec, dwChannelMask = 0;
|
||||||
|
uint16_t wValidBitsPerSample;
|
||||||
|
uint8_t guid[16];
|
||||||
|
int is_float = 0;
|
||||||
|
|
||||||
|
ASSERT_FORMAT(reader, size >= 16);
|
||||||
|
TRY_IO(riff_scan(reader, "SSLLSS", &wFormatTag, &nChannels,
|
||||||
|
&nSamplesPerSec, &nAvgBytesPerSec, &nBlockAlign,
|
||||||
|
&wBitsPerSample) != 6);
|
||||||
|
wValidBitsPerSample = wBitsPerSample;
|
||||||
|
|
||||||
|
if (wFormatTag != 1 && wFormatTag != 3 && wFormatTag != 0xfffe) {
|
||||||
|
reader->last_error = WAV_UNSUPPORTED_FORMAT;
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
ASSERT_FORMAT(reader,
|
||||||
|
nChannels && nSamplesPerSec && nAvgBytesPerSec &&
|
||||||
|
nBlockAlign && wBitsPerSample && !(wBitsPerSample & 7) &&
|
||||||
|
nBlockAlign == nChannels * wBitsPerSample / 8);
|
||||||
|
if (wFormatTag == 3)
|
||||||
|
is_float = 1;
|
||||||
|
|
||||||
|
if (wFormatTag != 0xfffe)
|
||||||
|
TRY_IO(riff_skip(reader, (size - 15) & ~1));
|
||||||
|
else {
|
||||||
|
ASSERT_FORMAT(reader, size >= 40);
|
||||||
|
TRY_IO(riff_scan(reader, "SSL",
|
||||||
|
&cbSize, &wValidBitsPerSample, &dwChannelMask) != 3);
|
||||||
|
TRY_IO(riff_read(reader, guid, 16) != 16);
|
||||||
|
|
||||||
|
if (memcmp(guid, WAV_GUID_FLOAT, 16) == 0)
|
||||||
|
is_float = 1;
|
||||||
|
else if (memcmp(guid, WAV_GUID_PCM, 16) != 0) {
|
||||||
|
reader->last_error = WAV_UNSUPPORTED_FORMAT;
|
||||||
|
goto FAIL;
|
||||||
|
}
|
||||||
|
ASSERT_FORMAT(reader,
|
||||||
|
wValidBitsPerSample &&
|
||||||
|
wValidBitsPerSample <= wBitsPerSample);
|
||||||
|
TRY_IO(riff_skip(reader, (size - 39) & ~1));
|
||||||
|
}
|
||||||
|
reader->sample_format.sample_rate = nSamplesPerSec;
|
||||||
|
reader->sample_format.bits_per_channel = wValidBitsPerSample;
|
||||||
|
reader->sample_format.bytes_per_frame = nBlockAlign;
|
||||||
|
reader->sample_format.channels_per_frame = nChannels;
|
||||||
|
reader->sample_format.channel_mask = dwChannelMask;
|
||||||
|
if (is_float)
|
||||||
|
reader->sample_format.sample_type = PCM_TYPE_FLOAT;
|
||||||
|
else if (wBitsPerSample == 8)
|
||||||
|
reader->sample_format.sample_type = PCM_TYPE_UINT;
|
||||||
|
else
|
||||||
|
reader->sample_format.sample_type = PCM_TYPE_SINT;
|
||||||
|
return 0;
|
||||||
|
FAIL:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int wav_parse(wav_reader_t *reader, int64_t *data_length)
|
||||||
|
{
|
||||||
|
uint32_t container, fcc, chunk_size;
|
||||||
|
|
||||||
|
*data_length = 0;
|
||||||
|
container = riff_next_chunk(reader, &chunk_size);
|
||||||
|
if (container != RIFF_FOURCC('R','I','F','F') &&
|
||||||
|
container != RIFF_FOURCC('R','F','6','4'))
|
||||||
|
goto FAIL;
|
||||||
|
TRY_IO(riff_read32(reader, &fcc));
|
||||||
|
if (fcc != RIFF_FOURCC('W','A','V','E'))
|
||||||
|
goto FAIL;
|
||||||
|
if (container == RIFF_FOURCC('R','F','6','4'))
|
||||||
|
riff_ds64(reader, data_length);
|
||||||
|
while ((fcc = riff_next_chunk(reader, &chunk_size)) != 0) {
|
||||||
|
if (fcc == RIFF_FOURCC('f','m','t',' ')) {
|
||||||
|
if (wav_fmt(reader, chunk_size) < 0)
|
||||||
|
goto FAIL;
|
||||||
|
} else if (fcc == RIFF_FOURCC('d','a','t','a')) {
|
||||||
|
if (container == RIFF_FOURCC('R','I','F','F'))
|
||||||
|
*data_length = chunk_size;
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
TRY_IO(riff_skip(reader, (chunk_size + 1) & ~1));
|
||||||
|
}
|
||||||
|
if (fcc == RIFF_FOURCC('d','a','t','a'))
|
||||||
|
return 0;
|
||||||
|
FAIL:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wav_reader_t *wav_open(wav_io_context_t *io_ctx, void *io_cookie,
|
||||||
|
int ignore_length)
|
||||||
|
{
|
||||||
|
wav_reader_t *reader;
|
||||||
|
int64_t data_length;
|
||||||
|
|
||||||
|
if ((reader = calloc(1, sizeof(wav_reader_t))) == 0)
|
||||||
|
return 0;
|
||||||
|
memcpy(&reader->io, io_ctx, sizeof(wav_io_context_t));
|
||||||
|
reader->io_cookie = io_cookie;
|
||||||
|
reader->ignore_length = ignore_length;
|
||||||
|
if (wav_parse(reader, &data_length) < 0) {
|
||||||
|
free(reader);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ignore_length || !data_length ||
|
||||||
|
data_length % reader->sample_format.bytes_per_frame != 0)
|
||||||
|
reader->length = INT64_MAX;
|
||||||
|
else
|
||||||
|
reader->length = data_length / reader->sample_format.bytes_per_frame;
|
||||||
|
return reader;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 nu774
|
||||||
|
* For conditions of distribution and use, see copyright notice in COPYING
|
||||||
|
*/
|
||||||
|
#ifndef WAV_READER_H
|
||||||
|
#define WAV_READER_H
|
||||||
|
|
||||||
|
#include "lpcm.h"
|
||||||
|
|
||||||
|
enum wav_error_code {
|
||||||
|
WAV_IO_ERROR = 1,
|
||||||
|
WAV_NO_MEMORY,
|
||||||
|
WAV_INVALID_FORMAT,
|
||||||
|
WAV_UNSUPPORTED_FORMAT
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*wav_read_callback)(void *cookie, void *data, uint32_t size);
|
||||||
|
typedef int (*wav_seek_callback)(void *cookie, int64_t off, int whence);
|
||||||
|
|
||||||
|
typedef struct wav_io_context_t {
|
||||||
|
wav_read_callback read;
|
||||||
|
wav_seek_callback seek;
|
||||||
|
} wav_io_context_t;
|
||||||
|
|
||||||
|
typedef struct wav_reader_t wav_reader_t;
|
||||||
|
|
||||||
|
wav_reader_t *wav_open(wav_io_context_t *io_ctx, void *io_cookie,
|
||||||
|
int ignore_length);
|
||||||
|
const pcm_sample_description_t *wav_get_format(wav_reader_t *reader);
|
||||||
|
int wav_read_frames(wav_reader_t *reader, void *buffer, unsigned nframes);
|
||||||
|
int64_t wav_get_length(wav_reader_t *reader);
|
||||||
|
int64_t wav_get_position(wav_reader_t *reader);
|
||||||
|
void wav_teardown(wav_reader_t **reader);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue