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