initial commit

This commit is contained in:
nu774 2013-01-05 01:10:05 +09:00
commit 48e2f01c56
28 changed files with 4360 additions and 0 deletions

0
AUTHORS Normal file
View File

17
COPYING Normal file
View File

@ -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
ChangeLog Normal file
View File

370
INSTALL Normal file
View File

@ -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.

25
Makefile.am Normal file
View File

@ -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
NEWS Normal file
View File

14
README Normal file
View File

@ -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
config.rpath Normal file
View File

52
configure.ac Normal file
View File

@ -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
m4/.gitkeep Normal file
View File

630
missings/getopt.c Normal file
View File

@ -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));
}

90
missings/getopt.h Normal file
View File

@ -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_ */

172
src/aacenc.c Normal file
View File

@ -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;
}

38
src/aacenc.h Normal file
View File

@ -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

24
src/compat.h Normal file
View File

@ -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

138
src/compat_posix.c Normal file
View File

@ -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 */

128
src/compat_win32.c Normal file
View File

@ -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;
}

234
src/lpcm.c Normal file
View File

@ -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;
}

37
src/lpcm.h Normal file
View File

@ -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

1188
src/m4af.c Normal file

File diff suppressed because it is too large Load Diff

114
src/m4af.h Normal file
View File

@ -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

85
src/m4af_endian.h Normal file
View File

@ -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 */

522
src/main.c Normal file
View File

@ -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, &params) < 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*)&params, 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*)&params);
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, &params, 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;
}

89
src/progress.c Normal file
View File

@ -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);
}

21
src/progress.h Normal file
View File

@ -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

333
src/wav_reader.c Normal file
View File

@ -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;
}

35
src/wav_reader.h Normal file
View File

@ -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

4
version.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef VERSION_H
#define VERSION_H
const char *fdkaac_version = "0.0.1";
#endif