]> iEval git - fdkaac.git/commitdiff
initial commit v0.0.1
authornu774 <honeycomb77@gmail.com>
Fri, 4 Jan 2013 16:10:05 +0000 (01:10 +0900)
committernu774 <honeycomb77@gmail.com>
Fri, 4 Jan 2013 16:10:05 +0000 (01:10 +0900)
28 files changed:
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
config.rpath [new file with mode: 0644]
configure.ac [new file with mode: 0644]
m4/.gitkeep [new file with mode: 0644]
missings/getopt.c [new file with mode: 0644]
missings/getopt.h [new file with mode: 0644]
src/aacenc.c [new file with mode: 0644]
src/aacenc.h [new file with mode: 0644]
src/compat.h [new file with mode: 0644]
src/compat_posix.c [new file with mode: 0644]
src/compat_win32.c [new file with mode: 0644]
src/lpcm.c [new file with mode: 0644]
src/lpcm.h [new file with mode: 0644]
src/m4af.c [new file with mode: 0644]
src/m4af.h [new file with mode: 0644]
src/m4af_endian.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/progress.c [new file with mode: 0644]
src/progress.h [new file with mode: 0644]
src/wav_reader.c [new file with mode: 0644]
src/wav_reader.h [new file with mode: 0644]
version.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..17484d2
--- /dev/null
+++ b/COPYING
@@ -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.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..a1e89e1
--- /dev/null
+++ b/INSTALL
@@ -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.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..438b7af
--- /dev/null
@@ -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
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..70a2301
--- /dev/null
+++ b/README
@@ -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
diff --git a/config.rpath b/config.rpath
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..58bc422
--- /dev/null
@@ -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
diff --git a/m4/.gitkeep b/m4/.gitkeep
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/missings/getopt.c b/missings/getopt.c
new file mode 100644 (file)
index 0000000..ea6800e
--- /dev/null
@@ -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));
+}
diff --git a/missings/getopt.h b/missings/getopt.h
new file mode 100644 (file)
index 0000000..252257c
--- /dev/null
@@ -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_ */
diff --git a/src/aacenc.c b/src/aacenc.c
new file mode 100644 (file)
index 0000000..e622c62
--- /dev/null
@@ -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;
+}
diff --git a/src/aacenc.h b/src/aacenc.h
new file mode 100644 (file)
index 0000000..ebb0509
--- /dev/null
@@ -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
diff --git a/src/compat.h b/src/compat.h
new file mode 100644 (file)
index 0000000..f20f03f
--- /dev/null
@@ -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
diff --git a/src/compat_posix.c b/src/compat_posix.c
new file mode 100644 (file)
index 0000000..1b8f14a
--- /dev/null
@@ -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 */
diff --git a/src/compat_win32.c b/src/compat_win32.c
new file mode 100644 (file)
index 0000000..a68fa3f
--- /dev/null
@@ -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;
+}
+
diff --git a/src/lpcm.c b/src/lpcm.c
new file mode 100644 (file)
index 0000000..5d338c7
--- /dev/null
@@ -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;
+}
diff --git a/src/lpcm.h b/src/lpcm.h
new file mode 100644 (file)
index 0000000..ae67a43
--- /dev/null
@@ -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
diff --git a/src/m4af.c b/src/m4af.c
new file mode 100644 (file)
index 0000000..0c62727
--- /dev/null
@@ -0,0 +1,1188 @@
+/* 
+ * 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 <stdlib.h>
+#include <string.h>
+#include <time.h>
+#if HAVE_STDINT_H
+#  include <stdint.h>
+#endif
+#include "m4af.h"
+#include "m4af_endian.h"
+
+#define m4af_realloc(memory,size) realloc(memory, size)
+#define m4af_free(memory) free(memory)
+
+typedef struct m4af_sample_entry_t {
+    uint32_t size;
+    uint32_t delta;
+} m4af_sample_entry_t;
+
+typedef struct m4af_chunk_entry_t {
+    int64_t offset;
+    uint32_t size;
+    uint32_t samples_per_chunk;
+    uint32_t duration;
+} m4af_chunk_entry_t;
+
+typedef struct m4af_itmf_entry_t {
+    uint32_t type; /* fcc */
+    union {
+        uint32_t type_code;
+        char *name;
+    } u;
+    char *data;
+    uint32_t data_size;
+} m4af_itmf_entry_t;
+
+typedef struct m4af_track_t {
+    uint32_t codec;
+    uint32_t timescale;
+    int64_t creation_time;
+    int64_t modification_time;
+    int64_t duration;
+    uint32_t frame_duration;
+    uint32_t encoder_delay;
+    uint32_t padding;
+    uint8_t *decSpecificInfo;
+    uint32_t decSpecificInfoSize;
+    uint32_t bufferSizeDB;
+    uint32_t maxBitrate;
+    uint32_t avgBitrate;
+
+    m4af_sample_entry_t *sample_table;
+    uint32_t num_samples;
+    uint32_t sample_table_capacity;
+
+    m4af_chunk_entry_t *chunk_table;
+    uint32_t num_chunks;
+    uint32_t chunk_table_capacity;
+
+    uint8_t *chunk_buffer;
+    uint32_t chunk_size;
+    uint32_t chunk_capacity;
+} m4af_track_t;
+
+struct m4af_writer_t {
+    uint32_t timescale;
+    int64_t creation_time;
+    int64_t modification_time;
+    int64_t mdat_pos;
+    int64_t mdat_size;
+    int last_error;
+
+    m4af_itmf_entry_t *itmf_table;
+    uint32_t num_tags;
+    uint32_t itmf_table_capacity;
+
+    m4af_io_callbacks_t io;
+    void *io_cookie;
+
+    uint16_t num_tracks;
+    m4af_track_t track[1];
+};
+
+static
+int64_t m4af_timestamp(void)
+{
+    return (int64_t)(time(0)) + (((1970 - 1904) * 365) + 17) * 24 * 60 * 60;
+}
+
+/*
+ * http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 
+ */
+static
+uint32_t m4af_roundup(uint32_t n)
+{
+    n--;
+    n |= n >> 1;
+    n |= n >> 2;
+    n |= n >> 4;
+    n |= n >> 8;
+    n |= n >> 16;
+    n++;
+    return n;
+}
+
+static
+int64_t m4af_tell(m4af_writer_t *ctx)
+{
+    int64_t pos = -1;
+    if (!ctx->last_error && (pos = ctx->io.tell(ctx->io_cookie)) < 0)
+        ctx->last_error = M4AF_IO_ERROR;
+    return pos;
+}
+
+static
+int m4af_set_pos(m4af_writer_t *ctx, int64_t pos)
+{
+    int rc = -1;
+    if (!ctx->last_error &&
+        (rc = ctx->io.seek(ctx->io_cookie, pos, SEEK_SET)) < 0)
+        ctx->last_error = M4AF_IO_ERROR;
+    return rc;
+}
+
+static
+int m4af_write(m4af_writer_t *ctx, const void *data, uint32_t size)
+{
+    int rc = -1;
+    if (!ctx->last_error &&
+        (rc = ctx->io.write(ctx->io_cookie, data, size)) < 0)
+        ctx->last_error = M4AF_IO_ERROR;
+    return rc;
+}
+
+static
+int m4af_write32(m4af_writer_t *ctx, uint32_t data)
+{
+    data = m4af_htob32(data);
+    return m4af_write(ctx, &data, 4);
+}
+
+static
+int m4af_write64(m4af_writer_t *ctx, uint64_t data)
+{
+    data = m4af_htob64(data);
+    return m4af_write(ctx, &data, 8);
+}
+
+static
+int m4af_write24(m4af_writer_t *ctx, uint32_t data)
+{
+    data = m4af_htob32(data << 8);
+    return m4af_write(ctx, &data, 3);
+}
+
+static
+void m4af_write32_at(m4af_writer_t *ctx, int64_t pos, uint32_t value)
+{
+    int64_t current_pos = m4af_tell(ctx);
+    m4af_set_pos(ctx, pos);
+    m4af_write32(ctx, value);
+    m4af_set_pos(ctx, current_pos);
+}
+
+m4af_writer_t *m4af_create(uint32_t codec, uint32_t timescale,
+                           m4af_io_callbacks_t *io, void *io_cookie)
+{
+    m4af_writer_t *ctx;
+    int64_t timestamp;
+
+    if (codec != M4AF_FOURCC('m','p','4','a') &&
+        codec != M4AF_FOURCC('a','l','a','c'))
+        return 0;
+    if ((ctx = m4af_realloc(0, sizeof(m4af_writer_t))) == 0)
+        return 0;
+    memset(ctx, 0, sizeof(m4af_writer_t));
+    memcpy(&ctx->io, io, sizeof(m4af_io_callbacks_t));
+    ctx->io_cookie = io_cookie;
+    ctx->timescale = timescale;
+    timestamp = m4af_timestamp();
+    ctx->creation_time = timestamp;
+    ctx->modification_time = timestamp;
+    ctx->num_tracks = 1;
+    ctx->track[0].codec = codec;
+    ctx->track[0].timescale = timescale;
+    ctx->track[0].creation_time = timestamp;
+    ctx->track[0].modification_time = timestamp;
+    return ctx;
+}
+
+void m4af_set_fixed_frame_duration(m4af_writer_t *ctx, int track_idx,
+                                   uint32_t length)
+{
+    ctx->track[track_idx].frame_duration = length;
+}
+
+int m4af_decoder_specific_info(m4af_writer_t *ctx, int track_idx,
+                               uint8_t *data, uint32_t size)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    if (size > track->decSpecificInfoSize) {
+        uint8_t *memory = m4af_realloc(track->decSpecificInfo, size);
+        if (memory == 0) {
+            ctx->last_error = M4AF_NO_MEMORY;
+            return -1;
+        }
+        track->decSpecificInfo = memory;
+    }
+    if (size > 0)
+        memcpy(track->decSpecificInfo, data, size);
+    track->decSpecificInfoSize = size;
+    return 0;
+}
+
+void m4af_set_priming(m4af_writer_t *ctx, int track_idx,
+                      uint32_t encoder_delay, uint32_t padding)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    track->encoder_delay = encoder_delay;
+    track->padding = padding;
+}
+
+static
+int m4af_add_sample_entry(m4af_writer_t *ctx, int track_idx,
+                          uint32_t size, uint32_t delta)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    m4af_sample_entry_t *entry;
+
+    if (ctx->last_error)
+        return -1;
+    if (track->num_samples == track->sample_table_capacity) {
+        uint32_t new_size = track->sample_table_capacity;
+        new_size = new_size ? new_size * 2 : 1;
+        entry = m4af_realloc(track->sample_table, new_size * sizeof(*entry));
+        if (entry == 0) {
+            ctx->last_error = M4AF_NO_MEMORY;
+            return -1;
+        }
+        track->sample_table = entry;
+        track->sample_table_capacity = new_size;
+    }
+    entry = track->sample_table + track->num_samples;
+    entry->size = size;
+    entry->delta = delta;
+    ++track->num_samples;
+    return 0;
+}
+
+static
+int m4af_flush_chunk(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    m4af_chunk_entry_t *entry;
+    if (!track->num_chunks || !track->chunk_size)
+        return 0;
+    entry = &track->chunk_table[track->num_chunks - 1];
+    entry->offset = m4af_tell(ctx);
+    m4af_write(ctx, track->chunk_buffer, track->chunk_size);
+    ctx->mdat_size += track->chunk_size;
+    track->chunk_size = 0;
+    return ctx->last_error ? -1 : 0;
+}
+
+static
+int m4af_update_chunk_table(m4af_writer_t *ctx, int track_idx,
+                            uint32_t size, uint32_t delta)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    m4af_chunk_entry_t *entry;
+    int add_new_chunk = 0;
+
+    if (ctx->last_error)
+        return -1;
+    if (track->num_chunks == 0)
+        add_new_chunk = 1;
+    else {
+        entry = &track->chunk_table[track->num_chunks - 1];
+        if (entry->duration + delta > track->timescale / 2)
+            add_new_chunk = 1;
+    }
+    if (add_new_chunk) {
+        m4af_flush_chunk(ctx, track_idx);
+        if (track->num_chunks == track->chunk_table_capacity) {
+            uint32_t new_size = track->chunk_table_capacity;
+            new_size = new_size ? new_size * 2 : 1;
+            entry = m4af_realloc(track->chunk_table, new_size * sizeof(*entry));
+            if (entry == 0) {
+                ctx->last_error = M4AF_NO_MEMORY;
+                return -1;
+            }
+            track->chunk_table = entry;
+            track->chunk_table_capacity = new_size;
+        }
+        memset(&track->chunk_table[track->num_chunks++], 0,
+               sizeof(m4af_chunk_entry_t));
+    }
+    entry = &track->chunk_table[track->num_chunks - 1];
+    entry->size += size;
+    ++entry->samples_per_chunk;
+    entry->duration += delta;
+    return 0;
+}
+
+static
+void m4af_update_max_bitrate(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    uint32_t duration = 0, size = 0, bitrate;
+    m4af_sample_entry_t *ent = track->sample_table + track->num_samples - 1;
+
+    for (; ent >= track->sample_table && duration < track->timescale; --ent) {
+        duration += ent->delta;
+        size += ent->size;
+    }
+    bitrate = (uint32_t)(size * 8.0 * track->timescale / duration + .5);
+    if (bitrate > track->maxBitrate)
+        track->maxBitrate = bitrate;
+}
+
+static
+int m4af_append_sample_to_chunk(m4af_writer_t *ctx, int track_idx,
+                                const void *data, uint32_t size)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    uint32_t newsize = track->chunk_size + size;
+
+    if (ctx->last_error)
+        return -1;
+    if (track->chunk_capacity < newsize) {
+        uint32_t capacity = m4af_roundup(newsize);
+        uint8_t *memory = realloc(track->chunk_buffer, capacity);
+        if (!memory) {
+            ctx->last_error = M4AF_NO_MEMORY;
+            return -1;
+        }
+        track->chunk_buffer = memory;
+        track->chunk_capacity = capacity;
+    }
+    memcpy(track->chunk_buffer + track->chunk_size, data, size);
+    track->chunk_size = newsize;
+    return 0;
+}
+
+int m4af_write_sample(m4af_writer_t *ctx, int track_idx, const void *data,
+                      uint32_t size, uint32_t duration)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    if (track->frame_duration)
+        duration = track->frame_duration;
+    if (size > track->bufferSizeDB)
+        track->bufferSizeDB = size;
+    track->duration += duration;
+    m4af_add_sample_entry(ctx, track_idx, size, duration);
+    m4af_update_chunk_table(ctx, track_idx, size, duration);
+    m4af_update_max_bitrate(ctx, track_idx);
+    m4af_append_sample_to_chunk(ctx, track_idx, data, size);
+    return ctx->last_error ? -1 : 0;
+}
+
+static
+int m4af_add_itmf_entry(m4af_writer_t *ctx)
+{
+    m4af_itmf_entry_t *entry;
+    if (ctx->num_tags == ctx->itmf_table_capacity) {
+        uint32_t new_size = ctx->itmf_table_capacity;
+        new_size = new_size ? new_size * 2 : 1;
+        entry = m4af_realloc(ctx->itmf_table, new_size * sizeof(*entry));
+        if (entry == 0) {
+            ctx->last_error = M4AF_NO_MEMORY;
+            return -1;
+        }
+        ctx->itmf_table = entry;
+        ctx->itmf_table_capacity = new_size;
+    }
+    ++ctx->num_tags;
+    return 0;
+}
+
+int m4af_add_itmf_long_tag(m4af_writer_t *ctx, const char *name,
+                           const char *data)
+{
+    m4af_itmf_entry_t *entry;
+    size_t name_len = strlen(name);
+    size_t data_len = strlen(data);
+    char *name_copy = m4af_realloc(0, name_len + 1);
+    char *data_copy = m4af_realloc(0, data_len);
+    if (!name_copy || !data_copy) {
+        ctx->last_error = M4AF_NO_MEMORY;
+        goto FAIL;
+    }
+    if (m4af_add_itmf_entry(ctx) < 0)
+        goto FAIL;
+    memcpy(name_copy, name, name_len + 1);
+    memcpy(data_copy, data, data_len);
+    entry = ctx->itmf_table + ctx->num_tags - 1;
+    entry->type = M4AF_FOURCC('-','-','-','-');
+    entry->u.name = name_copy;
+    entry->data = data_copy;
+    entry->data_size = data_len;
+    return 0;
+FAIL:
+    if (name_copy)
+        m4af_free(name_copy);
+    if (data_copy)
+        m4af_free(data_copy);
+    return -1;
+}
+
+int m4af_add_itmf_short_tag(m4af_writer_t *ctx, uint32_t type,
+                            uint32_t type_code, const void *data,
+                            uint32_t data_size)
+{
+    m4af_itmf_entry_t *entry;
+    char *data_copy = m4af_realloc(0, data_size);
+    if (!data_copy) {
+        ctx->last_error = M4AF_NO_MEMORY;
+        goto FAIL;
+    }
+    if (m4af_add_itmf_entry(ctx) < 0)
+        goto FAIL;
+    entry = ctx->itmf_table + ctx->num_tags - 1;
+    entry->type = type;
+    entry->u.type_code = type_code;
+    memcpy(data_copy, data, data_size);
+    entry->data = data_copy;
+    entry->data_size = data_size;
+    return 0;
+FAIL:
+    if (data_copy)
+        m4af_free(data_copy);
+    return -1;
+}
+
+int m4af_add_itmf_string_tag(m4af_writer_t *ctx, uint32_t type,
+                             const char *data)
+{
+    return m4af_add_itmf_short_tag(ctx, type, M4AF_UTF8, data, strlen(data));
+}
+
+int m4af_add_itmf_int8_tag(m4af_writer_t *ctx, uint32_t type, int value)
+{
+    uint8_t data = value;
+    return m4af_add_itmf_short_tag(ctx, type, M4AF_INTEGER, &data, 1);
+}
+
+int m4af_add_itmf_int16_tag(m4af_writer_t *ctx, uint32_t type, int value)
+{
+    uint16_t data = m4af_htob16(value);
+    return m4af_add_itmf_short_tag(ctx, type, M4AF_INTEGER, &data, 2);
+}
+
+int m4af_add_itmf_int32_tag(m4af_writer_t *ctx, uint32_t type, int value)
+{
+    uint32_t data = m4af_htob32(value);
+    return m4af_add_itmf_short_tag(ctx, type, M4AF_INTEGER, &data, 4);
+}
+
+int m4af_add_itmf_track_tag(m4af_writer_t *ctx, int track, int total)
+{
+    uint16_t data[4] = { 0 };
+    data[1] = m4af_htob16(track);
+    data[2] = m4af_htob16(total);
+    return m4af_add_itmf_short_tag(ctx, M4AF_FOURCC('t','r','k','n'),
+                                   M4AF_IMPLICIT, &data, 8);
+}
+
+int m4af_add_itmf_disk_tag(m4af_writer_t *ctx, int disk, int total)
+{
+    uint16_t data[3] = { 0 };
+    data[1] = m4af_htob16(disk);
+    data[2] = m4af_htob16(total);
+    return m4af_add_itmf_short_tag(ctx, M4AF_FOURCC('d','i','s','k'),
+                                   M4AF_IMPLICIT, &data, 6);
+}
+
+int m4af_add_itmf_genre_tag(m4af_writer_t *ctx, int genre)
+{
+    uint16_t data = m4af_htob16(genre);
+    return m4af_add_itmf_short_tag(ctx, M4AF_FOURCC('g','n','r','e'),
+                                   M4AF_IMPLICIT, &data, 2);
+}
+
+static
+int m4af_set_iTunSMPB(m4af_writer_t *ctx)
+{
+    const char *template = " 00000000 %08X %08X %08X%08X 00000000 00000000 "
+        "00000000 00000000 00000000 00000000 00000000 00000000";
+    m4af_track_t *track = &ctx->track[0];
+    char buf[256];
+    uint64_t length = track->duration - track->encoder_delay - track->padding;
+    sprintf(buf, template, track->encoder_delay, track->padding,
+            (uint32_t)(length >> 32), (uint32_t)length);
+    return m4af_add_itmf_long_tag(ctx, "iTunSMPB", buf);
+}
+
+static
+void m4af_update_size(m4af_writer_t *ctx, int64_t pos)
+{
+    int64_t current_pos = m4af_tell(ctx);
+    m4af_set_pos(ctx, pos);
+    m4af_write32(ctx, current_pos - pos);
+    m4af_set_pos(ctx, current_pos);
+}
+
+static
+int m4af_head_version(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    return track->duration > UINT32_MAX
+        || track->creation_time > UINT32_MAX
+        || track->modification_time > UINT32_MAX;
+}
+
+static
+void m4af_descriptor(m4af_writer_t *ctx, uint32_t tag, uint32_t size)
+{
+    uint8_t buf[5];
+    buf[0] = tag;
+    buf[1] = ((size >> 21) | 0x80);
+    buf[2] = ((size >> 14) | 0x80);
+    buf[3] = ((size >>  7) | 0x80);
+    buf[4] = (size & 0x7f);
+    m4af_write(ctx, buf, 5);
+}
+
+static
+void m4af_ftyp_box(m4af_writer_t *ctx)
+{
+    m4af_write(ctx, "\0\0\0\040""ftypM4A \0\0\0\0M4A mp42isom\0\0\0\0", 32);
+}
+
+static
+void m4af_free_box(m4af_writer_t *ctx, uint32_t size)
+{
+    int64_t pos = m4af_tell(ctx);
+    m4af_write32(ctx, size + 8);
+    m4af_write(ctx, "free", 4);
+    if (size > 0)
+        m4af_set_pos(ctx, pos + size + 8);
+}
+
+int m4af_begin_write(m4af_writer_t *ctx)
+{
+    m4af_ftyp_box(ctx);
+    m4af_free_box(ctx, 0);
+    m4af_write(ctx, "\0\0\0\0mdat", 8);
+    ctx->mdat_pos = m4af_tell(ctx);
+    return ctx->last_error ? -1 : 0;
+}
+
+static
+void m4af_stco_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    uint32_t i;
+    m4af_chunk_entry_t *index = track->chunk_table;
+    int is_co64 = (ctx->mdat_pos + ctx->mdat_size > UINT32_MAX);
+    int64_t pos = m4af_tell(ctx);
+
+    m4af_write32(ctx, 0); /* size */
+    m4af_write(ctx, is_co64 ? "co64" : "stco", 4);
+    m4af_write32(ctx, 0); /* version and flags */
+    m4af_write32(ctx, track->num_chunks);
+    for (i = 0; i < track->num_chunks; ++i, ++index) {
+        if (is_co64)
+            m4af_write64(ctx, index->offset);
+        else
+            m4af_write32(ctx, index->offset);
+    }
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_stsz_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    m4af_sample_entry_t *index = track->sample_table;
+    uint32_t i;
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx,
+               "\0\0\0\0"  /* size                     */
+               "stsz"      /* type                     */
+               "\0"        /* version                  */
+               "\0\0\0"    /* flags                    */
+               "\0\0\0\0"  /* sample_size: 0(variable) */
+               , 16);
+    m4af_write32(ctx, track->num_samples);
+    for (i = 0; i < track->num_samples; ++i, ++index)
+        m4af_write32(ctx, index->size);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_stsc_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    m4af_chunk_entry_t *index = track->chunk_table;
+    uint32_t i, prev_samples_per_chunk = 0, entry_count = 0;
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx,
+               "\0\0\0\0"  /* size        */
+               "stsc"      /* type        */
+               "\0"        /* version     */
+               "\0\0\0"    /* flags       */
+               "\0\0\0\0"  /* entry_count */
+               , 16);
+
+    for (i = 0; i < track->num_chunks; ++i, ++index) {
+        if (index->samples_per_chunk != prev_samples_per_chunk) {
+            ++entry_count;
+            m4af_write32(ctx, i + 1);
+            m4af_write32(ctx, index->samples_per_chunk);
+            m4af_write32(ctx, 1); /* sample_description_index */
+            prev_samples_per_chunk = index->samples_per_chunk;
+        }
+    }
+    m4af_write32_at(ctx, pos + 12, entry_count);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_stts_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    m4af_sample_entry_t *index = track->sample_table;
+    uint32_t i, prev_delta = 0, entry_count = 0, sample_count = 0;
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx,
+               "\0\0\0\0"  /* size        */
+               "stts"      /* type        */
+               "\0"        /* version     */
+               "\0\0\0"    /* flags       */
+               "\0\0\0\0"  /* entry_count */
+               , 16);
+
+    for (i = 0; i < track->num_samples; ++i, ++index) {
+        if (index->delta == prev_delta)
+            ++sample_count;
+        else {
+            ++entry_count;
+            if (sample_count) {
+                m4af_write32(ctx, sample_count);
+                m4af_write32(ctx, prev_delta);
+            }
+            prev_delta = index->delta;
+            sample_count = 1;
+        } 
+    }
+    if (sample_count) {
+        m4af_write32(ctx, sample_count);
+        m4af_write32(ctx, prev_delta);
+    }
+    m4af_write32_at(ctx, pos + 12, entry_count);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_esds_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0esds", 8);
+    m4af_write32(ctx, 0); /* version + flags */
+
+    /* ES_Descriptor */
+    m4af_descriptor(ctx, 3, 32 + track->decSpecificInfoSize);
+    m4af_write(ctx, "\0\0\0", 3);
+    /* DecoderConfigDescriptor */
+    m4af_descriptor(ctx, 4, 18 + track->decSpecificInfoSize);
+    m4af_write(ctx,
+               "\x40"   /* objectTypeIndication: 0x40(Audio ISO/IEC 14496-3)*/
+               "\x15"   /* streamType(6): 0x05(AudioStream)                 
+                         * upStream(1)  : 0
+                         * reserved(1)  : 1
+                         */
+               , 2);
+    m4af_write24(ctx, track->bufferSizeDB);
+    m4af_write32(ctx, track->maxBitrate);
+    m4af_write32(ctx, track->avgBitrate);
+    /* DecoderSpecificInfo */
+    m4af_descriptor(ctx, 5, track->decSpecificInfoSize);
+    m4af_write(ctx, track->decSpecificInfo, track->decSpecificInfoSize);
+    /* SLConfigDescriptor */
+    m4af_descriptor(ctx, 6, 1);
+    m4af_write(ctx, "\002", 1); /* predefined */
+
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_alac_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx,
+               "\0\0\0\0"  /* size        */
+               "alac"      /* type        */
+               "\0"        /* version     */
+               "\0\0\0"    /* flags       */
+               , 12);
+    m4af_write(ctx, track->decSpecificInfo, track->decSpecificInfoSize);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_mp4a_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    int64_t pos = m4af_tell(ctx);
+    m4af_write32(ctx, 0);   /* size         */
+    m4af_write32(ctx, track->codec); /* mp4a or alac */
+    m4af_write(ctx,
+               "\0\0\0\0\0\0" /* reserved                */
+               "\0\001"       /* data_reference_index: 1 */
+               "\0\0\0\0"     /* reserved[0]             */
+               "\0\0\0\0"     /* reserved[1]             */
+               "\0\002"       /* channelcount: 2         */
+               "\0\020"       /* samplesize: 16          */
+               "\0\0"         /* pre_defined             */
+               "\0\0"         /* reserved                */
+               ,24);
+    if (track->codec == M4AF_FOURCC('m','p','4','a')) {
+        m4af_write32(ctx, track->timescale << 16);
+        m4af_esds_box(ctx, track_idx);
+    } else {
+        m4af_write32(ctx, 44100);
+        m4af_alac_box(ctx, track_idx);
+    }
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_stsd_box(m4af_writer_t *ctx, int track_idx)
+{
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0stsd", 8);
+    m4af_write(ctx,
+               "\0"          /* version        */
+               "\0\0\0"      /* flags          */
+               "\0\0\0\001"  /* entry_count: 1 */
+               , 8);
+    m4af_mp4a_box(ctx, track_idx);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_stbl_box(m4af_writer_t *ctx, int track_idx)
+{
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0stbl", 8);
+    m4af_stsd_box(ctx, track_idx);
+    m4af_stts_box(ctx, track_idx);
+    m4af_stsc_box(ctx, track_idx);
+    m4af_stsz_box(ctx, track_idx);
+    m4af_stco_box(ctx, track_idx);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_url_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_write(ctx,
+               "\0\0\0\014"  /* size                       */
+               "url "        /* type                       */
+               "\0"          /* version                    */
+               "\0\0\001"    /* flags: 1(in the same file) */
+               , 12);
+}
+
+static
+void m4af_dref_box(m4af_writer_t *ctx, int track_idx)
+{
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0dref", 8);
+    m4af_write(ctx,
+               "\0"          /* version        */
+               "\0\0\0"      /* flags          */
+               "\0\0\0\001"  /* entry_count: 1 */
+               ,8);
+    m4af_url_box(ctx, track_idx);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_dinf_box(m4af_writer_t *ctx, int track_idx)
+{
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0dinf", 8);
+    m4af_dref_box(ctx, track_idx);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_smhd_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_write(ctx,
+               "\0\0\0\020"  /* size          */
+               "smhd"        /* type          */
+               "\0"          /* version       */
+               "\0\0\0"      /* flags         */
+               "\0\0"        /* balance       */
+               "\0\0"        /* reserved      */
+               , 16);
+}
+
+static
+void m4af_minf_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0minf", 8);
+    /* TODO: add TEXT support */
+    if (track->codec != M4AF_CODEC_TEXT)
+        m4af_smhd_box(ctx, track_idx);
+    m4af_dinf_box(ctx, track_idx);
+    m4af_stbl_box(ctx, track_idx);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_mdhd_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    int64_t pos = m4af_tell(ctx);
+    uint8_t version = m4af_head_version(ctx, track_idx);
+
+    m4af_write(ctx, "\0\0\0\0mdhd", 8);
+    m4af_write(ctx, &version, 1);
+    m4af_write(ctx, "\0\0\0", 3); /* flags */
+    if (version) {
+        m4af_write64(ctx, track->creation_time);
+        m4af_write64(ctx, track->modification_time);
+        m4af_write32(ctx, track->timescale);
+        m4af_write64(ctx, track->duration);
+    } else {
+        m4af_write32(ctx, track->creation_time);
+        m4af_write32(ctx, track->modification_time);
+        m4af_write32(ctx, track->timescale);
+        m4af_write32(ctx, track->duration);
+    }
+    m4af_write(ctx,
+               "\x55\xc4"  /* language: und */
+               "\0\0"      /* pre_defined   */
+               , 4);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_hdlr_box(m4af_writer_t *ctx, int track_idx, const char *type)
+{
+    int64_t pos = m4af_tell(ctx);
+    static const char reserved_and_name[10] = { 0 };
+
+    m4af_write(ctx,
+               "\0\0\0\0"  /* size          */
+               "hdlr"      /* type          */
+               "\0"        /* version       */
+               "\0\0\0"    /* flags         */
+               "\0\0\0\0"  /* pre_defined   */
+               , 16);
+    m4af_write(ctx, type, 4); /* handler_type */
+    /* reserved[0] */
+    m4af_write(ctx, !strcmp(type, "mdir") ? "appl" : "\0\0\0\0", 4);
+    /* reserved[1], reserved[2], name */
+    m4af_write(ctx, reserved_and_name, (pos & 1) ? 9 : 10);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_mdia_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    const char *hdlr =
+        (track->codec == M4AF_CODEC_TEXT) ? "text" : "soun";
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0mdia", 8);
+    m4af_mdhd_box(ctx, track_idx);
+    m4af_hdlr_box(ctx, track_idx, hdlr);
+    m4af_minf_box(ctx, track_idx);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_tkhd_box(m4af_writer_t *ctx, int track_idx)
+{
+    m4af_track_t *track = &ctx->track[track_idx];
+    int64_t pos = m4af_tell(ctx);
+    uint8_t version = m4af_head_version(ctx, track_idx);
+    m4af_write(ctx, "\0\0\0\0tkhd", 8);
+    m4af_write(ctx, &version, 1);
+    m4af_write(ctx, "\0\0\007", 3);  /* flags  */
+    if (version) {
+        m4af_write64(ctx, track->creation_time);
+        m4af_write64(ctx, track->modification_time);
+        m4af_write32(ctx, track_idx + 1);
+        m4af_write(ctx, "\0\0\0\0"   /* reserved    */
+                        , 4);
+        m4af_write64(ctx, track->duration);
+    } else {
+        m4af_write32(ctx, track->creation_time);
+        m4af_write32(ctx, track->modification_time);
+        m4af_write32(ctx, track_idx + 1);
+        m4af_write(ctx, "\0\0\0\0"   /* reserved    */
+                        , 4);
+        m4af_write32(ctx, track->duration);
+    }
+    m4af_write(ctx,
+               "\0\0\0\0"   /* reserved[0]      */
+               "\0\0\0\0"   /* reserved[1]      */
+               "\0\0"       /* layer            */
+               "\0\0"       /* alternate_group  */
+               "\001\0"     /* volume: 1.0      */
+               "\0\0"       /* reserved         */
+               "\0\001\0\0" /* matrix[0]        */
+               "\0\0\0\0"   /* matrix[1]        */
+               "\0\0\0\0"   /* matrix[2]        */
+               "\0\0\0\0"   /* matrix[3]        */
+               "\0\001\0\0" /* matrix[4]        */
+               "\0\0\0\0"   /* matrix[5]        */
+               "\0\0\0\0"   /* matrix[6]        */
+               "\0\0\0\0"   /* matrix[7]        */
+               "\100\0\0\0" /* matrix[8]        */
+               "\0\0\0\0"   /* width            */
+               "\0\0\0\0"   /* height           */
+               , 60);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_trak_box(m4af_writer_t *ctx, int track_idx)
+{
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0trak", 8);
+    m4af_tkhd_box(ctx, track_idx);
+    m4af_mdia_box(ctx, track_idx);
+    m4af_update_size(ctx, pos);
+}
+
+static
+int64_t m4af_movie_duration(m4af_writer_t *ctx)
+{
+    int64_t movie_duration = 0;
+    unsigned i;
+    for (i = 0; i < ctx->num_tracks; ++i) {
+        double x = ctx->track[i].duration;
+        int64_t duration = x * ctx->track[i].timescale / ctx->timescale + .5;
+        if (duration > movie_duration)
+            movie_duration = duration;
+    }
+    return movie_duration;
+}
+
+static
+void m4af_mvhd_box(m4af_writer_t *ctx)
+{
+    int64_t pos = m4af_tell(ctx);
+    uint8_t version = m4af_head_version(ctx, 0);
+    int64_t movie_duration = m4af_movie_duration(ctx);
+
+    m4af_write(ctx, "\0\0\0\0mvhd", 8);
+    m4af_write(ctx, &version, 1);
+    m4af_write(ctx, "\0\0\0", 3); /* flags */
+    if (version) {
+        m4af_write64(ctx, ctx->creation_time);
+        m4af_write64(ctx, ctx->modification_time);
+        m4af_write32(ctx, ctx->timescale);
+        m4af_write64(ctx, movie_duration);
+    } else {
+        m4af_write32(ctx, ctx->creation_time);
+        m4af_write32(ctx, ctx->modification_time);
+        m4af_write32(ctx, ctx->timescale);
+        m4af_write32(ctx, movie_duration);
+    }
+    m4af_write(ctx,
+               "\0\001\0\0" /* rate: 1.0        */
+               "\001\0"     /* volume: 1.0      */
+               "\0\0"       /* reserved         */
+               "\0\0\0\0"   /* reserved[0]      */
+               "\0\0\0\0"   /* reserved[1]      */
+               "\0\001\0\0" /* matrix[0]        */
+               "\0\0\0\0"   /* matrix[1]        */
+               "\0\0\0\0"   /* matrix[2]        */
+               "\0\0\0\0"   /* matrix[3]        */
+               "\0\001\0\0" /* matrix[4]        */
+               "\0\0\0\0"   /* matrix[5]        */
+               "\0\0\0\0"   /* matrix[6]        */
+               "\0\0\0\0"   /* matrix[7]        */
+               "\100\0\0\0" /* matrix[8]        */
+               "\0\0\0\0"   /* pre_defined[0]   */
+               "\0\0\0\0"   /* pre_defined[1]   */
+               "\0\0\0\0"   /* pre_defined[2]   */
+               "\0\0\0\0"   /* pre_defined[3]   */
+               "\0\0\0\0"   /* pre_defined[4]   */
+               "\0\0\0\0"   /* pre_defined[5]   */
+               , 76);
+    m4af_write32(ctx, ctx->num_tracks + 1);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_mean_box(m4af_writer_t *ctx)
+{
+    m4af_write(ctx,
+               "\0\0\0\034"       /* size           */
+               "mean"
+               "\0"               /* version        */
+               "\0\0\0"           /* flags          */
+               "com.apple.iTunes" /* meaning-string */
+               , 28);
+}
+
+static
+void m4af_name_box(m4af_writer_t *ctx, const char *name)
+{
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx,
+               "\0\0\0\0"  /* size    */
+               "name"      /* type    */
+               "\0"        /* version */
+               "\0\0\0"    /* flags   */
+               , 12);
+    m4af_write(ctx, name, strlen(name));
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_data_box(m4af_writer_t *ctx, uint32_t type_code,
+                   const char *data, uint32_t data_size)
+{
+    int64_t pos = m4af_tell(ctx);
+    uint8_t code = type_code;
+    m4af_write(ctx,
+               "\0\0\0\0"  /* size              */
+               "data"      /* type              */
+               "\0\0"      /* reserved          */
+               "\0"        /* type_set_indifier */
+               ,11);
+    m4af_write(ctx, &code, 1);
+    m4af_write(ctx, "\0\0\0\0", 4);   /* locale */
+    m4af_write(ctx, data, data_size);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_write_metadata(m4af_writer_t *ctx, m4af_itmf_entry_t *entry)
+{
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0", 4);
+    m4af_write32(ctx, entry->type);
+    if (entry->type != M4AF_FOURCC('-','-','-','-'))
+        m4af_data_box(ctx, entry->u.type_code, entry->data, entry->data_size);
+    else {
+        m4af_mean_box(ctx);
+        m4af_name_box(ctx, entry->u.name);
+        m4af_data_box(ctx, 1, entry->data, entry->data_size);
+    }
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_ilst_box(m4af_writer_t *ctx)
+{
+    uint32_t i;
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0ilst", 8);
+    for (i = 0; i < ctx->num_tags; ++i)
+        m4af_write_metadata(ctx, &ctx->itmf_table[i]);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_meta_box(m4af_writer_t *ctx)
+{
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx,
+               "\0\0\0\0"  /* size                     */
+               "meta"      /* type                     */
+               "\0"        /* version                  */
+               "\0\0\0"    /* flags                    */
+               , 12);
+    m4af_hdlr_box(ctx, 0, "mdir");
+    m4af_ilst_box(ctx);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_udta_box(m4af_writer_t *ctx)
+{
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0udta", 8);
+    m4af_meta_box(ctx);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_moov_box(m4af_writer_t *ctx)
+{
+    unsigned i;
+    int64_t pos = m4af_tell(ctx);
+    m4af_write(ctx, "\0\0\0\0moov", 8);
+    m4af_mvhd_box(ctx);
+    for (i = 0; i < ctx->num_tracks; ++i)
+        m4af_trak_box(ctx, i);
+    if (ctx->num_tags)
+        m4af_udta_box(ctx);
+    m4af_update_size(ctx, pos);
+}
+
+static
+void m4af_finalize_mdat(m4af_writer_t *ctx)
+{
+    if (ctx->mdat_size + 8 > UINT32_MAX) {
+        m4af_set_pos(ctx, ctx->mdat_pos - 16);
+        m4af_write32(ctx, 1);
+        m4af_write(ctx, "mdat", 4);
+        m4af_write64(ctx, ctx->mdat_size + 16);
+    } else {
+        m4af_set_pos(ctx, ctx->mdat_pos - 8);
+        m4af_write32(ctx, ctx->mdat_size + 8);
+    }
+    m4af_set_pos(ctx, ctx->mdat_pos + ctx->mdat_size);
+}
+
+int m4af_finalize(m4af_writer_t *ctx)
+{
+    unsigned i;
+    m4af_track_t *track;
+
+    for (i = 0; i < ctx->num_tracks; ++i) {
+        track = ctx->track + i;
+        if (track->duration) {
+            int64_t track_size = 0;
+           unsigned j;
+            for (j = 0; j < track->num_chunks; ++j)
+                track_size += track->chunk_table[j].size;
+            track->avgBitrate =
+                8.0 * track_size * track->timescale / track->duration + .5;
+        }
+        m4af_flush_chunk(ctx, i);
+    }
+    if (ctx->track[0].encoder_delay || ctx->track[0].padding)
+        m4af_set_iTunSMPB(ctx);
+    m4af_finalize_mdat(ctx);
+    m4af_moov_box(ctx);
+    return ctx->last_error ? -1 : 0;
+}
+
+static
+void m4af_free_itmf_table(m4af_writer_t *ctx)
+{
+    uint32_t i;
+    m4af_itmf_entry_t *entry = ctx->itmf_table;
+    for (i = 0; i < ctx->num_tags; ++i, ++entry) {
+        if (entry->type == M4AF_FOURCC('-','-','-','-'))
+            m4af_free(entry->u.name);
+        m4af_free(entry->data);
+    }
+    m4af_free(ctx->itmf_table);
+}
+
+void m4af_teardown(m4af_writer_t **ctxp)
+{
+    unsigned i;
+    m4af_writer_t *ctx = *ctxp;
+    m4af_track_t *track;
+    for (i = 0; i < ctx->num_tracks; ++i) {
+       track = ctx->track + i;
+       if (track->decSpecificInfo)
+           m4af_free(track->decSpecificInfo);
+       if (track->sample_table)
+           m4af_free(track->sample_table);
+       if (track->chunk_table)
+           m4af_free(track->chunk_table);
+       if (track->chunk_buffer)
+           m4af_free(track->chunk_buffer);
+    }
+    if (ctx->itmf_table)
+        m4af_free_itmf_table(ctx);
+    m4af_free(ctx);
+    *ctxp = 0;
+}
diff --git a/src/m4af.h b/src/m4af.h
new file mode 100644 (file)
index 0000000..fd6bc78
--- /dev/null
@@ -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
diff --git a/src/m4af_endian.h b/src/m4af_endian.h
new file mode 100644 (file)
index 0000000..bed407d
--- /dev/null
@@ -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 */
+
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..fe4653e
--- /dev/null
@@ -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;
+}
diff --git a/src/progress.c b/src/progress.c
new file mode 100644 (file)
index 0000000..c1c80b6
--- /dev/null
@@ -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);
+}
diff --git a/src/progress.h b/src/progress.h
new file mode 100644 (file)
index 0000000..527da91
--- /dev/null
@@ -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
diff --git a/src/wav_reader.c b/src/wav_reader.c
new file mode 100644 (file)
index 0000000..460225e
--- /dev/null
@@ -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;
+}
diff --git a/src/wav_reader.h b/src/wav_reader.h
new file mode 100644 (file)
index 0000000..9335e1d
--- /dev/null
@@ -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
diff --git a/version.h b/version.h
new file mode 100644 (file)
index 0000000..dd49c39
--- /dev/null
+++ b/version.h
@@ -0,0 +1,4 @@
+#ifndef VERSION_H
+#define VERSION_H
+const char *fdkaac_version = "0.0.1";
+#endif
This page took 0.135939 seconds and 4 git commands to generate.