--- /dev/null
+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.
--- /dev/null
+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.
+
--- /dev/null
+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
--- /dev/null
+==========================================================================
+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
--- /dev/null
+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
--- /dev/null
+/* $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));
+}
--- /dev/null
+/* $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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * Copyright (C) 2013 nu774
+ * For conditions of distribution and use, see copyright notice in COPYING
+ */
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <errno.h>
+#include <getopt.h>
+#include "compat.h"
+#include "wav_reader.h"
+#include "aacenc.h"
+#include "m4af.h"
+#include "progress.h"
+#include "version.h"
+
+#define PROGNAME "fdkaac"
+
+static
+int read_callback(void *cookie, void *data, uint32_t size)
+{
+ return fread(data, 1, size, (FILE*)cookie);
+}
+
+static
+int write_callback(void *cookie, const void *data, uint32_t size)
+{
+ return fwrite(data, 1, size, (FILE*)cookie);
+}
+
+static
+int seek_callback(void *cookie, int64_t off, int whence)
+{
+ return fseeko((FILE*)cookie, off, whence);
+}
+
+static
+int64_t tell_callback(void *cookie)
+{
+ return ftello((FILE*)cookie);
+}
+
+static
+void usage(void)
+{
+ printf(
+PROGNAME " %s\n"
+"Usage: " PROGNAME " [options] input_file\n"
+"Options:\n"
+" -h, --help Print this help message\n"
+" -p, --profile <n> Profile (audio object type)\n"
+" 2: MPEG-4 AAC LC (default)\n"
+" 5: MPEG-4 HE-AAC (SBR)\n"
+" 29: MPEG-4 HE-AAC v2 (SBR+PS)\n"
+" 23: MPEG-4 AAC LD\n"
+" 39: MPEG-4 AAC ELD\n"
+" 129: MPEG-2 AAC LC\n"
+" 132: MPEG-2 HE-AAC (SBR)\n"
+" 156: MPEG-2 HE-AAC v2 (SBR+PS)\n"
+" -b, --bitrate <n> Bitrate in bits per seconds (for CBR)\n"
+" -m, --bitrate-mode <n> Bitrate configuration\n"
+" 0: CBR (default)\n"
+" 1-5: VBR\n"
+" (VBR mode is not officially supported, and\n"
+" works only on a certain combination of\n"
+" parameter settings, sample rate, and\n"
+" channel configuration)\n"
+" -w, --bandwidth <n> Frequency bandwidth in Hz (AAC LC only)\n"
+" -a, --afterurner <n> Afterburner\n"
+" 0: Off\n"
+" 1: On(default)\n"
+" -L, --lowdelay-sbr Enable ELD-SBR (AAC ELD only)\n"
+" -s, --sbr-signaling <n> SBR signaling mode\n"
+" 0: Implicit, backward compatible(default)\n"
+" 1: Explicit SBR and implicit PS\n"
+" 2: Explicit hierarchical signaling\n"
+" -f, --transport-format <n> Transport format\n"
+" 0: RAW (default, muxed into M4A)\n"
+" 1: ADIF\n"
+" 2: ADTS\n"
+" 6: LATM MCP=1\n"
+" 7: LATM MCP=0\n"
+" 10: LOAS/LATM (LATM within LOAS)\n"
+" -c, --adts-crc-check Add CRC protection on ADTS header\n"
+" -h, --header-period <n> StreamMuxConfig/PCE repetition period in\n"
+" transport layer\n"
+"\n"
+" -o <filename> Output filename\n"
+" --ignore-length Ignore length of WAV header\n"
+"\n"
+"Tagging options:\n"
+" --title <string>\n"
+" --artist <string>\n"
+" --album <string>\n"
+" --genre <string>\n"
+" --date <string>\n"
+" --composer <string>\n"
+" --grouping <string>\n"
+" --comment <string>\n"
+" --album-artist <string>\n"
+" --track <number[/total]>\n"
+" --disk <number[/total]>\n"
+" --tempo <n>\n"
+ , fdkaac_version);
+}
+
+typedef struct aacenc_tag_entry_t {
+ uint32_t tag;
+ const char *data;
+} aacenc_tag_entry_t;
+
+typedef struct aacenc_param_ex_t {
+ AACENC_PARAMS
+
+ char *input_filename;
+ char *output_filename;
+ unsigned ignore_length;
+
+ aacenc_tag_entry_t *tag_table;
+ unsigned tag_count;
+ unsigned tag_table_capacity;
+} aacenc_param_ex_t;
+
+static
+int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
+{
+ int ch;
+ unsigned n;
+ aacenc_tag_entry_t *tag;
+
+ static struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "profile", required_argument, 0, 'p' },
+ { "bitrate", required_argument, 0, 'b' },
+ { "biterate-mode", required_argument, 0, 'm' },
+ { "bandwidth", required_argument, 0, 'w' },
+ { "afterburner", required_argument, 0, 'a' },
+ { "lowdelay-sbr", no_argument, 0, 'L' },
+ { "sbr-signaling", required_argument, 0, 's' },
+ { "transport-format", required_argument, 0, 'f' },
+ { "adts-crc-check", no_argument, 0, 'c' },
+ { "header-period", required_argument, 0, 'P' },
+
+ { "ignore-length", no_argument, 0, 'I' },
+
+ { "title", required_argument, 0, M4AF_TAG_TITLE },
+ { "artist", required_argument, 0, M4AF_TAG_ARTIST },
+ { "album", required_argument, 0, M4AF_TAG_ALBUM },
+ { "genre", required_argument, 0, M4AF_TAG_GENRE },
+ { "date", required_argument, 0, M4AF_TAG_DATE },
+ { "composer", required_argument, 0, M4AF_TAG_COMPOSER },
+ { "grouping", required_argument, 0, M4AF_TAG_GROUPING },
+ { "comment", required_argument, 0, M4AF_TAG_COMMENT },
+ { "album-artist", required_argument, 0, M4AF_TAG_ALBUM_ARTIST },
+ { "track", required_argument, 0, M4AF_TAG_TRACK },
+ { "disk", required_argument, 0, M4AF_TAG_DISK },
+ { "tempo", required_argument, 0, M4AF_TAG_TEMPO },
+ };
+ params->afterburner = 1;
+
+ aacenc_getmainargs(&argc, &argv);
+ while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:cP:Io:",
+ long_options, 0)) != EOF) {
+ switch (ch) {
+ case 'h':
+ return usage(), -1;
+ case 'p':
+ if (sscanf(optarg, "%u", &n) != 1) {
+ fprintf(stderr, "invalid arg for profile\n");
+ return -1;
+ }
+ params->profile = n;
+ break;
+ case 'b':
+ if (sscanf(optarg, "%u", &n) != 1) {
+ fprintf(stderr, "invalid arg for bitrate\n");
+ return -1;
+ }
+ params->bitrate = n;
+ break;
+ case 'm':
+ if (sscanf(optarg, "%u", &n) != 1 || n > 5) {
+ fprintf(stderr, "invalid arg for bitrate-mode\n");
+ return -1;
+ }
+ params->bitrate_mode = n;
+ break;
+ case 'w':
+ if (sscanf(optarg, "%u", &n) != 1) {
+ fprintf(stderr, "invalid arg for bandwidth\n");
+ return -1;
+ }
+ params->bandwidth = n;
+ break;
+ case 'a':
+ if (sscanf(optarg, "%u", &n) != 1 || n > 1) {
+ fprintf(stderr, "invalid arg for afterburner\n");
+ return -1;
+ }
+ params->afterburner = n;
+ break;
+ case 'L':
+ params->lowdelay_sbr = 1;
+ break;
+ case 's':
+ if (sscanf(optarg, "%u", &n) != 1 || n > 2) {
+ fprintf(stderr, "invalid arg for sbr-signaling\n");
+ return -1;
+ }
+ params->sbr_signaling = n;
+ break;
+ case 'f':
+ if (sscanf(optarg, "%u", &n) != 1) {
+ fprintf(stderr, "invalid arg for transport-format\n");
+ return -1;
+ }
+ params->transport_format = n;
+ break;
+ case 'c':
+ params->adts_crc_check = 1;
+ break;
+ case 'P':
+ if (sscanf(optarg, "%u", &n) != 1) {
+ fprintf(stderr, "invalid arg for header-period\n");
+ return -1;
+ }
+ params->header_period = n;
+ break;
+ case 'o':
+ params->output_filename = optarg;
+ break;
+ case 'I':
+ params->ignore_length = 1;
+ break;
+ case M4AF_TAG_TITLE:
+ case M4AF_TAG_ARTIST:
+ case M4AF_TAG_ALBUM:
+ case M4AF_TAG_GENRE:
+ case M4AF_TAG_DATE:
+ case M4AF_TAG_COMPOSER:
+ case M4AF_TAG_GROUPING:
+ case M4AF_TAG_COMMENT:
+ case M4AF_TAG_ALBUM_ARTIST:
+ case M4AF_TAG_TRACK:
+ case M4AF_TAG_DISK:
+ case M4AF_TAG_TEMPO:
+ if (params->tag_count == params->tag_table_capacity) {
+ unsigned newsize = params->tag_table_capacity;
+ newsize = newsize ? newsize * 2 : 1;
+ params->tag_table =
+ realloc(params->tag_table,
+ newsize * sizeof(aacenc_tag_entry_t));
+ params->tag_table_capacity = newsize;
+ }
+ tag = params->tag_table + params->tag_count;
+ tag->tag = ch;
+ tag->data = optarg;
+ params->tag_count++;
+ break;
+ default:
+ return usage(), -1;
+ }
+ }
+ if (argc == optind)
+ return usage(), -1;
+ if (!params->bitrate && !params->bitrate_mode) {
+ fprintf(stderr, "bitrate or bitrate-mode is mandatory\n");
+ return -1;
+ }
+ if (params->output_filename && !strcmp(params->output_filename, "-") &&
+ !params->transport_format) {
+ fprintf(stderr, "stdout streaming is not available on M4A output\n");
+ return -1;
+ }
+ if (params->bitrate && params->bitrate < 10000)
+ params->bitrate *= 1000;
+ params->input_filename = argv[optind];
+ return 0;
+};
+
+static
+int write_sample(FILE *ofp, m4af_writer_t *m4af,
+ const void *data, uint32_t size, uint32_t duration)
+{
+ if (!m4af) {
+ if (fwrite(data, 1, size, ofp) < 0) {
+ fprintf(stderr, "ERROR: fwrite(): %s\n", strerror(errno));
+ return -1;
+ }
+ } else if (m4af_write_sample(m4af, 0, data, size, duration) < 0) {
+ fprintf(stderr, "ERROR: failed to write m4a sample\n");
+ return -1;
+ }
+ return 0;
+}
+
+static
+int encode(wav_reader_t *wavf, HANDLE_AACENCODER encoder,
+ uint32_t frame_length, FILE *ofp, m4af_writer_t *m4af)
+{
+ uint8_t *ibuf = 0;
+ int16_t *pcmbuf = 0;
+ uint32_t pcmsize = 0;
+ uint8_t *obuf = 0;
+ uint32_t olen;
+ uint32_t osize = 0;
+ int nread = 1;
+ int consumed;
+ int rc = -1;
+ int frames_written = 0;
+ aacenc_progress_t progress = { 0 };
+ const pcm_sample_description_t *format = wav_get_format(wavf);
+
+ ibuf = malloc(frame_length * format->bytes_per_frame);
+ aacenc_progress_init(&progress, wav_get_length(wavf), format->sample_rate);
+ do {
+ if (nread) {
+ if ((nread = wav_read_frames(wavf, ibuf, frame_length)) < 0) {
+ fprintf(stderr, "ERROR: read failed\n");
+ goto END;
+ } else if (nread > 0) {
+ if (pcm_convert_to_native_sint16(format, ibuf, nread,
+ &pcmbuf, &pcmsize) < 0) {
+ fprintf(stderr, "ERROR: unsupported sample format\n");
+ goto END;
+ }
+ }
+ aacenc_progress_update(&progress, wav_get_position(wavf),
+ format->sample_rate * 2);
+ }
+ if ((consumed = aac_encode_frame(encoder, format, pcmbuf, nread,
+ &obuf, &olen, &osize)) < 0)
+ goto END;
+ if (olen > 0) {
+ if (write_sample(ofp, m4af, obuf, olen, frame_length) < 0)
+ goto END;
+ ++frames_written;
+ }
+ } while (nread > 0 || olen > 0);
+ aacenc_progress_finish(&progress, wav_get_position(wavf));
+ rc = frames_written;
+END:
+ if (ibuf) free(ibuf);
+ if (pcmbuf) free(pcmbuf);
+ if (obuf) free(obuf);
+ return rc;
+}
+
+static
+int finalize_m4a(m4af_writer_t *m4af, const aacenc_param_ex_t *params,
+ HANDLE_AACENCODER encoder)
+{
+ unsigned i;
+ aacenc_tag_entry_t *tag = params->tag_table;
+
+ for (i = 0; i < params->tag_count; ++i, ++tag) {
+ switch (tag->tag) {
+ case M4AF_TAG_TRACK:
+ {
+ unsigned m, n = 0;
+ if (sscanf(tag->data, "%u/%u", &m, &n) >= 1)
+ m4af_add_itmf_track_tag(m4af, m, n);
+ break;
+ }
+ case M4AF_TAG_DISK:
+ {
+ unsigned m, n = 0;
+ if (sscanf(tag->data, "%u/%u", &m, &n) >= 1)
+ m4af_add_itmf_disk_tag(m4af, m, n);
+ break;
+ }
+ case M4AF_TAG_TEMPO:
+ {
+ unsigned n;
+ if (sscanf(tag->data, "%u", &n) == 1)
+ m4af_add_itmf_int16_tag(m4af, tag->tag, n);
+ break;
+ }
+ default:
+ {
+ char *u8 = aacenc_to_utf8(tag->data);
+ m4af_add_itmf_string_tag(m4af, tag->tag, u8);
+ free(u8);
+ }
+ }
+ }
+ {
+ char tool_info[256];
+ char *p = tool_info;
+ LIB_INFO *lib_info = 0;
+
+ p += sprintf(p, PROGNAME " %s, ", fdkaac_version);
+
+ lib_info = malloc(FDK_MODULE_LAST * sizeof(LIB_INFO));
+ /* XXX: aacEncGetLibInfo() seems buggy and sometimes fails */
+ if (aacEncGetLibInfo(lib_info) == AACENC_OK) {
+ for (i = 0; i < FDK_MODULE_LAST; ++i)
+ if (lib_info[i].module_id == FDK_AACENC)
+ break;
+ p += sprintf(p, "libfdk-aac %s, ", lib_info[i].versionStr);
+ }
+ free(lib_info);
+ if (params->bitrate_mode)
+ sprintf(p, "VBR mode %d", params->bitrate_mode);
+ else
+ sprintf(p, "CBR %dkbps", params->bitrate / 1000);
+
+ m4af_add_itmf_string_tag(m4af, M4AF_TAG_TOOL, tool_info);
+ }
+ if (m4af_finalize(m4af) < 0) {
+ fprintf(stderr, "ERROR: failed to finalize m4a\n");
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ wav_io_context_t wav_io = { read_callback, seek_callback };
+ m4af_io_callbacks_t m4af_io = {
+ write_callback, seek_callback, tell_callback };
+ aacenc_param_ex_t params = { 0 };
+
+ int result = 2;
+ FILE *ifp = 0;
+ FILE *ofp = 0;
+ char *output_filename = 0;
+ wav_reader_t *wavf = 0;
+ HANDLE_AACENCODER encoder = 0;
+ AACENC_InfoStruct aacinfo = { 0 };
+ m4af_writer_t *m4af = 0;
+ const pcm_sample_description_t *sample_format;
+ int downsampled_timescale = 0;
+ int frame_count = 0;
+
+ setlocale(LC_CTYPE, "");
+ setbuf(stderr, 0);
+
+ if (parse_options(argc, argv, ¶ms) < 0)
+ return 1;
+
+ if ((ifp = aacenc_fopen(params.input_filename, "rb")) == 0) {
+ aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.input_filename,
+ strerror(errno));
+ goto END;
+ }
+
+ if (ifp == stdin)
+ wav_io.seek = 0;
+
+ if ((wavf = wav_open(&wav_io, ifp, params.ignore_length)) == 0) {
+ fprintf(stderr, "ERROR: broken / unsupported input file\n");
+ goto END;
+ }
+ sample_format = wav_get_format(wavf);
+
+ if (aacenc_init(&encoder, (aacenc_param_t*)¶ms, sample_format,
+ &aacinfo) < 0)
+ goto END;
+
+ if (!params.output_filename) {
+ size_t ilen = strlen(params.input_filename);
+ const char *ext = strrchr(params.input_filename, '.');
+ if (ext) ilen = ext - params.input_filename;
+ output_filename = malloc(ilen + 5);
+ sprintf(output_filename, "%.*s%s", ilen, params.input_filename,
+ params.transport_format ? ".aac" : ".m4a");
+ params.output_filename = output_filename;
+ }
+
+ if ((ofp = aacenc_fopen(params.output_filename, "wb")) == 0) {
+ aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename,
+ strerror(errno));
+ goto END;
+ }
+ if (!params.transport_format) {
+ uint32_t scale;
+ unsigned framelen = aacinfo.frameLength;
+ int sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)¶ms);
+ int sig_mode = aacEncoder_GetParam(encoder, AACENC_SIGNALING_MODE);
+ if (sbr_mode && !sig_mode)
+ downsampled_timescale = 1;
+ scale = sample_format->sample_rate >> downsampled_timescale;
+ if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io, ofp)) < 0)
+ goto END;
+ m4af_decoder_specific_info(m4af, 0, aacinfo.confBuf, aacinfo.confSize);
+ m4af_set_fixed_frame_duration(m4af, 0,
+ framelen >> downsampled_timescale);
+ m4af_begin_write(m4af);
+ }
+ frame_count = encode(wavf, encoder, aacinfo.frameLength, ofp, m4af);
+ if (frame_count < 0)
+ goto END;
+ if (m4af) {
+ uint32_t delay = aacinfo.encoderDelay;
+ int64_t frames_read = wav_get_position(wavf);
+ uint32_t padding = frame_count * aacinfo.frameLength
+ - frames_read - aacinfo.encoderDelay;
+ m4af_set_priming(m4af, 0, delay >> downsampled_timescale,
+ padding >> downsampled_timescale);
+ if (finalize_m4a(m4af, ¶ms, encoder) < 0)
+ goto END;
+ }
+ result = 0;
+END:
+ if (wavf) wav_teardown(&wavf);
+ if (ifp) fclose(ifp);
+ if (m4af) m4af_teardown(&m4af);
+ if (ofp) fclose(ofp);
+ if (encoder) aacEncClose(&encoder);
+ if (output_filename) free(output_filename);
+ if (params.tag_table) free(params.tag_table);
+
+ return result;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+#ifndef VERSION_H
+#define VERSION_H
+const char *fdkaac_version = "0.0.1";
+#endif