1
0
mirror of https://github.com/nu774/fdkaac.git synced 2025-06-05 23:29:14 +02:00

61 Commits

Author SHA1 Message Date
a7c4895534 bump version 2014-01-18 17:44:51 +09:00
209130e887 fix reading of caf file without chan chunk
Since channel map was not initialized, first channel was copied to every
other channel.
2014-01-18 17:41:57 +09:00
b6ab92a9e7 Merge pull request #9 from rbrito/fix-typo
README: Fix typo in bandwidth to match CLI options.
2013-11-17 22:02:43 -08:00
e18fe91a40 man: Regen manpage with hyphens escaped.
Otherwise, automated verification programs like Debian's lintian complain
about them.

Signed-off-by: Rogério Brito <rbrito@ime.usp.br>
2013-11-18 02:55:18 -02:00
472e82e42f README: Remove trailing whitespaces that end up in the manpages.
Signed-off-by: Rogério Brito <rbrito@ime.usp.br>
2013-11-18 02:52:35 -02:00
ce0833f8e5 README: Fix typo in bandwidth to match CLI options.
Signed-off-by: Rogério Brito <rbrito@ime.usp.br>
2013-11-18 02:48:39 -02:00
661f883dce add genman.sh, update fdkaac.1 2013-11-08 23:18:20 +09:00
b815f858d0 update ChangeLog 2013-11-08 12:49:19 +09:00
5d6ea58e92 bump version 2013-11-08 12:48:14 +09:00
71e4764062 fix to use libFDKAAC signaling mode 1
It turned out that mode 1 is explicit, backward compatible signaling.
We don't have to implement it on our side.
2013-11-08 12:23:52 +09:00
a26e3cf3e8 fix README 2013-11-05 15:45:08 +09:00
c6e18ebec1 rename README.md -> README 2013-11-04 22:53:28 +09:00
c1db26327a Update README -> README.md, generate groff manpage from README.md 2013-11-04 15:33:01 +09:00
1c1d1931cb update ChangeLog 2013-11-04 12:19:36 +09:00
2ecae04c89 update git2changelog to accept non-ascii output 2013-11-04 12:18:27 +09:00
30c77dfb6f add manpage 2013-11-04 12:15:09 +09:00
ed646ccf79 fix gcc warnings 2013-11-03 22:40:26 +09:00
8009f87dca Merge pull request #7 from rbrito/misc-fixes
gitignore: Add list of files to ignore.
2013-11-03 05:26:02 -08:00
d09acb7283 gitignore: Add list of files to ignore.
All of these files are generated by autotools or by the compilation and they
do not belong in a version control.

Signed-off-by: Rogério Brito <rbrito@ime.usp.br>
2013-11-03 10:45:51 -02:00
8ac221b853 update ChangeLog 2013-11-03 18:13:59 +09:00
2c116b15d5 bump version 2013-11-03 18:12:43 +09:00
e1adc17835 add --sbr-ratio to support AACENC_SBR_RATIO appeared on libFDK 3.4.12 2013-11-03 18:09:56 +09:00
3c0f152d39 support 7.1 channel mode added on FDK 3.4.12 2013-11-03 18:08:46 +09:00
5732f1f6c5 update ChangeLog 2013-10-30 19:05:13 +09:00
a3271927b8 bump version 2013-10-30 19:02:00 +09:00
3de0e22dcc use tell() to obtain data chunk offset 2013-10-30 14:27:28 +09:00
d533c8e002 rename aacenc_result_t -> aacenc_frame_t, simplify write_sample() 2013-10-30 11:58:30 +09:00
c5eec15549 prepend 1 sample zero padding in case of SBR and enc_delay is odd
This is required because odd enc_delay cannot be exactly expressed in
downsampled scale, and HE-AACv2 FDK encoder actually has odd enc_delay.
2013-10-30 02:09:23 +09:00
3b518efd31 cleanup interface of aac_encode_frame() 2013-10-30 02:09:23 +09:00
556a3db11b add some copyright notice 2013-10-30 02:09:23 +09:00
4d48b091d4 smart padding for better gapless playback
Taken smart padding code using LPC extrapolation from vorbis/opus.
Padding is done on both beginning and ending, but enc_delay and padding
remains the same (we discard extra padding frame introduced on our side
after encoding).
2013-10-30 02:09:08 +09:00
d11b044131 fix unused variable warning 2013-10-29 19:36:22 +09:00
6709cf694c fix warning: cast size_t as sprintf() arg to int 2013-10-29 19:36:22 +09:00
7259767b09 fix vcxproj 2013-10-29 19:24:29 +09:00
3aa2787e34 fix pcm_seek() to inline 2013-10-29 19:24:29 +09:00
b159a7b095 bump version 2013-10-27 22:43:18 +09:00
be234dc464 add --include-sbr-delay 2013-10-27 22:40:42 +09:00
c9ac59e8d3 fix help message: show -I as shorthand for --ignorelength 2013-10-27 21:32:42 +09:00
9b8f9915c2 remove --sbr-signaling
Instead, we always use explicit/backward compatible SBR signaling by
ASC extension in case of m4a, which is not supported by FDK library
(so we do it on our side).
For LOAS, we use explicit hierarchical signaling.
2013-10-27 21:15:12 +09:00
5ccbfaa710 re-fix #ifdef cond for lrint() 2013-10-26 11:29:30 +09:00
8f05e0751b tag mapping: add recorded date and tempo, remove performer->artist 2013-10-26 01:38:58 +09:00
053279541b fix MSVC12 build issue 2013-10-25 17:04:26 +09:00
f48bf1294c fix build issue on platform where fileno is a naive macro 2013-10-25 10:25:33 +09:00
8896249ac5 update ChangeLog 2013-10-25 00:09:27 +09:00
5f0d784cd3 bump version 2013-10-25 00:03:13 +09:00
6a3b77de8e update README 2013-10-25 00:02:23 +09:00
1af8624b00 caf input support 2013-10-24 23:47:03 +09:00
29a8f73faf refactor pcm io routines 2013-10-24 10:38:27 +09:00
3b666b7546 cleanup metadata handling 2013-10-23 23:35:23 +09:00
8cb6378fca --tag-from-json: properly support number/total format in json track field 2013-10-23 12:19:14 +09:00
bd8c9a5d15 bump version 2013-10-22 19:50:35 +09:00
91ef87b610 fixed bogus sgpd written on --gapless-mode=1 and 2 2013-10-22 19:49:01 +09:00
29e0948097 bump version 2013-10-21 00:35:51 +09:00
e8e9f79eec reimplement int16 conversion as pcm_reader 2013-10-21 00:17:14 +09:00
2d744bd56c add abstraction layer for pcm reading 2013-10-20 22:50:10 +09:00
2f6fc566cc improve handling of avgBitrate 2013-10-20 21:33:06 +09:00
0c502d30e5 update ChangeLog and git2changelog.py 2013-10-19 12:54:50 +09:00
b87f2251a4 bump version 2013-10-18 22:36:10 +09:00
afe73f4916 update README 2013-10-18 22:35:34 +09:00
fe2d3aa3e7 set avgBitrate field to zero for 14496-1 compliance 2013-10-18 22:11:09 +09:00
2f960ef8fd updated ChangeLog with new git2changelog.py 2013-09-07 15:58:59 +09:00
30 changed files with 3363 additions and 668 deletions

22
.gitignore vendored Normal file
View File

@ -0,0 +1,22 @@
*.o
*~
Makefile
Makefile.in
aclocal.m4
autom4te.cache
compile
config.guess
config.h
config.h.in
config.log
config.status
config.sub
configure
depcomp
install-sh
missing
missings/.deps/
src/.deps/
src/.dirstamp
stamp-h1
fdkaac

318
ChangeLog
View File

@ -0,0 +1,318 @@
2013-11-08 nu774 <honeycomb77@gmail.com>
* update ChangeLog [HEAD]
* bump version [v0.5.1]
* fix to use libFDKAAC signaling mode 1
2013-11-05 nu774 <honeycomb77@gmail.com>
* fix README
2013-11-04 nu774 <honeycomb77@gmail.com>
* rename README.md -> README
* Update README -> README.md, generate groff manpage from README.md
* update ChangeLog
* update git2changelog to accept non-ascii output
* add manpage
2013-11-03 nu774 <honeycomb77@gmail.com>
* fix gcc warnings
* Merge pull request #7 from rbrito/misc-fixes
2013-11-03 Rogério Brito <rbrito@ime.usp.br>
* gitignore: Add list of files to ignore.
2013-11-03 nu774 <honeycomb77@gmail.com>
* update ChangeLog
* bump version [v0.5.0]
* add --sbr-ratio to support AACENC_SBR_RATIO appeared on libFDK 3.4.12
* support 7.1 channel mode added on FDK 3.4.12
2013-10-30 nu774 <honeycomb77@gmail.com>
* update ChangeLog
* bump version [v0.4.2]
* use tell() to obtain data chunk offset
* rename aacenc_result_t -> aacenc_frame_t, simplify write_sample()
* prepend 1 sample zero padding in case of SBR and enc_delay is odd
* cleanup interface of aac_encode_frame()
* add some copyright notice
2013-10-29 nu774 <honeycomb77@gmail.com>
* smart padding for better gapless playback
* fix unused variable warning
* fix warning: cast size_t as sprintf() arg to int
* fix vcxproj
* fix pcm_seek() to inline
2013-10-27 nu774 <honeycomb77@gmail.com>
* bump version [v0.4.1]
* add --include-sbr-delay
* fix help message: show -I as shorthand for --ignorelength
* remove --sbr-signaling
2013-10-26 nu774 <honeycomb77@gmail.com>
* re-fix #ifdef cond for lrint() [old]
* tag mapping: add recorded date and tempo, remove performer->artist
2013-10-25 nu774 <honeycomb77@gmail.com>
* fix MSVC12 build issue
* fix build issue on platform where fileno is a naive macro
* update ChangeLog
* bump version [v0.4.0]
* update README
2013-10-24 nu774 <honeycomb77@gmail.com>
* caf input support
* refactor pcm io routines
2013-10-23 nu774 <honeycomb77@gmail.com>
* cleanup metadata handling
* --tag-from-json: properly support number/total format in json track field
2013-10-22 nu774 <honeycomb77@gmail.com>
* bump version [v0.3.3]
* fixed bogus sgpd written on --gapless-mode=1 and 2
2013-10-21 nu774 <honeycomb77@gmail.com>
* bump version [v0.3.2]
2013-10-20 nu774 <honeycomb77@gmail.com>
* reimplement int16 conversion as pcm_reader
* add abstraction layer for pcm reading
* improve handling of avgBitrate
2013-10-19 nu774 <honeycomb77@gmail.com>
* update ChangeLog and git2changelog.py
2013-10-18 nu774 <honeycomb77@gmail.com>
* bump version [v0.3.1]
* update README
* set avgBitrate field to zero for 14496-1 compliance
2013-09-07 nu774 <honeycomb77@gmail.com>
* updated ChangeLog with new git2changelog.py
2013-06-14 nu774 <honeycomb77@gmail.com>
* add --moov-before-mdat [v0.3.0]
2013-03-04 nu774 <honeycomb77@gmail.com>
* fix an error message
2013-03-03 nu774 <honeycomb77@gmail.com>
* bump version [v0.2.0]
* add --gapless-mode
2013-02-20 nu774 <honeycomb77@gmail.com>
* simplify __timeb64 condition
* use fseeko64() on i686-pc-mingw32
2013-02-18 nu774 <honeycomb77@gmail.com>
* fix build issue on i686-pc-mingw (struct __timeb64 is missing)
2013-02-17 nu774 <honeycomb77@gmail.com>
* bump version [v0.1.9]
* fix to accept option -C
2013-02-16 nu774 <honeycomb77@gmail.com>
* bump version [v0.1.8]
* refine json metadata importing
* m4af: duplication check on adding tags
2013-02-15 nu774 <honeycomb77@gmail.com>
* add --tag-from-json [v0.1.7]
* fix implicit int variable decl.
* update m4af
2013-02-03 nu774 <honeycomb77@gmail.com>
* bump version [v0.1.6]
* win32: change _wfopen() -> wfsopen()
2013-01-30 nu774 <honeycomb77@gmail.com>
* update README (add note for character encoding)
2013-01-28 nu774 <honeycomb77@gmail.com>
* bump version [v0.1.5]
* gracefully shutdown on signals
2013-01-27 nu774 <honeycomb77@gmail.com>
* fix MSVC project build issue
2013-01-25 nu774 <honeycomb77@gmail.com>
* bump version [v0.1.4]
* add --tag-from-file
2013-01-24 nu774 <honeycomb77@gmail.com>
* add --silent
2013-01-19 nu774 <honeycomb77@gmail.com>
* retab
* bump version [v0.1.3]
* fix crash on wrong long option, rename --ignore-length to --ignorelength
2013-01-17 nu774 <honeycomb77@gmail.com>
* bump version [v0.1.2]
* compat_win32: free argv with atexit()
* take care of COPYRIGHT-SIGN in UTF-8
2013-01-15 nu774 <honeycomb77@gmail.com>
* bump version [v0.1.1]
* fix return type of put_type_entry() to void
* add ADTS header size(7) to output byte length
2013-01-13 nu774 <honeycomb77@gmail.com>
* fix mp4 duration & version calcuration [v0.1.0]
2013-01-11 nu774 <honeycomb77@gmail.com>
* add support for xid
* support for i686-pc-mingw32 (missing _vscprintf)
2013-01-10 nu774 <honeycomb77@gmail.com>
* bump version [v0.0.9]
* rename basename() -> aacenc_basename() and move to compat layer
2013-01-09 nu774 <honeycomb77@gmail.com>
* add --tag and --long-tag
* fix corner case of progress display
* calculate length from file size
* raw input support
2013-01-08 nu774 <honeycomb77@gmail.com>
* insert a white space in progress message
2013-01-07 nu774 <honeycomb77@gmail.com>
* fix typo of bitrate-mode option [v0.0.8]
* more static inlining (missed on the previous commit) [v0.0.7]
* check error of fread() and fwrite() [v0.0.6]
* change inline->static inline to follow C99 semantics (for Clang)
* explicitly add -lfdk-aac to LDADD in Makefile.am
* add some files to EXTRA_DIST in Makefile.am
* fixed a typo in usage message [v0.0.5]
2013-01-06 nu774 <honeycomb77@gmail.com>
* add MSVC projects
* add .gitattributes
* more tweak on configure.ac and Makefile.am (take care of getopt_long) [v0.0.4]
* use fstat() to test seekability of input file
* retrieve bitrate for tool tag with aacEncoder_GetParam()
* output to current working directory by default
2013-01-05 nu774 <honeycomb77@gmail.com>
* zero clear LIB_INFO before calling aacEncGetLibInfo() [v0.0.3]
* tweak configure.ac and Makefile.am
* update version.h [v0.0.2]
* fixed to clip before converting float to int
* initial commit [v0.0.1]

View File

@ -96,24 +96,33 @@ copy ..\fdk-aac\libSYS\include\machine_type.h include\fdk-aac\ </Command>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\missings\getopt.c" /> <ClCompile Include="..\missings\getopt.c" />
<ClCompile Include="..\src\aacenc.c" /> <ClCompile Include="..\src\aacenc.c" />
<ClCompile Include="..\src\caf_reader.c" />
<ClCompile Include="..\src\compat_win32.c" /> <ClCompile Include="..\src\compat_win32.c" />
<ClCompile Include="..\src\extrapolater.c" />
<ClCompile Include="..\src\lpc.c" />
<ClCompile Include="..\src\lpcm.c" /> <ClCompile Include="..\src\lpcm.c" />
<ClCompile Include="..\src\m4af.c" /> <ClCompile Include="..\src\m4af.c" />
<ClCompile Include="..\src\main.c" /> <ClCompile Include="..\src\main.c" />
<ClCompile Include="..\src\metadata.c" /> <ClCompile Include="..\src\metadata.c" />
<ClCompile Include="..\src\parson.c" /> <ClCompile Include="..\src\parson.c" />
<ClCompile Include="..\src\pcm_readhelper.c" />
<ClCompile Include="..\src\pcm_sint16_converter.c" />
<ClCompile Include="..\src\progress.c" /> <ClCompile Include="..\src\progress.c" />
<ClCompile Include="..\src\wav_reader.c" /> <ClCompile Include="..\src\wav_reader.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\missings\getopt.h" /> <ClInclude Include="..\missings\getopt.h" />
<ClInclude Include="..\src\aacenc.h" /> <ClInclude Include="..\src\aacenc.h" />
<ClInclude Include="..\src\caf_reader.h" />
<ClInclude Include="..\src\catypes.h" />
<ClInclude Include="..\src\compat.h" /> <ClInclude Include="..\src\compat.h" />
<ClInclude Include="..\src\lpc.h" />
<ClInclude Include="..\src\lpcm.h" /> <ClInclude Include="..\src\lpcm.h" />
<ClInclude Include="..\src\m4af.h" /> <ClInclude Include="..\src\m4af.h" />
<ClInclude Include="..\src\m4af_endian.h" /> <ClInclude Include="..\src\m4af_endian.h" />
<ClInclude Include="..\src\metadata.h" /> <ClInclude Include="..\src\metadata.h" />
<ClInclude Include="..\src\parson.h" /> <ClInclude Include="..\src\parson.h" />
<ClInclude Include="..\src\pcm_reader.h" />
<ClInclude Include="..\src\progress.h" /> <ClInclude Include="..\src\progress.h" />
<ClInclude Include="..\src\wav_reader.h" /> <ClInclude Include="..\src\wav_reader.h" />
</ItemGroup> </ItemGroup>

View File

@ -18,9 +18,18 @@
<ClCompile Include="..\src\aacenc.c"> <ClCompile Include="..\src\aacenc.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\src\caf_reader.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\compat_win32.c"> <ClCompile Include="..\src\compat_win32.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\src\extrapolater.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\lpc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\lpcm.c"> <ClCompile Include="..\src\lpcm.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -30,6 +39,12 @@
<ClCompile Include="..\src\main.c"> <ClCompile Include="..\src\main.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\src\pcm_readhelper.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\pcm_sint16_converter.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\progress.c"> <ClCompile Include="..\src\progress.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -44,9 +59,18 @@
<ClInclude Include="..\src\aacenc.h"> <ClInclude Include="..\src\aacenc.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\src\caf_reader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\catypes.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\compat.h"> <ClInclude Include="..\src\compat.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\src\lpc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\lpcm.h"> <ClInclude Include="..\src\lpcm.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -56,6 +80,9 @@
<ClInclude Include="..\src\m4af_endian.h"> <ClInclude Include="..\src\m4af_endian.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\src\pcm_reader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\progress.h"> <ClInclude Include="..\src\progress.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>

View File

@ -5,14 +5,21 @@ bin_PROGRAMS = fdkaac
fdkaac_SOURCES = \ fdkaac_SOURCES = \
src/aacenc.c \ src/aacenc.c \
src/caf_reader.c \
src/extrapolater.c \
src/lpc.c \
src/lpcm.c \ src/lpcm.c \
src/m4af.c \ src/m4af.c \
src/main.c \ src/main.c \
src/metadata.c \ src/metadata.c \
src/parson.c \ src/parson.c \
src/pcm_readhelper.c \
src/pcm_sint16_converter.c \
src/progress.c \ src/progress.c \
src/wav_reader.c src/wav_reader.c
dist_man_MANS = man/fdkaac.1
fdkaac_LDADD = \ fdkaac_LDADD = \
@LIBICONV@ -lfdk-aac -lm @LIBICONV@ -lfdk-aac -lm

400
README
View File

@ -1,102 +1,332 @@
========================================================================== % FDKAAC(1)
fdkaac - command line frontend encoder for libfdk-aac % nu774 <honeycomb77@gmail.com>
========================================================================== % November, 2013
Prerequisites NAME
------------- ====
You need libfdk-aac.
On Posix environment, you will also need GNU gettext (for iconv.m4) and
GNU autoconf/automake.
How to build on Posix environment fdkaac - command line frontend for libfdk-aac encoder
---------------------------------
First, you need to build libfdk-aac and install on your system.
Once you have done it, the following will do the task.
(MinGW build can be done the same way, and doesn't require gettext/iconv)
$ autoreconf -i SYNOPSIS
$ ./configure && make && make install ========
How to build on MSVC **fdkaac** [OPTIONS] [FILE]
--------------------
First you have to extract libfdk-aac source here, so that directory tree will
look like the following:
+- fdk-aac ---+-documentation
| +-libAACdec
| +-libAACenc
| :
+- m4
+- missings
+- MSVC
+- src
MSVC solution for Visual Studio 2010 is under MSVC directory. DESCRIPTION
===========
Tagging Options **fdkaac** reads linear PCM audio in either WAV, raw PCM, or CAF format,
--------------- and encodes it into either M4A / AAC file.
Generic tagging options like --tag, --tag-from-file, --long-tag allows you
to set arbitrary tags.
Available tags and their fcc (four char code) for --tag and --tag-from-file
can be found at http://code.google.com/p/mp4v2/wiki/iTunesMetadata
For tags such as Artist where first char of fcc is copyright sign, If the input file is "-", data is read from stdin. Likewise, if the
you can skip first char and just say like --tag="ART:Foo Bar" or output file is "-", data is written to stdout if one of streamable AAC
--tag-from-file=lyr:/path/to/your/lyrics.txt transport formats are selected by **-f**.
Currently, --tag-from-file just stores file contents into m4a without any When CAF input and M4A output is used, tags in CAF file are copied into
character encoding / line terminater conversion. the resulting M4A.
Therefore, only use UTF-8 (without BOM) when setting text tags by this option.
On the other hand, --tag / --long-tag (and other command line arguments) are OPTIONS
converted from locale character encoding to UTF-8 on Posix environment. =======
On Windows, command line arguments are always treated as Unicode.
Tagging using JSON -h, --help
------------------ : Show command help
With --tag-from-json, fdkaac can read JSON file and set tags from it.
By default, tags are assumed to be in the root object(dictionary) like this:
{ -o \<FILE\>
"title": "No Expectations", : Output filename.
"artist": "The Rolling Stones",
"album": "Beggars Banquet",
"track": 2
}
In this case, you can simply specify the filename like: -p, --profile \<n\>
--tag-from-json=/path/to/json : Target profile (MPEG4 audio object type, AOT)
If the object containing tags is placed somewhere else, you can optionally 2
specify the path of the object with dotted notation. : MPEG-4 AAC LC (default)
{ 5
"format" : { : MPEG-4 HE-AAC (SBR)
"filename" : "Middle Curse.flac",
"nb_streams" : 1,
"format_name" : "flac",
"format_long_name" : "raw FLAC",
"start_time" : "N/A",
"duration" : "216.146667",
"size" : "11851007.000000",
"bit_rate" : "438628.000000",
"tags" : {
"ALBUM" : "Scary World Theory",
"ARTIST" : "Lali Puna",
"DATE" : "2001",
"DISCID" : "9208CC0A",
"TITLE" : "Middle Curse",
"TRACKTOTAL" : "10",
"track" : "2"
}
}
}
In this example, tags are placed under the object "format.tags". 29
("format" is a child of the root, and "tags" is a child of the "format"). : MPEG-4 HE-AAC v2 (SBR+PS)
In this case, you can say:
--tag-from-json=/path/to/json?format.tags
For your information, ffprobe of ffmpeg project (or avprobe of libav) can 23
output media information/metadata in json format like this. : MPEG-4 AAC LD
39
: MPEG-4 AAC ELD
129
: MPEG-2 AAC LC
132
: MPEG-2 HE-AAC (SBR)
156
: MPEG-2 HE-AAC v2 (SBR+PS)
-b, --bitrate \<n\>
: Target bitrate (for CBR)
-m, --bitrate-mode \<n\>
: Bitrate configuration mode. Available VBR quality value depends on
other parameters such as profile, sample rate, or number of
channels.
0
: CBR (default)
1-5
: VBR (higher value -\> higher bitrate)
-w, --bandwidth \<n\>
: Frequency bandwidth (lowpass cut-off frequency) in Hz. Available on
AAC LC only.
-a, --afterburner \<n\>
: Configure afterburner mode. When enabled, quality is increased at
the expense of additional computational workload.
0
: Off
1
: On (default)
-L, --lowdelay-sbr \<n\>
: Configure SBR activity on AAC ELD.
-1
: Use ELD SBR auto configuration
0
: Disable SBR on ELD (default)
1
: Enable SBR on ELD
-s, --sbr-ratio \<n\>
: Controls activation of downsampled SBR.
0
: Use lib default (default)
1
: Use downsampled SBR (default for ELD+SBR)
2
: Use dual-rate SBR (default for HE-AAC)
Dual-rate SBR is what is normally used for HE-AAC, where AAC is
encoded at half the sample rate of SBR, hence "dual rate". On the
other hand, downsampled SBR uses same sample rate for both of AAC
and SBR (single rate), therefore downsampled SBR typically consumes
more bitrate.
Downsampled SBR is newly introduced feature in FDK encoder library
version 3.4.12. When libfdk-aac in the system doesn't support this,
dual-rate SBR will be used. When available, dual-rate SBR is the
default for HE-AAC and downsampled SBR is the default for ELD+SBR.
Note that downsampled HE-AAC is not so common as dual-rate one. When
downsampled HE-AAC is selected, **fdkaac** is forced to choose
explicit hierarchical SBR signaling, which (at least) iTunes doesn't
accept.
-f, --transport-format \<n\>
: Transport format. Tagging and gapless playback is only available on
M4A. Streaming to stdout is only available on others.
0
: M4A (default)
1
: ADIF
2
: ADTS
6
: LATM MCP=1
7
: LATM MCP=0
10
: LOAS/LATM (LATM within LOAS)
-C, --adts-crc-check
: Add CRC protection on ADTS header.
-h, --header-period \<n\>
: StreamMuxConfig/PCE repetition period in the transport layer.
-G, --gapless-mode \<n\>
: Method to declare amount of encoder delay (and padding) in M4A
container. These values are mandatory for proper gapless playback on
player side.
0
: iTunSMPB (default)
1
: ISO standard (edts and sgpd)
2
: Both
--include-sbr-delay
: When specified, count SBR decoder delay in encoder delay.
This is not iTunes compatible and will lead to gapless playback
issue on LC only decoder, but this is the default behavior of FDK
library.
Whether counting SBR decoder delay in encoder delay or not result in
incompatibility in gapless playback. You should pick which one will
work for your favorite player.
However, it's better not to choose SBR at all if you want gapless
playback. LC doesn't have such issues.
-I, --ignorelength
: Ignore length field of data chunk in input WAV file.
-S, --silent
: Don't print progress messages.
--moov-before-mdat
: Place moov box before mdat box in M4A container. This option might
be important for some hardware players, that are known to refuse
moov box placed after mdat box.
-R, --raw
: Regard input as raw PCM.
--raw-channels \<n\>
: Specify number of channels of raw input (default: 2)
--raw-rate \<n\>
: Specify sample rate of raw input (default: 44100)
--raw-format \<spec\>
: Specify sample format of raw input (default: "S16L"). **Spec** is as
the following (case insensitive):
1st char -- type of sample
: **S** (igned) | **U** (nsigned) | **F** (loat)
2nd part (in digits)
: bits per channel
Last char -- endianness (can be omitted)
: **L** (ittle, default) | **B** (ig)
--title \<string\>
: Set title tag.
--artist \<string\>
: Set artist tag.
--album \<string\>
: Set album tag.
--genre \<string\>
: Set genre tag.
--date \<string\>
: Set date tag.
--composer \<string\>
: Set composer tag.
--grouping \<string\>
: Set grouping tag.
--comment \<string\>
: Set comment tag.
--album-artist \<string\>
: Set album artist tag.
--track \<number[/total]\>
: Set track tag, with or without number of total tracks.
--disk \<number[/total]\>
: Set disk tag, with or without number of total discs.
--tempo \<n\>
: Set tempo (BPM) tag.
--tag \<fcc\>:\<value\>
: Set iTunes predefined tag with explicit fourcc key and value. See
[https://code.google.com/p/mp4v2/wiki/iTunesMetadata](https://code.google.com/p/mp4v2/wiki/iTunesMetadata)
for known predefined keys. You can omit first char of **fcc** when
it is the copyright sign.
--tag-from-file \<fcc\>:\<filename\>
: Same as --tag, but set content of file as tag value.
--long-tag \<name\>:\<value\>
: Set arbitrary tag as iTunes custom metadata. Stored in
com.apple.iTunes field.
--tag-from-json \<filename[?dot\_notation]\>
: Read tags from JSON. By default, tags are assumed to be direct
children of the root object in JSON. Optionally you can specify
arbitrary dot notation to locate the object containing tags.
EXAMPLES
========
Encode WAV file into a M4A file. MPEG4 AAC LC, VBR quality 3:
fdkaac -m3 foo.wav
Encode WAV file into a M4A file. MPEG4 HE-AAC, bitrate 64kbps:
fdkaac -p5 -b64 foo.wav
Piping from **ffmpeg** (you need version supporting CAF output):
ffmpeg -i foo.flac -f caf - | fdkaac -b128 - -o foo.m4a
Import tags via json:
ffprobe -v 0 -of json -show_format foo.flac >foo.json
flac -dc foo.flac | \
fdkaac - -ox.m4a -m2 --import-tag-from-json=foo.json?format.tags
NOTES
=====
Upto 32bit integer or 64bit floating point format is supported as input.
However, FDK library is implemented based on fixed point math and only
supports 16bit integer PCM. Therefore, be wary of clipping. You might
want to dither/noise shape beforehand when your input has higher
resolution.
Following channel layouts are supported by the encoder.
1ch
: C
2ch
: L R
3ch
: C L R
4ch
: C L R Cs
5ch
: C L R Ls Rs
5.1ch
: C L R Ls Rs LFE
7.1ch (front)
: C Lc Rc L R Ls Rs LFE
7.1ch (rear)
: C L R Ls Rs Rls Rrs LFE
Note that not all tags can be read/written this way.

2
genman.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
pandoc -s -f markdown -t man README >fdkaac.1 && mv -f fdkaac.1 man/fdkaac.1

44
git2changelog.py Executable file
View File

@ -0,0 +1,44 @@
#!/usr/bin/env python
# Copyright (C) 2013 nu774
# For conditions of distribution and use, see copyright notice in COPYING
import sys
import re
from subprocess import Popen, PIPE
from itertools import groupby
from collections import namedtuple
GITLOG_FMT = 'commit %H%nauthor %cn <%ae>%ndate %ad%nsubject %s%nref %d%n%n'
GITLOG_CMD = ['git','log','--date=short','--format={0}'.format(GITLOG_FMT)]
Commit = namedtuple('Commit', 'commit author date subject ref')
def parse_gitlog(stream):
re_decode_ref = re.compile(r'(?<=\()([^,)]+)')
re_strip_tag = re.compile(r'^tag: ')
commit = dict()
for line in stream:
fields = line.decode('utf-8').rstrip('\r\n').split(' ', 1)
if len(fields) == 2:
key, value = fields
if key == 'ref':
m = re_decode_ref.search(value)
if m:
value = ' [{0}]'.format(re_strip_tag.sub('', m.group()))
else:
value = ''
commit[key] = value
elif commit:
yield Commit(**commit)
commit = dict()
output=sys.stdout.write
with Popen(GITLOG_CMD, shell=False, stdout=PIPE).stdout as pipe:
commits = parse_gitlog(pipe)
commits_by_date_author = groupby(commits, key=lambda x: (x.date, x.author))
for (date, author), commits in commits_by_date_author:
output(u'{0} {1}\n\n'.format(date, author).encode('utf-8'))
for c in commits:
output(u' * {0}{1}\n\n'.format(c.subject, c.ref).encode('utf-8'))

493
man/fdkaac.1 Normal file
View File

@ -0,0 +1,493 @@
.TH FDKAAC 1 "November, 2013"
.SH NAME
.PP
fdkaac \- command line frontend for libfdk\-aac encoder
.SH SYNOPSIS
.PP
\f[B]fdkaac\f[] [OPTIONS][FILE]
.SH DESCRIPTION
.PP
\f[B]fdkaac\f[] reads linear PCM audio in either WAV, raw PCM, or CAF
format, and encodes it into either M4A / AAC file.
.PP
If the input file is "\-", data is read from stdin.
Likewise, if the output file is "\-", data is written to stdout if one
of streamable AAC transport formats are selected by \f[B]\-f\f[].
.PP
When CAF input and M4A output is used, tags in CAF file are copied into
the resulting M4A.
.SH OPTIONS
.TP
.B \-h, \-\-help
Show command help
.RS
.RE
.TP
.B \-o <FILE>
Output filename.
.RS
.RE
.TP
.B \-p, \-\-profile <n>
Target profile (MPEG4 audio object type, AOT)
.RS
.TP
.B 2
MPEG\-4 AAC LC (default)
.RS
.RE
.TP
.B 5
MPEG\-4 HE\-AAC (SBR)
.RS
.RE
.TP
.B 29
MPEG\-4 HE\-AAC v2 (SBR+PS)
.RS
.RE
.TP
.B 23
MPEG\-4 AAC LD
.RS
.RE
.TP
.B 39
MPEG\-4 AAC ELD
.RS
.RE
.TP
.B 129
MPEG\-2 AAC LC
.RS
.RE
.TP
.B 132
MPEG\-2 HE\-AAC (SBR)
.RS
.RE
.TP
.B 156
MPEG\-2 HE\-AAC v2 (SBR+PS)
.RS
.RE
.RE
.TP
.B \-b, \-\-bitrate <n>
Target bitrate (for CBR)
.RS
.RE
.TP
.B \-m, \-\-bitrate\-mode <n>
Bitrate configuration mode.
Available VBR quality value depends on other parameters such as profile,
sample rate, or number of channels.
.RS
.TP
.B 0
CBR (default)
.RS
.RE
.TP
.B 1\-5
VBR (higher value \-> higher bitrate)
.RS
.RE
.RE
.TP
.B \-w, \-\-bandwidth <n>
Frequency bandwidth (lowpass cut\-off frequency) in Hz.
Available on AAC LC only.
.RS
.RE
.TP
.B \-a, \-\-afterburner <n>
Configure afterburner mode.
When enabled, quality is increased at the expense of additional
computational workload.
.RS
.TP
.B 0
Off
.RS
.RE
.TP
.B 1
On (default)
.RS
.RE
.RE
.TP
.B \-L, \-\-lowdelay\-sbr <n>
Configure SBR activity on AAC ELD.
.RS
.TP
.B \-1
Use ELD SBR auto configuration
.RS
.RE
.TP
.B 0
Disable SBR on ELD (default)
.RS
.RE
.TP
.B 1
Enable SBR on ELD
.RS
.RE
.RE
.TP
.B \-s, \-\-sbr\-ratio <n>
Controls activation of downsampled SBR.
.RS
.TP
.B 0
Use lib default (default)
.RS
.RE
.TP
.B 1
Use downsampled SBR (default for ELD+SBR)
.RS
.RE
.TP
.B 2
Use dual\-rate SBR (default for HE\-AAC)
.RS
.RE
.PP
Dual\-rate SBR is what is normally used for HE\-AAC, where AAC is
encoded at half the sample rate of SBR, hence "dual rate".
On the other hand, downsampled SBR uses same sample rate for both of AAC
and SBR (single rate), therefore downsampled SBR typically consumes more
bitrate.
.PP
Downsampled SBR is newly introduced feature in FDK encoder library
version 3.4.12.
When libfdk\-aac in the system doesn\[aq]t support this, dual\-rate SBR
will be used.
When available, dual\-rate SBR is the default for HE\-AAC and
downsampled SBR is the default for ELD+SBR.
.PP
Note that downsampled HE\-AAC is not so common as dual\-rate one.
When downsampled HE\-AAC is selected, \f[B]fdkaac\f[] is forced to
choose explicit hierarchical SBR signaling, which (at least) iTunes
doesn\[aq]t accept.
.RE
.TP
.B \-f, \-\-transport\-format <n>
Transport format.
Tagging and gapless playback is only available on M4A.
Streaming to stdout is only available on others.
.RS
.TP
.B 0
M4A (default)
.RS
.RE
.TP
.B 1
ADIF
.RS
.RE
.TP
.B 2
ADTS
.RS
.RE
.TP
.B 6
LATM MCP=1
.RS
.RE
.TP
.B 7
LATM MCP=0
.RS
.RE
.TP
.B 10
LOAS/LATM (LATM within LOAS)
.RS
.RE
.RE
.TP
.B \-C, \-\-adts\-crc\-check
Add CRC protection on ADTS header.
.RS
.RE
.TP
.B \-h, \-\-header\-period <n>
StreamMuxConfig/PCE repetition period in the transport layer.
.RS
.RE
.TP
.B \-G, \-\-gapless\-mode <n>
Method to declare amount of encoder delay (and padding) in M4A
container.
These values are mandatory for proper gapless playback on player side.
.RS
.TP
.B 0
iTunSMPB (default)
.RS
.RE
.TP
.B 1
ISO standard (edts and sgpd)
.RS
.RE
.TP
.B 2
Both
.RS
.RE
.RE
.TP
.B \-\-include\-sbr\-delay
When specified, count SBR decoder delay in encoder delay.
.RS
.PP
This is not iTunes compatible and will lead to gapless playback issue on
LC only decoder, but this is the default behavior of FDK library.
.PP
Whether counting SBR decoder delay in encoder delay or not result in
incompatibility in gapless playback.
You should pick which one will work for your favorite player.
.PP
However, it\[aq]s better not to choose SBR at all if you want gapless
playback.
LC doesn\[aq]t have such issues.
.RE
.TP
.B \-I, \-\-ignorelength
Ignore length field of data chunk in input WAV file.
.RS
.RE
.TP
.B \-S, \-\-silent
Don\[aq]t print progress messages.
.RS
.RE
.TP
.B \-\-moov\-before\-mdat
Place moov box before mdat box in M4A container.
This option might be important for some hardware players, that are known
to refuse moov box placed after mdat box.
.RS
.RE
.TP
.B \-R, \-\-raw
Regard input as raw PCM.
.RS
.RE
.TP
.B \-\-raw\-channels <n>
Specify number of channels of raw input (default: 2)
.RS
.RE
.TP
.B \-\-raw\-rate <n>
Specify sample rate of raw input (default: 44100)
.RS
.RE
.TP
.B \-\-raw\-format <spec>
Specify sample format of raw input (default: "S16L").
\f[B]Spec\f[] is as the following (case insensitive):
.RS
.TP
.B 1st char \-\- type of sample
\f[B]S\f[] (igned) | \f[B]U\f[] (nsigned) | \f[B]F\f[] (loat)
.RS
.RE
.TP
.B 2nd part (in digits)
bits per channel
.RS
.RE
.TP
.B Last char \-\- endianness (can be omitted)
\f[B]L\f[] (ittle, default) | \f[B]B\f[] (ig)
.RS
.RE
.RE
.TP
.B \-\-title <string>
Set title tag.
.RS
.RE
.TP
.B \-\-artist <string>
Set artist tag.
.RS
.RE
.TP
.B \-\-album <string>
Set album tag.
.RS
.RE
.TP
.B \-\-genre <string>
Set genre tag.
.RS
.RE
.TP
.B \-\-date <string>
Set date tag.
.RS
.RE
.TP
.B \-\-composer <string>
Set composer tag.
.RS
.RE
.TP
.B \-\-grouping <string>
Set grouping tag.
.RS
.RE
.TP
.B \-\-comment <string>
Set comment tag.
.RS
.RE
.TP
.B \-\-album\-artist <string>
Set album artist tag.
.RS
.RE
.TP
.B \-\-track <number[/total]>
Set track tag, with or without number of total tracks.
.RS
.RE
.TP
.B \-\-disk <number[/total]>
Set disk tag, with or without number of total discs.
.RS
.RE
.TP
.B \-\-tempo <n>
Set tempo (BPM) tag.
.RS
.RE
.TP
.B \-\-tag <fcc>:<value>
Set iTunes predefined tag with explicit fourcc key and value.
See <https://code.google.com/p/mp4v2/wiki/iTunesMetadata> for known
predefined keys.
You can omit first char of \f[B]fcc\f[] when it is the copyright sign.
.RS
.RE
.TP
.B \-\-tag\-from\-file <fcc>:<filename>
Same as \-\-tag, but set content of file as tag value.
.RS
.RE
.TP
.B \-\-long\-tag <name>:<value>
Set arbitrary tag as iTunes custom metadata.
Stored in com.apple.iTunes field.
.RS
.RE
.TP
.B \-\-tag\-from\-json <filename[?dot_notation]>
Read tags from JSON.
By default, tags are assumed to be direct children of the root object in
JSON.
Optionally you can specify arbitrary dot notation to locate the object
containing tags.
.RS
.RE
.SH EXAMPLES
.PP
Encode WAV file into a M4A file.
MPEG4 AAC LC, VBR quality 3:
.IP
.nf
\f[C]
fdkaac\ \-m3\ foo.wav
\f[]
.fi
.PP
Encode WAV file into a M4A file.
MPEG4 HE\-AAC, bitrate 64kbps:
.IP
.nf
\f[C]
fdkaac\ \-p5\ \-b64\ foo.wav
\f[]
.fi
.PP
Piping from \f[B]ffmpeg\f[] (you need version supporting CAF output):
.IP
.nf
\f[C]
ffmpeg\ \-i\ foo.flac\ \-f\ caf\ \-\ |\ fdkaac\ \-b128\ \-\ \-o\ foo.m4a
\f[]
.fi
.PP
Import tags via json:
.IP
.nf
\f[C]
ffprobe\ \-v\ 0\ \-of\ json\ \-show_format\ foo.flac\ >foo.json
flac\ \-dc\ foo.flac\ |\ \\
fdkaac\ \-\ \-ox.m4a\ \-m2\ \-\-import\-tag\-from\-json=foo.json?format.tags
\f[]
.fi
.SH NOTES
.PP
Upto 32bit integer or 64bit floating point format is supported as input.
However, FDK library is implemented based on fixed point math and only
supports 16bit integer PCM.
Therefore, be wary of clipping.
You might want to dither/noise shape beforehand when your input has
higher resolution.
.PP
Following channel layouts are supported by the encoder.
.TP
.B 1ch
C
.RS
.RE
.TP
.B 2ch
L R
.RS
.RE
.TP
.B 3ch
C L R
.RS
.RE
.TP
.B 4ch
C L R Cs
.RS
.RE
.TP
.B 5ch
C L R Ls Rs
.RS
.RE
.TP
.B 5.1ch
C L R Ls Rs LFE
.RS
.RE
.TP
.B 7.1ch (front)
C Lc Rc L R Ls Rs LFE
.RS
.RE
.TP
.B 7.1ch (rear)
C L R Ls Rs Rls Rrs LFE
.RS
.RE
.SH AUTHORS
nu774 <honeycomb77@gmail.com>.

View File

@ -10,8 +10,20 @@
#endif #endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "aacenc.h" #include "aacenc.h"
int aacenc_is_sbr_ratio_available()
{
#if AACENCODER_LIB_VL0 < 3 || (AACENCODER_LIB_VL0==3 && AACENCODER_LIB_VL1<4)
return 0;
#else
LIB_INFO lib_info;
aacenc_get_lib_info(&lib_info);
return lib_info.version > 0x03040000;
#endif
}
int aacenc_is_sbr_active(const aacenc_param_t *params) int aacenc_is_sbr_active(const aacenc_param_t *params)
{ {
switch (params->profile) { switch (params->profile) {
@ -25,15 +37,42 @@ int aacenc_is_sbr_active(const aacenc_param_t *params)
return 0; return 0;
} }
int aacenc_is_dual_rate_sbr(const aacenc_param_t *params)
{
if (params->profile == AOT_PS || params->profile == AOT_MP2_PS)
return 1;
else if (params->profile == AOT_SBR || params->profile == AOT_MP2_SBR)
return params->sbr_ratio == 0 || params->sbr_ratio == 2;
else if (params->profile == AOT_ER_AAC_ELD && params->lowdelay_sbr)
return params->sbr_ratio == 2;
return 0;
}
void aacenc_get_lib_info(LIB_INFO *info)
{
LIB_INFO *lib_info = 0;
lib_info = calloc(FDK_MODULE_LAST, sizeof(LIB_INFO));
if (aacEncGetLibInfo(lib_info) == AACENC_OK) {
int i;
for (i = 0; i < FDK_MODULE_LAST; ++i) {
if (lib_info[i].module_id == FDK_AACENC) {
memcpy(info, &lib_info[i], sizeof(LIB_INFO));
break;
}
}
}
free(lib_info);
}
static static
int aacenc_channel_mode(const pcm_sample_description_t *format) int aacenc_channel_mode(const pcm_sample_description_t *format)
{ {
uint32_t chanmask = format->channel_mask; uint32_t chanmask = format->channel_mask;
if (format->channels_per_frame > 6) if (format->channels_per_frame > 8)
return 0; return 0;
if (!chanmask) { if (!chanmask) {
static uint32_t defaults[] = { 0x4, 0x3, 0x7, 0, 0x37, 0x3f }; static uint32_t defaults[] = { 0x4, 0x3, 0x7, 0, 0x37, 0x3f, 0, 0x63f };
chanmask = defaults[format->channels_per_frame - 1]; chanmask = defaults[format->channels_per_frame - 1];
} }
switch (chanmask) { switch (chanmask) {
@ -45,6 +84,10 @@ int aacenc_channel_mode(const pcm_sample_description_t *format)
case 0x107: return MODE_1_2_1; case 0x107: return MODE_1_2_1;
case 0x607: return MODE_1_2_2; case 0x607: return MODE_1_2_2;
case 0x60f: return MODE_1_2_2_1; case 0x60f: return MODE_1_2_2_1;
#if AACENCODER_LIB_VL0 > 3 || (AACENCODER_LIB_VL0==3 && AACENCODER_LIB_VL1>=4)
case 0xff: return MODE_1_2_2_2_1;
case 0x63f: return MODE_7_1_REAR_SURROUND;
#endif
} }
return 0; return 0;
} }
@ -55,8 +98,11 @@ int aacenc_init(HANDLE_AACENCODER *encoder, const aacenc_param_t *params,
{ {
int channel_mode; int channel_mode;
int aot; int aot;
LIB_INFO lib_info;
*encoder = 0; *encoder = 0;
aacenc_get_lib_info(&lib_info);
if ((channel_mode = aacenc_channel_mode(format)) == 0) { if ((channel_mode = aacenc_channel_mode(format)) == 0) {
fprintf(stderr, "ERROR: unsupported channel layout\n"); fprintf(stderr, "ERROR: unsupported channel layout\n");
goto FAIL; goto FAIL;
@ -82,13 +128,21 @@ int aacenc_init(HANDLE_AACENCODER *encoder, const aacenc_param_t *params,
fprintf(stderr, "ERROR: unsupported sample rate\n"); fprintf(stderr, "ERROR: unsupported sample rate\n");
goto FAIL; goto FAIL;
} }
aacEncoder_SetParam(*encoder, AACENC_CHANNELMODE, channel_mode); if (aacEncoder_SetParam(*encoder, AACENC_CHANNELMODE,
channel_mode) != AACENC_OK) {
fprintf(stderr, "ERROR: unsupported channel mode\n");
goto FAIL;
}
aacEncoder_SetParam(*encoder, AACENC_BANDWIDTH, params->bandwidth); aacEncoder_SetParam(*encoder, AACENC_BANDWIDTH, params->bandwidth);
aacEncoder_SetParam(*encoder, AACENC_CHANNELORDER, 1); aacEncoder_SetParam(*encoder, AACENC_CHANNELORDER, 1);
aacEncoder_SetParam(*encoder, AACENC_AFTERBURNER, !!params->afterburner); aacEncoder_SetParam(*encoder, AACENC_AFTERBURNER, !!params->afterburner);
if (aot == AOT_ER_AAC_ELD && params->lowdelay_sbr) aacEncoder_SetParam(*encoder, AACENC_SBR_MODE, params->lowdelay_sbr);
aacEncoder_SetParam(*encoder, AACENC_SBR_MODE, 1);
#if AACENCODER_LIB_VL0 > 3 || (AACENCODER_LIB_VL0==3 && AACENCODER_LIB_VL1>=4)
if (lib_info.version > 0x03040000)
aacEncoder_SetParam(*encoder, AACENC_SBR_RATIO, params->sbr_ratio);
#endif
if (aacEncoder_SetParam(*encoder, AACENC_TRANSMUX, if (aacEncoder_SetParam(*encoder, AACENC_TRANSMUX,
params->transport_format) != AACENC_OK) { params->transport_format) != AACENC_OK) {
@ -124,7 +178,7 @@ FAIL:
int aac_encode_frame(HANDLE_AACENCODER encoder, int aac_encode_frame(HANDLE_AACENCODER encoder,
const pcm_sample_description_t *format, const pcm_sample_description_t *format,
const int16_t *input, unsigned iframes, const int16_t *input, unsigned iframes,
uint8_t **output, uint32_t *olen, uint32_t *osize) aacenc_frame_t *output)
{ {
uint32_t ilen = iframes * format->channels_per_frame; uint32_t ilen = iframes * format->channels_per_frame;
AACENC_BufDesc ibdesc = { 0 }, obdesc = { 0 }; AACENC_BufDesc ibdesc = { 0 }, obdesc = { 0 };
@ -142,12 +196,14 @@ int aac_encode_frame(HANDLE_AACENCODER encoder,
unsigned channel_mode, obytes; unsigned channel_mode, obytes;
channel_mode = aacEncoder_GetParam(encoder, AACENC_CHANNELMODE); channel_mode = aacEncoder_GetParam(encoder, AACENC_CHANNELMODE);
obytes = 6144 / 8 * channel_mode + 7; obytes = 6144 / 8 * channel_mode;
if (!*output || *osize < obytes) { if (!output->data || output->capacity < obytes) {
*osize = obytes; uint8_t *p = realloc(output->data, obytes);
*output = realloc(*output, obytes); if (!p) return -1;
output->capacity = obytes;
output->data = p;
} }
obufs[0] = *output; obufs[0] = output->data;
obuf_sizes[0] = obytes; obuf_sizes[0] = obytes;
iargs.numInSamples = ilen ? ilen : -1; /* -1 for signaling EOF */ iargs.numInSamples = ilen ? ilen : -1; /* -1 for signaling EOF */
@ -167,6 +223,6 @@ int aac_encode_frame(HANDLE_AACENCODER encoder,
fprintf(stderr, "ERROR: aacEncEncode() failed\n"); fprintf(stderr, "ERROR: aacEncEncode() failed\n");
return -1; return -1;
} }
*olen = oargs.numOutBytes; output->size = oargs.numOutBytes;
return oargs.numInSamples; return oargs.numInSamples / format->channels_per_frame;
} }

View File

@ -15,6 +15,7 @@
unsigned bandwidth; \ unsigned bandwidth; \
unsigned afterburner; \ unsigned afterburner; \
unsigned lowdelay_sbr; \ unsigned lowdelay_sbr; \
unsigned sbr_ratio; \
unsigned sbr_signaling; \ unsigned sbr_signaling; \
unsigned transport_format; \ unsigned transport_format; \
unsigned adts_crc_check; \ unsigned adts_crc_check; \
@ -24,8 +25,19 @@ typedef struct aacenc_param_t {
AACENC_PARAMS AACENC_PARAMS
} aacenc_param_t; } aacenc_param_t;
typedef struct aacenc_frame_t {
uint8_t *data;
uint32_t size, capacity;
} aacenc_frame_t;
int aacenc_is_sbr_ratio_available();
int aacenc_is_sbr_active(const aacenc_param_t *params); int aacenc_is_sbr_active(const aacenc_param_t *params);
int aacenc_is_dual_rate_sbr(const aacenc_param_t *params);
void aacenc_get_lib_info(LIB_INFO *info);
int aacenc_init(HANDLE_AACENCODER *encoder, const aacenc_param_t *params, int aacenc_init(HANDLE_AACENCODER *encoder, const aacenc_param_t *params,
const pcm_sample_description_t *format, const pcm_sample_description_t *format,
AACENC_InfoStruct *info); AACENC_InfoStruct *info);
@ -33,6 +45,6 @@ int aacenc_init(HANDLE_AACENCODER *encoder, const aacenc_param_t *params,
int aac_encode_frame(HANDLE_AACENCODER encoder, int aac_encode_frame(HANDLE_AACENCODER encoder,
const pcm_sample_description_t *format, const pcm_sample_description_t *format,
const int16_t *input, unsigned iframes, const int16_t *input, unsigned iframes,
uint8_t **output, uint32_t *olen, uint32_t *osize); aacenc_frame_t *output);
#endif #endif

252
src/caf_reader.c Normal file
View File

@ -0,0 +1,252 @@
/*
* 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 "caf_reader.h"
#include "m4af.h"
typedef struct caf_reader_t {
pcm_reader_vtbl_t *vtbl;
pcm_sample_description_t sample_format;
int64_t length;
int64_t position;
int64_t data_offset;
pcm_io_context_t io;
aacenc_tag_callback_t tag_callback;
void *tag_ctx;
uint8_t chanmap[8];
} caf_reader_t;
static const pcm_sample_description_t *caf_get_format(pcm_reader_t *reader)
{
return &((caf_reader_t *)reader)->sample_format;
}
static int64_t caf_get_length(pcm_reader_t *reader)
{
return ((caf_reader_t *)reader)->length;
}
static int64_t caf_get_position(pcm_reader_t *reader)
{
return ((caf_reader_t *)reader)->position;
}
static void caf_teardown(pcm_reader_t **reader)
{
free(*reader);
*reader = 0;
}
static
uint32_t caf_next_chunk(caf_reader_t *reader, int64_t *chunk_size)
{
uint32_t fcc;
if (pcm_scanb(&reader->io, "LQ", &fcc, chunk_size) == 2)
return fcc;
return 0;
}
static
int caf_desc(caf_reader_t *reader, int64_t chunk_size)
{
double mSampleRate;
uint32_t mFormatID, mFormatFlags, mBytesPerPacket, mFramesPerPacket,
mChannelsPerFrame, mBitsPerChannel;
pcm_sample_description_t *desc = &reader->sample_format;
ENSURE(chunk_size >= 32);
TRY_IO(pcm_scanb(&reader->io, "QLLLLLL", &mSampleRate, &mFormatID,
&mFormatFlags, &mBytesPerPacket, &mFramesPerPacket,
&mChannelsPerFrame, &mBitsPerChannel) != 7);
ENSURE(mFormatID == M4AF_FOURCC('l','p','c','m'));
ENSURE(mSampleRate && mBytesPerPacket &&
mChannelsPerFrame >= 1 && mChannelsPerFrame <= 8 &&
mBitsPerChannel && mFramesPerPacket == 1 &&
mBytesPerPacket % mChannelsPerFrame == 0 &&
mBytesPerPacket >= mChannelsPerFrame * ((mBitsPerChannel + 7) / 8));
desc->sample_rate = mSampleRate;
desc->bits_per_channel = mBitsPerChannel;
desc->bytes_per_frame = mBytesPerPacket;
desc->channels_per_frame = mChannelsPerFrame;
switch (mFormatFlags) {
case 0: desc->sample_type = PCM_TYPE_SINT_BE; break;
case 1: desc->sample_type = PCM_TYPE_FLOAT_BE; break;
case 2: desc->sample_type = PCM_TYPE_SINT; break;
case 3: desc->sample_type = PCM_TYPE_FLOAT; break;
default: goto FAIL;
}
TRY_IO(pcm_skip(&reader->io, chunk_size - 32));
return 0;
FAIL:
return -1;
}
static
int caf_info(caf_reader_t *reader, int64_t chunk_size)
{
char *buf, *key, *val, *end;
size_t len;
if (chunk_size < 4 || (buf = malloc(chunk_size)) == 0)
return -1;
pcm_read(&reader->io, buf, chunk_size);
key = buf + 4;
end = buf + chunk_size;
do {
if ((val = key + strlen(key) + 1) < end) {
len = strlen(val);
if (reader->tag_callback)
reader->tag_callback(reader->tag_ctx, key, val, len);
key = val + len + 1;
}
} while (key < end && val < end);
if (reader->tag_callback)
reader->tag_callback(reader->tag_ctx, 0, 0, 0);
free(buf);
return 0;
}
static
int caf_read_frames(pcm_reader_t *preader, void *buffer, unsigned nframes)
{
int rc;
unsigned i, j, nbytes;
caf_reader_t *reader = (caf_reader_t *)preader;
unsigned bpf = reader->sample_format.bytes_per_frame;
unsigned nchannels = reader->sample_format.channels_per_frame;
unsigned bpc = bpf / nchannels;
uint8_t tmp[64]; /* enough room for maximum bpf: 8ch float64 */
uint8_t *bp;
uint8_t *chanmap = reader->chanmap;
if (nframes > reader->length - reader->position)
nframes = reader->length - reader->position;
nbytes = nframes * bpf;
if (nbytes) {
if ((rc = pcm_read(&reader->io, buffer, nbytes)) < 0)
return -1;
nframes = rc / bpf;
for (bp = buffer, i = 0; i < nframes; ++i, bp += bpf) {
memcpy(tmp, bp, bpf);
for (j = 0; j < nchannels; ++j)
memcpy(bp + bpc * j, tmp + bpc * chanmap[j], bpc);
}
reader->position += nframes;
}
if (nframes == 0) {
/* fetch info after data chunk */
uint32_t fcc;
int64_t chunk_size;
while ((fcc = caf_next_chunk(reader, &chunk_size)) != 0) {
if (fcc == M4AF_FOURCC('i','n','f','o'))
TRY_IO(caf_info(reader, chunk_size));
else
TRY_IO(pcm_skip(&reader->io, chunk_size));
}
}
return nframes;
FAIL:
return 0;
}
static
int caf_parse(caf_reader_t *reader, int64_t *data_length)
{
uint32_t fcc;
int64_t chunk_size;
*data_length = 0;
/* CAFFileHeader */
TRY_IO(pcm_read32be(&reader->io, &fcc));
ENSURE(fcc == M4AF_FOURCC('c','a','f','f'));
TRY_IO(pcm_skip(&reader->io, 4)); /* mFileVersion, mFileFlags */
while ((fcc = caf_next_chunk(reader, &chunk_size)) != 0) {
if (fcc == M4AF_FOURCC('d','e','s','c'))
TRY_IO(caf_desc(reader, chunk_size));
else if (fcc == M4AF_FOURCC('i','n','f','o'))
TRY_IO(caf_info(reader, chunk_size));
else if (fcc == M4AF_FOURCC('c','h','a','n')) {
ENSURE(reader->sample_format.channels_per_frame);
if (apple_chan_chunk(&reader->io, chunk_size,
&reader->sample_format, reader->chanmap) < 0)
goto FAIL;
} else if (fcc == M4AF_FOURCC('d','a','t','a')) {
TRY_IO(pcm_skip(&reader->io, 4)); /* mEditCount */
*data_length = (chunk_size == ~0ULL) ? chunk_size : chunk_size - 4;
reader->data_offset = pcm_tell(&reader->io);
break;
} else
TRY_IO(pcm_skip(&reader->io, chunk_size));
}
ENSURE(reader->sample_format.channels_per_frame);
ENSURE(fcc == M4AF_FOURCC('d','a','t','a'));
return 0;
FAIL:
return -1;
}
static pcm_reader_vtbl_t caf_vtable = {
caf_get_format,
caf_get_length,
caf_get_position,
caf_read_frames,
caf_teardown
};
pcm_reader_t *caf_open(pcm_io_context_t *io,
aacenc_tag_callback_t tag_callback, void *tag_ctx)
{
caf_reader_t *reader = 0;
int64_t data_length;
unsigned bpf;
if ((reader = calloc(1, sizeof(caf_reader_t))) == 0)
return 0;
memcpy(&reader->io, io, sizeof(pcm_io_context_t));
reader->tag_callback = tag_callback;
reader->tag_ctx = tag_ctx;
memcpy(reader->chanmap, "\000\001\002\003\004\005\006\007", 8);
if (caf_parse(reader, &data_length) < 0) {
free(reader);
return 0;
}
bpf = reader->sample_format.bytes_per_frame;
/* CAF uses -1 to indicate "unknown size" */
if (data_length < 0 || data_length % bpf)
reader->length = INT64_MAX;
else
reader->length = data_length / bpf;
if (reader->length == INT64_MAX) {
if (pcm_seek(&reader->io, 0, SEEK_END) >= 0) {
int64_t size = pcm_tell(&reader->io);
if (size > 0)
reader->length = (size - reader->data_offset) / bpf;
pcm_seek(&reader->io, reader->data_offset, SEEK_SET);
}
}
reader->vtbl = &caf_vtable;
return (pcm_reader_t *)reader;
}

15
src/caf_reader.h Normal file
View File

@ -0,0 +1,15 @@
/*
* Copyright (C) 2013 nu774
* For conditions of distribution and use, see copyright notice in COPYING
*/
#ifndef CAF_READER_H
#define CAF_READER_H
#include "lpcm.h"
#include "pcm_reader.h"
#include "metadata.h"
pcm_reader_t *caf_open(pcm_io_context_t *io,
aacenc_tag_callback_t tag_callback, void *tag_ctx);
#endif

241
src/catypes.h Normal file
View File

@ -0,0 +1,241 @@
#if !defined(__CoreAudioTypes_h__)
#define __CoreAudioTypes_h__
enum { kVariableLengthArray = 1 };
typedef uint32_t AudioChannelLabel;
typedef uint32_t AudioChannelLayoutTag;
struct AudioChannelDescription
{
AudioChannelLabel mChannelLabel;
uint32_t mChannelFlags;
float mCoordinates[3];
};
typedef struct AudioChannelDescription AudioChannelDescription;
struct AudioChannelLayout
{
AudioChannelLayoutTag mChannelLayoutTag;
uint32_t mChannelBitmap;
uint32_t mNumberChannelDescriptions;
AudioChannelDescription mChannelDescriptions[kVariableLengthArray];
};
typedef struct AudioChannelLayout AudioChannelLayout;
enum
{
kAudioChannelLabel_Unknown = 0xFFFFFFFF, // unknown or unspecified other use
kAudioChannelLabel_Unused = 0, // channel is present, but has no intended use or destination
kAudioChannelLabel_UseCoordinates = 100, // channel is described by the mCoordinates fields.
kAudioChannelLabel_Left = 1,
kAudioChannelLabel_Right = 2,
kAudioChannelLabel_Center = 3,
kAudioChannelLabel_LFEScreen = 4,
kAudioChannelLabel_LeftSurround = 5, // WAVE: "Back Left"
kAudioChannelLabel_RightSurround = 6, // WAVE: "Back Right"
kAudioChannelLabel_LeftCenter = 7,
kAudioChannelLabel_RightCenter = 8,
kAudioChannelLabel_CenterSurround = 9, // WAVE: "Back Center" or plain "Rear Surround"
kAudioChannelLabel_LeftSurroundDirect = 10, // WAVE: "Side Left"
kAudioChannelLabel_RightSurroundDirect = 11, // WAVE: "Side Right"
kAudioChannelLabel_TopCenterSurround = 12,
kAudioChannelLabel_VerticalHeightLeft = 13, // WAVE: "Top Front Left"
kAudioChannelLabel_VerticalHeightCenter = 14, // WAVE: "Top Front Center"
kAudioChannelLabel_VerticalHeightRight = 15, // WAVE: "Top Front Right"
kAudioChannelLabel_TopBackLeft = 16,
kAudioChannelLabel_TopBackCenter = 17,
kAudioChannelLabel_TopBackRight = 18,
kAudioChannelLabel_RearSurroundLeft = 33,
kAudioChannelLabel_RearSurroundRight = 34,
kAudioChannelLabel_LeftWide = 35,
kAudioChannelLabel_RightWide = 36,
kAudioChannelLabel_LFE2 = 37,
kAudioChannelLabel_LeftTotal = 38, // matrix encoded 4 channels
kAudioChannelLabel_RightTotal = 39, // matrix encoded 4 channels
kAudioChannelLabel_HearingImpaired = 40,
kAudioChannelLabel_Narration = 41,
kAudioChannelLabel_Mono = 42,
kAudioChannelLabel_DialogCentricMix = 43,
kAudioChannelLabel_CenterSurroundDirect = 44, // back center, non diffuse
kAudioChannelLabel_Haptic = 45,
// first order ambisonic channels
kAudioChannelLabel_Ambisonic_W = 200,
kAudioChannelLabel_Ambisonic_X = 201,
kAudioChannelLabel_Ambisonic_Y = 202,
kAudioChannelLabel_Ambisonic_Z = 203,
// Mid/Side Recording
kAudioChannelLabel_MS_Mid = 204,
kAudioChannelLabel_MS_Side = 205,
// X-Y Recording
kAudioChannelLabel_XY_X = 206,
kAudioChannelLabel_XY_Y = 207,
// other
kAudioChannelLabel_HeadphonesLeft = 301,
kAudioChannelLabel_HeadphonesRight = 302,
kAudioChannelLabel_ClickTrack = 304,
kAudioChannelLabel_ForeignLanguage = 305,
// generic discrete channel
kAudioChannelLabel_Discrete = 400,
// numbered discrete channel
kAudioChannelLabel_Discrete_0 = (1L<<16) | 0,
kAudioChannelLabel_Discrete_1 = (1L<<16) | 1,
kAudioChannelLabel_Discrete_2 = (1L<<16) | 2,
kAudioChannelLabel_Discrete_3 = (1L<<16) | 3,
kAudioChannelLabel_Discrete_4 = (1L<<16) | 4,
kAudioChannelLabel_Discrete_5 = (1L<<16) | 5,
kAudioChannelLabel_Discrete_6 = (1L<<16) | 6,
kAudioChannelLabel_Discrete_7 = (1L<<16) | 7,
kAudioChannelLabel_Discrete_8 = (1L<<16) | 8,
kAudioChannelLabel_Discrete_9 = (1L<<16) | 9,
kAudioChannelLabel_Discrete_10 = (1L<<16) | 10,
kAudioChannelLabel_Discrete_11 = (1L<<16) | 11,
kAudioChannelLabel_Discrete_12 = (1L<<16) | 12,
kAudioChannelLabel_Discrete_13 = (1L<<16) | 13,
kAudioChannelLabel_Discrete_14 = (1L<<16) | 14,
kAudioChannelLabel_Discrete_15 = (1L<<16) | 15,
kAudioChannelLabel_Discrete_65535 = (1L<<16) | 65535
};
#define AudioChannelLayoutTag_GetNumberOfChannels(layoutTag) \
((uint32_t)((layoutTag) & 0x0000FFFF))
enum
{
kAudioChannelLayoutTag_UseChannelDescriptions = (0L<<16) | 0, // use the array of AudioChannelDescriptions to define the mapping.
kAudioChannelLayoutTag_UseChannelBitmap = (1L<<16) | 0, // use the bitmap to define the mapping.
kAudioChannelLayoutTag_Mono = (100L<<16) | 1, // a standard mono stream
kAudioChannelLayoutTag_Stereo = (101L<<16) | 2, // a standard stereo stream (L R) - implied playback
kAudioChannelLayoutTag_StereoHeadphones = (102L<<16) | 2, // a standard stereo stream (L R) - implied headphone playbac
kAudioChannelLayoutTag_MatrixStereo = (103L<<16) | 2, // a matrix encoded stereo stream (Lt, Rt)
kAudioChannelLayoutTag_MidSide = (104L<<16) | 2, // mid/side recording
kAudioChannelLayoutTag_XY = (105L<<16) | 2, // coincident mic pair (often 2 figure 8's)
kAudioChannelLayoutTag_Binaural = (106L<<16) | 2, // binaural stereo (left, right)
kAudioChannelLayoutTag_Ambisonic_B_Format = (107L<<16) | 4, // W, X, Y, Z
kAudioChannelLayoutTag_Quadraphonic = (108L<<16) | 4, // front left, front right, back left, back right
kAudioChannelLayoutTag_Pentagonal = (109L<<16) | 5, // left, right, rear left, rear right, center
kAudioChannelLayoutTag_Hexagonal = (110L<<16) | 6, // left, right, rear left, rear right, center, rear
kAudioChannelLayoutTag_Octagonal = (111L<<16) | 8, // front left, front right, rear left, rear right,
// front center, rear center, side left, side right
kAudioChannelLayoutTag_Cube = (112L<<16) | 8, // left, right, rear left, rear right
// top left, top right, top rear left, top rear right
// MPEG defined layouts
kAudioChannelLayoutTag_MPEG_1_0 = kAudioChannelLayoutTag_Mono, // C
kAudioChannelLayoutTag_MPEG_2_0 = kAudioChannelLayoutTag_Stereo, // L R
kAudioChannelLayoutTag_MPEG_3_0_A = (113L<<16) | 3, // L R C
kAudioChannelLayoutTag_MPEG_3_0_B = (114L<<16) | 3, // C L R
kAudioChannelLayoutTag_MPEG_4_0_A = (115L<<16) | 4, // L R C Cs
kAudioChannelLayoutTag_MPEG_4_0_B = (116L<<16) | 4, // C L R Cs
kAudioChannelLayoutTag_MPEG_5_0_A = (117L<<16) | 5, // L R C Ls Rs
kAudioChannelLayoutTag_MPEG_5_0_B = (118L<<16) | 5, // L R Ls Rs C
kAudioChannelLayoutTag_MPEG_5_0_C = (119L<<16) | 5, // L C R Ls Rs
kAudioChannelLayoutTag_MPEG_5_0_D = (120L<<16) | 5, // C L R Ls Rs
kAudioChannelLayoutTag_MPEG_5_1_A = (121L<<16) | 6, // L R C LFE Ls Rs
kAudioChannelLayoutTag_MPEG_5_1_B = (122L<<16) | 6, // L R Ls Rs C LFE
kAudioChannelLayoutTag_MPEG_5_1_C = (123L<<16) | 6, // L C R Ls Rs LFE
kAudioChannelLayoutTag_MPEG_5_1_D = (124L<<16) | 6, // C L R Ls Rs LFE
kAudioChannelLayoutTag_MPEG_6_1_A = (125L<<16) | 7, // L R C LFE Ls Rs Cs
kAudioChannelLayoutTag_MPEG_7_1_A = (126L<<16) | 8, // L R C LFE Ls Rs Lc Rc
kAudioChannelLayoutTag_MPEG_7_1_B = (127L<<16) | 8, // C Lc Rc L R Ls Rs LFE (doc: IS-13818-7 MPEG2-AAC Table 3.1)
kAudioChannelLayoutTag_MPEG_7_1_C = (128L<<16) | 8, // L R C LFE Ls Rs Rls Rrs
kAudioChannelLayoutTag_Emagic_Default_7_1 = (129L<<16) | 8, // L R Ls Rs C LFE Lc Rc
kAudioChannelLayoutTag_SMPTE_DTV = (130L<<16) | 8, // L R C LFE Ls Rs Lt Rt
// (kAudioChannelLayoutTag_ITU_5_1 plus a matrix encoded stereo mix)
// ITU defined layouts
kAudioChannelLayoutTag_ITU_1_0 = kAudioChannelLayoutTag_Mono, // C
kAudioChannelLayoutTag_ITU_2_0 = kAudioChannelLayoutTag_Stereo, // L R
kAudioChannelLayoutTag_ITU_2_1 = (131L<<16) | 3, // L R Cs
kAudioChannelLayoutTag_ITU_2_2 = (132L<<16) | 4, // L R Ls Rs
kAudioChannelLayoutTag_ITU_3_0 = kAudioChannelLayoutTag_MPEG_3_0_A, // L R C
kAudioChannelLayoutTag_ITU_3_1 = kAudioChannelLayoutTag_MPEG_4_0_A, // L R C Cs
kAudioChannelLayoutTag_ITU_3_2 = kAudioChannelLayoutTag_MPEG_5_0_A, // L R C Ls Rs
kAudioChannelLayoutTag_ITU_3_2_1 = kAudioChannelLayoutTag_MPEG_5_1_A, // L R C LFE Ls Rs
kAudioChannelLayoutTag_ITU_3_4_1 = kAudioChannelLayoutTag_MPEG_7_1_C, // L R C LFE Ls Rs Rls Rrs
// DVD defined layouts
kAudioChannelLayoutTag_DVD_0 = kAudioChannelLayoutTag_Mono, // C (mono)
kAudioChannelLayoutTag_DVD_1 = kAudioChannelLayoutTag_Stereo, // L R
kAudioChannelLayoutTag_DVD_2 = kAudioChannelLayoutTag_ITU_2_1, // L R Cs
kAudioChannelLayoutTag_DVD_3 = kAudioChannelLayoutTag_ITU_2_2, // L R Ls Rs
kAudioChannelLayoutTag_DVD_4 = (133L<<16) | 3, // L R LFE
kAudioChannelLayoutTag_DVD_5 = (134L<<16) | 4, // L R LFE Cs
kAudioChannelLayoutTag_DVD_6 = (135L<<16) | 5, // L R LFE Ls Rs
kAudioChannelLayoutTag_DVD_7 = kAudioChannelLayoutTag_MPEG_3_0_A, // L R C
kAudioChannelLayoutTag_DVD_8 = kAudioChannelLayoutTag_MPEG_4_0_A, // L R C Cs
kAudioChannelLayoutTag_DVD_9 = kAudioChannelLayoutTag_MPEG_5_0_A, // L R C Ls Rs
kAudioChannelLayoutTag_DVD_10 = (136L<<16) | 4, // L R C LFE
kAudioChannelLayoutTag_DVD_11 = (137L<<16) | 5, // L R C LFE Cs
kAudioChannelLayoutTag_DVD_12 = kAudioChannelLayoutTag_MPEG_5_1_A, // L R C LFE Ls Rs
// 13 through 17 are duplicates of 8 through 12.
kAudioChannelLayoutTag_DVD_13 = kAudioChannelLayoutTag_DVD_8, // L R C Cs
kAudioChannelLayoutTag_DVD_14 = kAudioChannelLayoutTag_DVD_9, // L R C Ls Rs
kAudioChannelLayoutTag_DVD_15 = kAudioChannelLayoutTag_DVD_10, // L R C LFE
kAudioChannelLayoutTag_DVD_16 = kAudioChannelLayoutTag_DVD_11, // L R C LFE Cs
kAudioChannelLayoutTag_DVD_17 = kAudioChannelLayoutTag_DVD_12, // L R C LFE Ls Rs
kAudioChannelLayoutTag_DVD_18 = (138L<<16) | 5, // L R Ls Rs LFE
kAudioChannelLayoutTag_DVD_19 = kAudioChannelLayoutTag_MPEG_5_0_B, // L R Ls Rs C
kAudioChannelLayoutTag_DVD_20 = kAudioChannelLayoutTag_MPEG_5_1_B, // L R Ls Rs C LFE
// These layouts are recommended for AudioUnit usage
// These are the symmetrical layouts
kAudioChannelLayoutTag_AudioUnit_4 = kAudioChannelLayoutTag_Quadraphonic,
kAudioChannelLayoutTag_AudioUnit_5 = kAudioChannelLayoutTag_Pentagonal,
kAudioChannelLayoutTag_AudioUnit_6 = kAudioChannelLayoutTag_Hexagonal,
kAudioChannelLayoutTag_AudioUnit_8 = kAudioChannelLayoutTag_Octagonal,
// These are the surround-based layouts
kAudioChannelLayoutTag_AudioUnit_5_0 = kAudioChannelLayoutTag_MPEG_5_0_B, // L R Ls Rs C
kAudioChannelLayoutTag_AudioUnit_6_0 = (139L<<16) | 6, // L R Ls Rs C Cs
kAudioChannelLayoutTag_AudioUnit_7_0 = (140L<<16) | 7, // L R Ls Rs C Rls Rrs
kAudioChannelLayoutTag_AudioUnit_7_0_Front = (148L<<16) | 7, // L R Ls Rs C Lc Rc
kAudioChannelLayoutTag_AudioUnit_5_1 = kAudioChannelLayoutTag_MPEG_5_1_A, // L R C LFE Ls Rs
kAudioChannelLayoutTag_AudioUnit_6_1 = kAudioChannelLayoutTag_MPEG_6_1_A, // L R C LFE Ls Rs Cs
kAudioChannelLayoutTag_AudioUnit_7_1 = kAudioChannelLayoutTag_MPEG_7_1_C, // L R C LFE Ls Rs Rls Rrs
kAudioChannelLayoutTag_AudioUnit_7_1_Front = kAudioChannelLayoutTag_MPEG_7_1_A, // L R C LFE Ls Rs Lc Rc
kAudioChannelLayoutTag_AAC_3_0 = kAudioChannelLayoutTag_MPEG_3_0_B, // C L R
kAudioChannelLayoutTag_AAC_Quadraphonic = kAudioChannelLayoutTag_Quadraphonic, // L R Ls Rs
kAudioChannelLayoutTag_AAC_4_0 = kAudioChannelLayoutTag_MPEG_4_0_B, // C L R Cs
kAudioChannelLayoutTag_AAC_5_0 = kAudioChannelLayoutTag_MPEG_5_0_D, // C L R Ls Rs
kAudioChannelLayoutTag_AAC_5_1 = kAudioChannelLayoutTag_MPEG_5_1_D, // C L R Ls Rs Lfe
kAudioChannelLayoutTag_AAC_6_0 = (141L<<16) | 6, // C L R Ls Rs Cs
kAudioChannelLayoutTag_AAC_6_1 = (142L<<16) | 7, // C L R Ls Rs Cs Lfe
kAudioChannelLayoutTag_AAC_7_0 = (143L<<16) | 7, // C L R Ls Rs Rls Rrs
kAudioChannelLayoutTag_AAC_7_1 = kAudioChannelLayoutTag_MPEG_7_1_B, // C Lc Rc L R Ls Rs Lfe
kAudioChannelLayoutTag_AAC_Octagonal = (144L<<16) | 8, // C L R Ls Rs Rls Rrs Cs
kAudioChannelLayoutTag_TMH_10_2_std = (145L<<16) | 16, // L R C Vhc Lsd Rsd Ls Rs Vhl Vhr Lw Rw Csd Cs LFE1 LFE2
kAudioChannelLayoutTag_TMH_10_2_full = (146L<<16) | 21, // TMH_10_2_std plus: Lc Rc HI VI Haptic
kAudioChannelLayoutTag_AC3_1_0_1 = (149L<<16) | 2, // C LFE
kAudioChannelLayoutTag_AC3_3_0 = (150L<<16) | 3, // L C R
kAudioChannelLayoutTag_AC3_3_1 = (151L<<16) | 4, // L C R Cs
kAudioChannelLayoutTag_AC3_3_0_1 = (152L<<16) | 4, // L C R LFE
kAudioChannelLayoutTag_AC3_2_1_1 = (153L<<16) | 4, // L R Cs LFE
kAudioChannelLayoutTag_AC3_3_1_1 = (154L<<16) | 5, // L C R Cs LFE
kAudioChannelLayoutTag_DiscreteInOrder = (147L<<16) | 0, // needs to be ORed with the actual number of channels
kAudioChannelLayoutTag_Unknown = 0xFFFF0000 // needs to be ORed with the actual number of channels
};
#endif

208
src/extrapolater.c Normal file
View File

@ -0,0 +1,208 @@
/*
* 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 <string.h>
#include <assert.h>
#include "pcm_reader.h"
#include "lpc.h"
typedef int16_t sample_t;
typedef struct buffer_t {
sample_t *data;
unsigned count; /* count in frames */
unsigned capacity; /* size in bytes */
} buffer_t;
typedef struct extrapolater_t {
pcm_reader_vtbl_t *vtbl;
pcm_reader_t *src;
pcm_sample_description_t format;
buffer_t buffer[2];
unsigned nbuffer;
int (*process)(struct extrapolater_t *, void *, unsigned);
} extrapolater_t;
#define LPC_ORDER 32
static inline pcm_reader_t *get_source(pcm_reader_t *reader)
{
return ((extrapolater_t *)reader)->src;
}
static const
pcm_sample_description_t *get_format(pcm_reader_t *reader)
{
return pcm_get_format(get_source(reader));
}
static int64_t get_length(pcm_reader_t *reader)
{
return pcm_get_length(get_source(reader));
}
static int64_t get_position(pcm_reader_t *reader)
{
return pcm_get_position(get_source(reader));
}
static int realloc_buffer(buffer_t *bp, size_t size)
{
if (bp->capacity < size) {
void *p = realloc(bp->data, size);
if (!p) return -1;
bp->data = p;
bp->capacity = size;
}
return 0;
}
static void reverse_buffer(sample_t *data, unsigned nframes, unsigned nchannels)
{
unsigned i = 0, j = nchannels * (nframes - 1), n;
for (; i < j; i += nchannels, j -= nchannels) {
for (n = 0; n < nchannels; ++n) {
sample_t tmp = data[i + n];
data[i + n] = data[j + n];
data[j + n] = tmp;
}
}
}
static int fetch(extrapolater_t *self, unsigned nframes)
{
const pcm_sample_description_t *sfmt = pcm_get_format(self->src);
buffer_t *bp = &self->buffer[self->nbuffer];
int rc = 0;
if (realloc_buffer(bp, nframes * sfmt->bytes_per_frame) == 0) {
rc = pcm_read_frames(self->src, bp->data, nframes);
bp->count = rc > 0 ? rc : 0;
}
if (rc > 0)
self->nbuffer ^= 1;
return bp->count;
}
static int extrapolate(extrapolater_t *self, const buffer_t *bp,
void *dst, unsigned nframes)
{
const pcm_sample_description_t *sfmt = pcm_get_format(self->src);
unsigned i, n = sfmt->channels_per_frame;
float lpc[LPC_ORDER];
for (i = 0; i < n; ++i) {
vorbis_lpc_from_data(bp->data + i, lpc, bp->count, LPC_ORDER, n);
vorbis_lpc_predict(lpc, &bp->data[i + n * (bp->count - LPC_ORDER)],
LPC_ORDER, (sample_t*)dst + i, nframes, n);
}
return nframes;
}
static int process1(extrapolater_t *self, void *buffer, unsigned nframes);
static int process2(extrapolater_t *self, void *buffer, unsigned nframes);
static int process3(extrapolater_t *self, void *buffer, unsigned nframes);
static int process0(extrapolater_t *self, void *buffer, unsigned nframes)
{
const pcm_sample_description_t *sfmt = pcm_get_format(self->src);
unsigned nchannels = sfmt->channels_per_frame;
buffer_t *bp = &self->buffer[self->nbuffer];
if (fetch(self, nframes) < 2 * LPC_ORDER)
memset(buffer, 0, nframes * sfmt->bytes_per_frame);
else {
reverse_buffer(bp->data, bp->count, nchannels);
extrapolate(self, bp, buffer, nframes);
reverse_buffer(buffer, nframes, nchannels);
reverse_buffer(bp->data, bp->count, nchannels);
}
self->process = bp->count ? process1 : process2;
return nframes;
}
static int process1(extrapolater_t *self, void *buffer, unsigned nframes)
{
const pcm_sample_description_t *sfmt = pcm_get_format(self->src);
buffer_t *bp = &self->buffer[self->nbuffer ^ 1];
assert(bp->count <= nframes);
memcpy(buffer, bp->data, bp->count * sfmt->bytes_per_frame);
if (!fetch(self, nframes))
self->process = process2;
return bp->count;
}
static int process2(extrapolater_t *self, void *buffer, unsigned nframes)
{
const pcm_sample_description_t *sfmt = pcm_get_format(self->src);
buffer_t *bp = &self->buffer[self->nbuffer];
buffer_t *bbp = &self->buffer[self->nbuffer ^ 1];
if (bp->count < 2 * LPC_ORDER) {
size_t total = bp->count + bbp->count;
if (bbp->count &&
realloc_buffer(bbp, total * sfmt->bytes_per_frame) == 0)
{
memcpy(bbp->data + bbp->count * sfmt->channels_per_frame,
bp->data, bp->count * sfmt->bytes_per_frame);
bbp->count = total;
bp->count = 0;
bp = bbp;
self->nbuffer ^= 1;
}
}
self->process = process3;
if (bp->count >= 2 * LPC_ORDER)
extrapolate(self, bp, buffer, nframes);
else
memset(buffer, 0, nframes * sfmt->bytes_per_frame);
return nframes;
}
static int process3(extrapolater_t *self, void *buffer, unsigned nframes)
{
return 0;
}
static int read_frames(pcm_reader_t *reader, void *buffer, unsigned nframes)
{
extrapolater_t *self = (extrapolater_t *)reader;
return self->process(self, buffer, nframes);
}
static void teardown(pcm_reader_t **reader)
{
extrapolater_t *self = (extrapolater_t *)*reader;
pcm_teardown(&self->src);
free(self->buffer[0].data);
free(self->buffer[1].data);
free(self);
*reader = 0;
}
static pcm_reader_vtbl_t my_vtable = {
get_format, get_length, get_position, read_frames, teardown
};
pcm_reader_t *extrapolater_open(pcm_reader_t *reader)
{
extrapolater_t *self = 0;
if ((self = calloc(1, sizeof(extrapolater_t))) == 0)
return 0;
self->src = reader;
self->vtbl = &my_vtable;
self->process = process0;
return (pcm_reader_t *)self;
}

169
src/lpc.c Normal file
View File

@ -0,0 +1,169 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 *
* by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************
function: LPC low level routines
last mod: $Id: lpc.c 16227 2009-07-08 06:58:46Z xiphmont $
********************************************************************/
/* Some of these routines (autocorrelator, LPC coefficient estimator)
are derived from code written by Jutta Degener and Carsten Bormann;
thus we include their copyright below. The entirety of this file
is freely redistributable on the condition that both of these
copyright notices are preserved without modification. */
/* Preserved Copyright: *********************************************/
/* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
Technische Universita"t Berlin
Any use of this software is permitted provided that this notice is not
removed and that neither the authors nor the Technische Universita"t
Berlin are deemed to have made any representations as to the
suitability of this software for any purpose nor are held responsible
for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR
THIS SOFTWARE.
As a matter of courtesy, the authors request to be informed about uses
this software has found, about bugs in this software, and about any
improvements that may be of general interest.
Berlin, 28.11.1994
Jutta Degener
Carsten Bormann
*********************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if HAVE_STDINT_H
# include <stdint.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "lpc.h"
#include "lpcm.h"
/* Autocorrelation LPC coeff generation algorithm invented by
N. Levinson in 1947, modified by J. Durbin in 1959. */
/* Input : n elements of time doamin data
Output: m lpc coefficients, excitation energy */
float vorbis_lpc_from_data(short *data,float *lpci,int n,int m,int stride){
double *aut=malloc(sizeof(*aut)*(m+1));
double *lpc=malloc(sizeof(*lpc)*(m));
double error;
double epsilon;
int i,j;
/* autocorrelation, p+1 lag coefficients */
j=m+1;
while(j--){
double d=0; /* double needed for accumulator depth */
for(i=j;i<n;i++)d+=(double)data[i*stride]*data[(i-j)*stride]/1073741824.0;
aut[j]=d;
}
/* Generate lpc coefficients from autocorr values */
/* set our noise floor to about -100dB */
error=aut[0] * (1. + 1e-10);
epsilon=1e-9*aut[0]+1e-10;
for(i=0;i<m;i++){
double r= -aut[i+1];
if(error<epsilon){
memset(lpc+i,0,(m-i)*sizeof(*lpc));
goto done;
}
/* Sum up this iteration's reflection coefficient; note that in
Vorbis we don't save it. If anyone wants to recycle this code
and needs reflection coefficients, save the results of 'r' from
each iteration. */
for(j=0;j<i;j++)r-=lpc[j]*aut[i-j];
r/=error;
/* Update LPC coefficients and total error */
lpc[i]=r;
for(j=0;j<i/2;j++){
double tmp=lpc[j];
lpc[j]+=r*lpc[i-1-j];
lpc[i-1-j]+=r*tmp;
}
if(i&1)lpc[j]+=lpc[j]*r;
error*=1.-r*r;
}
done:
/* slightly damp the filter */
{
double g = .99;
double damp = g;
for(j=0;j<m;j++){
lpc[j]*=damp;
damp*=g;
}
}
for(j=0;j<m;j++)lpci[j]=(float)lpc[j];
/* we need the error value to know how big an impulse to hit the
filter with later */
free(aut);
free(lpc);
return error;
}
void vorbis_lpc_predict(float *coeff,short *prime,int m,
short *data,long n,int stride){
/* in: coeff[0...m-1] LPC coefficients
prime[0...m-1] initial values (allocated size of n+m-1)
out: data[0...n-1] data samples */
long i,j,o,p;
float y;
float *work=malloc(sizeof(*work)*(m+n));
if(!prime)
for(i=0;i<m;i++)
work[i]=0.f;
else
for(i=0;i<m;i++)
work[i]=prime[i*stride]/32768.0f;
for(i=0;i<n;i++){
y=0;
o=i;
p=m;
for(j=0;j<m;j++)
y-=work[o++]*coeff[--p];
work[o]=y;
data[i*stride]=lrint(pcm_clip(y*32768.0,-32768.0,32767.0));
}
free(work);
}

27
src/lpc.h Normal file
View File

@ -0,0 +1,27 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 *
* by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************
function: LPC low level routines
last mod: $Id: lpc.h 16037 2009-05-26 21:10:58Z xiphmont $
********************************************************************/
#ifndef _V_LPC_H_
#define _V_LPC_H_
/* simple linear scale LPC code */
extern float vorbis_lpc_from_data(short *data,float *lpc,int n,int m,int stride);
extern void vorbis_lpc_predict(float *coeff,short *prime,int m,
short *data,long n,int stride);
#endif

View File

@ -13,36 +13,6 @@
#include "lpcm.h" #include "lpcm.h"
#include "m4af_endian.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
static
inline double pcm_clip(double n, double min_value, double max_value)
{
if (n < min_value)
return min_value;
else if (n > max_value)
return max_value;
return n;
}
static static
inline float pcm_i2f(int32_t n) inline float pcm_i2f(int32_t n)
{ {
@ -187,14 +157,14 @@ inline int16_t pcm_f64be_to_s16(int64_t n)
int pcm_convert_to_native_sint16(const pcm_sample_description_t *format, int pcm_convert_to_native_sint16(const pcm_sample_description_t *format,
const void *input, uint32_t nframes, const void *input, uint32_t nframes,
int16_t **result, uint32_t *osize) int16_t *result)
{ {
#define CONVERT(type, conv) \ #define CONVERT(type, conv) \
do { \ do { \
unsigned i; \ unsigned i; \
type *ip = (type *)input; \ type *ip = (type *)input; \
for (i = 0; i < count; ++i) { \ for (i = 0; i < count; ++i) { \
(*result)[i] = conv(ip[i]); \ result[i] = conv(ip[i]); \
} \ } \
} while(0) } while(0)
@ -204,7 +174,7 @@ int pcm_convert_to_native_sint16(const pcm_sample_description_t *format,
uint8_t *ip = (uint8_t *)input; \ uint8_t *ip = (uint8_t *)input; \
bytes_per_channel = PCM_BYTES_PER_CHANNEL(format); \ bytes_per_channel = PCM_BYTES_PER_CHANNEL(format); \
for (i = 0; i < count; ++i) { \ for (i = 0; i < count; ++i) { \
(*result)[i] = conv(ip); \ result[i] = conv(ip); \
ip += bytes_per_channel; \ ip += bytes_per_channel; \
} \ } \
} while(0) } while(0)
@ -212,11 +182,6 @@ int pcm_convert_to_native_sint16(const pcm_sample_description_t *format,
uint32_t count = nframes * format->channels_per_frame; uint32_t count = nframes * format->channels_per_frame;
if (!count) if (!count)
return 0; 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) { switch (PCM_BYTES_PER_CHANNEL(format) | format->sample_type<<4) {
case 1 | PCM_TYPE_SINT<<4: case 1 | PCM_TYPE_SINT<<4:
CONVERT(int8_t, pcm_s8_to_s16); break; CONVERT(int8_t, pcm_s8_to_s16); break;

View File

@ -31,7 +31,37 @@ typedef struct pcm_sample_description_t {
#define PCM_BYTES_PER_CHANNEL(desc) \ #define PCM_BYTES_PER_CHANNEL(desc) \
((desc)->bytes_per_frame / (desc)->channels_per_frame) ((desc)->bytes_per_frame / (desc)->channels_per_frame)
#if defined(_MSC_VER) && _MSC_VER < 1800
# ifdef _M_IX86
static inline int lrint(double x)
{
int n;
_asm {
fld x
fistp n
}
return n;
}
# else
# include <emmintrin.h>
static inline int lrint(double x)
{
return _mm_cvtsd_si32(_mm_load_sd(&x));
}
# endif
#endif
static
inline double pcm_clip(double n, double min_value, double max_value)
{
if (n < min_value)
return min_value;
else if (n > max_value)
return max_value;
return n;
}
int pcm_convert_to_native_sint16(const pcm_sample_description_t *format, int pcm_convert_to_native_sint16(const pcm_sample_description_t *format,
const void *input, uint32_t nframes, const void *input, uint32_t nframes,
int16_t **result, uint32_t *osize); int16_t *result);
#endif #endif

View File

@ -53,6 +53,7 @@ typedef struct m4af_track_t {
uint32_t bufferSizeDB; uint32_t bufferSizeDB;
uint32_t maxBitrate; uint32_t maxBitrate;
uint32_t avgBitrate; uint32_t avgBitrate;
int is_vbr;
m4af_sample_entry_t *sample_table; m4af_sample_entry_t *sample_table;
uint32_t num_samples; uint32_t num_samples;
@ -278,6 +279,12 @@ DONE:
return ctx->last_error; return ctx->last_error;
} }
void m4af_set_vbr_mode(m4af_ctx_t *ctx, uint32_t track_idx, int is_vbr)
{
m4af_track_t *track = &ctx->track[track_idx];
track->is_vbr = is_vbr;
}
void m4af_set_priming(m4af_ctx_t *ctx, uint32_t track_idx, void m4af_set_priming(m4af_ctx_t *ctx, uint32_t track_idx,
uint32_t encoder_delay, uint32_t padding) uint32_t encoder_delay, uint32_t padding)
{ {
@ -771,7 +778,7 @@ void m4af_write_esds_box(m4af_ctx_t *ctx, uint32_t track_idx)
, 2); , 2);
m4af_write24(ctx, track->bufferSizeDB); m4af_write24(ctx, track->bufferSizeDB);
m4af_write32(ctx, track->maxBitrate); m4af_write32(ctx, track->maxBitrate);
m4af_write32(ctx, track->avgBitrate); m4af_write32(ctx, track->is_vbr ? 0: track->avgBitrate);
/* DecoderSpecificInfo */ /* DecoderSpecificInfo */
m4af_write_descriptor(ctx, 5, track->decSpecificInfoSize); m4af_write_descriptor(ctx, 5, track->decSpecificInfoSize);
m4af_write(ctx, track->decSpecificInfo, track->decSpecificInfoSize); m4af_write(ctx, track->decSpecificInfo, track->decSpecificInfoSize);
@ -857,17 +864,15 @@ void m4af_write_sbgp_box(m4af_ctx_t *ctx, uint32_t track_idx)
static static
void m4af_write_sgpd_box(m4af_ctx_t *ctx, uint32_t track_idx) void m4af_write_sgpd_box(m4af_ctx_t *ctx, uint32_t track_idx)
{ {
m4af_track_t *track = &ctx->track[track_idx];
m4af_write(ctx, m4af_write(ctx,
"\0\0\0\032" /* size */ "\0\0\0\026" /* size: 22 */
"sgpd" /* type */ "sgpd" /* type */
"\001" /* version */ "\0" /* version */
"\0\0\0" /* flags */ "\0\0\0" /* flags */
"roll" /* grouping_type */ "roll" /* grouping_type */
"\0\0\0\002" /* default_length: 2 */
"\0\0\0\001" /* entry_count: 1 */ "\0\0\0\001" /* entry_count: 1 */
"\377\377" /* payload_data: -1 */ "\377\377" /* payload_data: -1 */
, 26); , 22);
} }
static static
@ -1044,7 +1049,6 @@ void m4af_write_elst_box(m4af_ctx_t *ctx, uint32_t track_idx)
static static
void m4af_write_edts_box(m4af_ctx_t *ctx, uint32_t track_idx) void m4af_write_edts_box(m4af_ctx_t *ctx, uint32_t track_idx)
{ {
m4af_track_t *track = &ctx->track[track_idx];
int64_t pos = m4af_tell(ctx); int64_t pos = m4af_tell(ctx);
m4af_write(ctx, "\0\0\0\0edts", 8); m4af_write(ctx, "\0\0\0\0edts", 8);
m4af_write_elst_box(ctx, track_idx); m4af_write_elst_box(ctx, track_idx);

View File

@ -97,6 +97,8 @@ int m4af_write_sample(m4af_ctx_t *ctx, uint32_t track_idx, const void *data,
int m4af_set_decoder_specific_info(m4af_ctx_t *ctx, uint32_t track_idx, int m4af_set_decoder_specific_info(m4af_ctx_t *ctx, uint32_t track_idx,
uint8_t *data, uint32_t size); uint8_t *data, uint32_t size);
void m4af_set_vbr_mode(m4af_ctx_t *ctx, uint32_t track_idx, int is_vbr);
void m4af_set_priming(m4af_ctx_t *ctx, uint32_t track_idx, void m4af_set_priming(m4af_ctx_t *ctx, uint32_t track_idx,
uint32_t encoder_delay, uint32_t padding); uint32_t encoder_delay, uint32_t padding);

View File

@ -34,6 +34,7 @@
#endif #endif
#include "compat.h" #include "compat.h"
#include "wav_reader.h" #include "wav_reader.h"
#include "caf_reader.h"
#include "aacenc.h" #include "aacenc.h"
#include "m4af.h" #include "m4af.h"
#include "progress.h" #include "progress.h"
@ -53,7 +54,8 @@ static void handle_signals(void)
{ {
int i, sigs[] = { SIGINT, SIGHUP, SIGTERM }; int i, sigs[] = { SIGINT, SIGHUP, SIGTERM };
for (i = 0; i < sizeof(sigs)/sizeof(sigs[0]); ++i) { for (i = 0; i < sizeof(sigs)/sizeof(sigs[0]); ++i) {
struct sigaction sa = { 0 }; struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = signal_handler; sa.sa_handler = signal_handler;
sa.sa_flags |= SA_RESTART; sa.sa_flags |= SA_RESTART;
sigaction(sigs[i], &sa, 0); sigaction(sigs[i], &sa, 0);
@ -131,11 +133,14 @@ PROGNAME " %s\n"
" -a, --afterburner <n> Afterburner\n" " -a, --afterburner <n> Afterburner\n"
" 0: Off\n" " 0: Off\n"
" 1: On(default)\n" " 1: On(default)\n"
" -L, --lowdelay-sbr Enable ELD-SBR (AAC ELD only)\n" " -L, --lowdelay-sbr <-1|0|1> Configure SBR activity on AAC ELD\n"
" -s, --sbr-signaling <n> SBR signaling mode\n" " -1: Use ELD SBR auto configurator\n"
" 0: Implicit, backward compatible(default)\n" " 0: Disable SBR on ELD (default)\n"
" 1: Explicit SBR and implicit PS\n" " 1: Enable SBR on ELD\n"
" 2: Explicit hierarchical signaling\n" " -s, --sbr-ratio <0|1|2> Controls activation of downsampled SBR\n"
" 0: Use lib default (default)\n"
" 1: downsampled SBR (default for ELD+SBR)\n"
" 2: dual-rate SBR (default for HE-AAC)\n"
" -f, --transport-format <n> Transport format\n" " -f, --transport-format <n> Transport format\n"
" 0: RAW (default, muxed into M4A)\n" " 0: RAW (default, muxed into M4A)\n"
" 1: ADIF\n" " 1: ADIF\n"
@ -152,7 +157,10 @@ PROGNAME " %s\n"
" 0: iTunSMPB (default)\n" " 0: iTunSMPB (default)\n"
" 1: ISO standard (edts + sgpd)\n" " 1: ISO standard (edts + sgpd)\n"
" 2: Both\n" " 2: Both\n"
" --ignorelength Ignore length of WAV header\n" " --include-sbr-delay Count SBR decoder delay in encoder delay\n"
" This is not iTunes compatible, but is default\n"
" behavior of FDK library.\n"
" -I, --ignorelength Ignore length of WAV header\n"
" -S, --silent Don't print progress messages\n" " -S, --silent Don't print progress messages\n"
" --moov-before-mdat Place moov box before mdat box on m4a output\n" " --moov-before-mdat Place moov box before mdat box on m4a output\n"
"\n" "\n"
@ -203,8 +211,11 @@ typedef struct aacenc_param_ex_t {
AACENC_PARAMS AACENC_PARAMS
char *input_filename; char *input_filename;
FILE *input_fp;
char *output_filename; char *output_filename;
FILE *output_fp;
unsigned gapless_mode; unsigned gapless_mode;
unsigned include_sbr_delay;
unsigned ignore_length; unsigned ignore_length;
int silent; int silent;
int moov_before_mdat; int moov_before_mdat;
@ -214,7 +225,9 @@ typedef struct aacenc_param_ex_t {
unsigned raw_rate; unsigned raw_rate;
const char *raw_format; const char *raw_format;
aacenc_tag_param_t tags; aacenc_tag_store_t tags;
aacenc_tag_store_t source_tags;
aacenc_translate_generic_text_tag_ctx_t source_tag_ctx;
char *json_filename; char *json_filename;
} aacenc_param_ex_t; } aacenc_param_ex_t;
@ -223,8 +236,9 @@ static
int parse_options(int argc, char **argv, aacenc_param_ex_t *params) int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
{ {
int ch; int ch;
unsigned n; int n;
#define OPT_INCLUDE_SBR_DELAY M4AF_FOURCC('s','d','l','y')
#define OPT_MOOV_BEFORE_MDAT M4AF_FOURCC('m','o','o','v') #define OPT_MOOV_BEFORE_MDAT M4AF_FOURCC('m','o','o','v')
#define OPT_RAW_CHANNELS M4AF_FOURCC('r','c','h','n') #define OPT_RAW_CHANNELS M4AF_FOURCC('r','c','h','n')
#define OPT_RAW_RATE M4AF_FOURCC('r','r','a','t') #define OPT_RAW_RATE M4AF_FOURCC('r','r','a','t')
@ -241,13 +255,14 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
{ "bitrate-mode", required_argument, 0, 'm' }, { "bitrate-mode", required_argument, 0, 'm' },
{ "bandwidth", required_argument, 0, 'w' }, { "bandwidth", required_argument, 0, 'w' },
{ "afterburner", required_argument, 0, 'a' }, { "afterburner", required_argument, 0, 'a' },
{ "lowdelay-sbr", no_argument, 0, 'L' }, { "lowdelay-sbr", required_argument, 0, 'L' },
{ "sbr-signaling", required_argument, 0, 's' }, { "sbr-ratio", required_argument, 0, 's' },
{ "transport-format", required_argument, 0, 'f' }, { "transport-format", required_argument, 0, 'f' },
{ "adts-crc-check", no_argument, 0, 'C' }, { "adts-crc-check", no_argument, 0, 'C' },
{ "header-period", required_argument, 0, 'P' }, { "header-period", required_argument, 0, 'P' },
{ "gapless-mode", required_argument, 0, 'G' }, { "gapless-mode", required_argument, 0, 'G' },
{ "include-sbr-delay", no_argument, 0, OPT_INCLUDE_SBR_DELAY },
{ "ignorelength", no_argument, 0, 'I' }, { "ignorelength", no_argument, 0, 'I' },
{ "silent", no_argument, 0, 'S' }, { "silent", no_argument, 0, 'S' },
{ "moov-before-mdat", no_argument, 0, OPT_MOOV_BEFORE_MDAT }, { "moov-before-mdat", no_argument, 0, OPT_MOOV_BEFORE_MDAT },
@ -319,14 +334,18 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
params->afterburner = n; params->afterburner = n;
break; break;
case 'L': case 'L':
params->lowdelay_sbr = 1; if (sscanf(optarg, "%d", &n) != 1 || n < -1 || n > 1) {
fprintf(stderr, "invalid arg for lowdelay-sbr\n");
return -1;
}
params->lowdelay_sbr = n;
break; break;
case 's': case 's':
if (sscanf(optarg, "%u", &n) != 1 || n > 2) { if (sscanf(optarg, "%u", &n) != 1 || n > 2) {
fprintf(stderr, "invalid arg for sbr-signaling\n"); fprintf(stderr, "invalid arg for sbr-ratio\n");
return -1; return -1;
} }
params->sbr_signaling = n; params->sbr_ratio = n;
break; break;
case 'f': case 'f':
if (sscanf(optarg, "%u", &n) != 1) { if (sscanf(optarg, "%u", &n) != 1) {
@ -355,6 +374,9 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
} }
params->gapless_mode = n; params->gapless_mode = n;
break; break;
case OPT_INCLUDE_SBR_DELAY:
params->include_sbr_delay = 1;
break;
case 'I': case 'I':
params->ignore_length = 1; params->ignore_length = 1;
break; break;
@ -396,7 +418,7 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
case M4AF_TAG_TRACK: case M4AF_TAG_TRACK:
case M4AF_TAG_DISK: case M4AF_TAG_DISK:
case M4AF_TAG_TEMPO: case M4AF_TAG_TEMPO:
aacenc_param_add_itmf_entry(&params->tags, ch, 0, optarg, aacenc_add_tag_to_store(&params->tags, ch, 0, optarg,
strlen(optarg), 0); strlen(optarg), 0);
break; break;
case OPT_SHORT_TAG: case OPT_SHORT_TAG:
@ -430,7 +452,7 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
for (; *optarg; ++optarg) for (; *optarg; ++optarg)
fcc = ((fcc << 8) | (*optarg & 0xff)); fcc = ((fcc << 8) | (*optarg & 0xff));
} }
aacenc_param_add_itmf_entry(&params->tags, fcc, optarg, aacenc_add_tag_to_store(&params->tags, fcc, optarg,
val, strlen(val), val, strlen(val),
ch == OPT_SHORT_TAG_FILE); ch == OPT_SHORT_TAG_FILE);
} }
@ -470,16 +492,15 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
}; };
static static
int write_sample(FILE *ofp, m4af_ctx_t *m4af, int write_sample(FILE *ofp, m4af_ctx_t *m4af, aacenc_frame_t *frame)
const void *data, uint32_t size, uint32_t duration)
{ {
if (!m4af) { if (!m4af) {
fwrite(data, 1, size, ofp); fwrite(frame->data, 1, frame->size, ofp);
if (ferror(ofp)) { if (ferror(ofp)) {
fprintf(stderr, "ERROR: fwrite(): %s\n", strerror(errno)); fprintf(stderr, "ERROR: fwrite(): %s\n", strerror(errno));
return -1; return -1;
} }
} else if (m4af_write_sample(m4af, 0, data, size, duration) < 0) { } else if (m4af_write_sample(m4af, 0, frame->data, frame->size, 0) < 0) {
fprintf(stderr, "ERROR: failed to write m4a sample\n"); fprintf(stderr, "ERROR: failed to write m4a sample\n");
return -1; return -1;
} }
@ -487,60 +508,76 @@ int write_sample(FILE *ofp, m4af_ctx_t *m4af,
} }
static static
int encode(wav_reader_t *wavf, HANDLE_AACENCODER encoder, int encode(aacenc_param_ex_t *params, pcm_reader_t *reader,
uint32_t frame_length, FILE *ofp, m4af_ctx_t *m4af, HANDLE_AACENCODER encoder, uint32_t frame_length,
int show_progress) m4af_ctx_t *m4af)
{ {
uint8_t *ibuf = 0; int16_t *ibuf = 0, *ip;
int16_t *pcmbuf = 0; aacenc_frame_t obuf[2] = {{ 0 }}, *obp;
uint32_t pcmsize = 0; unsigned flip = 0;
uint8_t *obuf = 0;
uint32_t olen;
uint32_t osize = 0;
int nread = 1; int nread = 1;
int consumed;
int rc = -1; int rc = -1;
int frames_written = 0; int remaining, consumed;
int frames_written = 0, encoded = 0;
aacenc_progress_t progress = { 0 }; aacenc_progress_t progress = { 0 };
const pcm_sample_description_t *format = wav_get_format(wavf); const pcm_sample_description_t *fmt = pcm_get_format(reader);
ibuf = malloc(frame_length * format->bytes_per_frame); ibuf = malloc(frame_length * fmt->bytes_per_frame);
aacenc_progress_init(&progress, wav_get_length(wavf), format->sample_rate); aacenc_progress_init(&progress, pcm_get_length(reader), fmt->sample_rate);
do {
for (;;) {
/*
* Since we delay the write, we cannot just exit loop when interrupted.
* Instead, we regard it as EOF.
*/
if (g_interrupted) if (g_interrupted)
nread = 0; nread = 0;
else if (nread) { if (nread > 0) {
if ((nread = wav_read_frames(wavf, ibuf, frame_length)) < 0) { if ((nread = pcm_read_frames(reader, ibuf, frame_length)) < 0) {
fprintf(stderr, "ERROR: read failed\n"); fprintf(stderr, "ERROR: read failed\n");
goto END; 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;
} }
if (!params->silent)
aacenc_progress_update(&progress, pcm_get_position(reader),
fmt->sample_rate * 2);
} }
if (show_progress) ip = ibuf;
aacenc_progress_update(&progress, wav_get_position(wavf), remaining = nread;
format->sample_rate * 2); do {
} obp = &obuf[flip];
if ((consumed = aac_encode_frame(encoder, format, pcmbuf, nread, consumed = aac_encode_frame(encoder, fmt, ip, remaining, obp);
&obuf, &olen, &osize)) < 0) if (consumed < 0) goto END;
goto END; if (consumed == 0 && obp->size == 0) goto DONE;
if (olen > 0) { if (obp->size == 0) break;
if (write_sample(ofp, m4af, obuf, olen, frame_length) < 0)
remaining -= consumed;
ip += consumed * fmt->channels_per_frame;
flip ^= 1;
/*
* As we pad 1 frame at beginning and ending by our extrapolator,
* we want to drop them.
* We delay output by 1 frame by double buffering, and discard
* second frame and final frame from the encoder.
* Since sbr_header is included in the first frame (in case of
* SBR), we cannot discard first frame. So we pick second instead.
*/
++encoded;
if (encoded == 1 || encoded == 3)
continue;
obp = &obuf[flip];
if (write_sample(params->output_fp, m4af, obp) < 0)
goto END; goto END;
++frames_written; ++frames_written;
} while (remaining > 0);
} }
} while (nread > 0 || olen > 0); DONE:
if (!params->silent)
if (show_progress) aacenc_progress_finish(&progress, pcm_get_position(reader));
aacenc_progress_finish(&progress, wav_get_position(wavf));
rc = frames_written; rc = frames_written;
END: END:
if (ibuf) free(ibuf); if (ibuf) free(ibuf);
if (pcmbuf) free(pcmbuf); if (obuf[0].data) free(obuf[0].data);
if (obuf) free(obuf); if (obuf[1].data) free(obuf[1].data);
return rc; return rc;
} }
@ -550,19 +587,11 @@ void put_tool_tag(m4af_ctx_t *m4af, const aacenc_param_ex_t *params,
{ {
char tool_info[256]; char tool_info[256];
char *p = tool_info; char *p = tool_info;
LIB_INFO *lib_info = 0; LIB_INFO lib_info;
p += sprintf(p, PROGNAME " %s, ", fdkaac_version); p += sprintf(p, PROGNAME " %s, ", fdkaac_version);
aacenc_get_lib_info(&lib_info);
lib_info = calloc(FDK_MODULE_LAST, sizeof(LIB_INFO)); p += sprintf(p, "libfdk-aac %s, ", lib_info.versionStr);
if (aacEncGetLibInfo(lib_info) == AACENC_OK) {
int i;
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) if (params->bitrate_mode)
sprintf(p, "VBR mode %d", params->bitrate_mode); sprintf(p, "VBR mode %d", params->bitrate_mode);
else else
@ -577,13 +606,18 @@ int finalize_m4a(m4af_ctx_t *m4af, const aacenc_param_ex_t *params,
HANDLE_AACENCODER encoder) HANDLE_AACENCODER encoder)
{ {
unsigned i; unsigned i;
aacenc_tag_entry_t *tag = params->tags.tag_table; aacenc_tag_entry_t *tag;
tag = params->source_tags.tag_table;
for (i = 0; i < params->source_tags.tag_count; ++i, ++tag)
aacenc_write_tag_entry(m4af, tag);
if (params->json_filename) if (params->json_filename)
aacenc_put_tags_from_json(m4af, params->json_filename); aacenc_write_tags_from_json(m4af, params->json_filename);
tag = params->tags.tag_table;
for (i = 0; i < params->tags.tag_count; ++i, ++tag) for (i = 0; i < params->tags.tag_count; ++i, ++tag)
aacenc_put_tag_entry(m4af, tag); aacenc_write_tag_entry(m4af, tag);
put_tool_tag(m4af, params, encoder); put_tool_tag(m4af, params, encoder);
@ -609,7 +643,7 @@ char *generate_output_filename(const char *filename, const char *ext)
const char *ext_org = strrchr(base, '.'); const char *ext_org = strrchr(base, '.');
if (ext_org) ilen = ext_org - base; if (ext_org) ilen = ext_org - base;
p = malloc(ilen + ext_len + 1); p = malloc(ilen + ext_len + 1);
sprintf(p, "%.*s%s", ilen, base, ext); sprintf(p, "%.*s%s", (int)ilen, base, ext);
} }
return p; return p;
} }
@ -650,25 +684,95 @@ int parse_raw_spec(const char *spec, pcm_sample_description_t *desc)
return 0; return 0;
} }
static pcm_io_vtbl_t pcm_io_vtbl = {
read_callback, seek_callback, tell_callback
};
static pcm_io_vtbl_t pcm_io_vtbl_noseek = { read_callback, 0, tell_callback };
static
pcm_reader_t *open_input(aacenc_param_ex_t *params)
{
pcm_io_context_t io = { 0 };
pcm_reader_t *reader = 0;
struct stat stb = { 0 };
if ((params->input_fp = aacenc_fopen(params->input_filename, "rb")) == 0) {
aacenc_fprintf(stderr, "ERROR: %s: %s\n", params->input_filename,
strerror(errno));
goto END;
}
io.cookie = params->input_fp;
if (fstat(fileno(params->input_fp), &stb) == 0
&& (stb.st_mode & S_IFMT) == S_IFREG)
io.vtbl = &pcm_io_vtbl;
else
io.vtbl = &pcm_io_vtbl_noseek;
if (params->is_raw) {
int bytes_per_channel;
pcm_sample_description_t desc = { 0 };
if (parse_raw_spec(params->raw_format, &desc) < 0) {
fprintf(stderr, "ERROR: invalid raw-format spec\n");
goto END;
}
desc.sample_rate = params->raw_rate;
desc.channels_per_frame = params->raw_channels;
bytes_per_channel = (desc.bits_per_channel + 7) / 8;
desc.bytes_per_frame = params->raw_channels * bytes_per_channel;
if ((reader = raw_open(&io, &desc)) == 0) {
fprintf(stderr, "ERROR: failed to open raw input\n");
goto END;
}
} else {
int c;
ungetc(c = getc(params->input_fp), params->input_fp);
switch (c) {
case 'R':
if ((reader = wav_open(&io, params->ignore_length)) == 0) {
fprintf(stderr, "ERROR: broken / unsupported input file\n");
goto END;
}
break;
case 'c':
params->source_tag_ctx.add = aacenc_add_tag_entry_to_store;
params->source_tag_ctx.add_ctx = &params->source_tags;
if ((reader = caf_open(&io,
aacenc_translate_generic_text_tag,
&params->source_tag_ctx)) == 0) {
fprintf(stderr, "ERROR: broken / unsupported input file\n");
goto END;
}
break;
default:
fprintf(stderr, "ERROR: unsupported input file\n");
goto END;
}
}
if ((reader = pcm_open_sint16_converter(reader)) != 0)
reader = extrapolater_open(reader);
return reader;
END:
return 0;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
wav_io_context_t wav_io = { read_callback, seek_callback, tell_callback }; static m4af_io_callbacks_t m4af_io = {
m4af_io_callbacks_t read_callback, write_callback, seek_callback, tell_callback
m4af_io = { read_callback, write_callback, seek_callback, tell_callback }; };
aacenc_param_ex_t params = { 0 }; aacenc_param_ex_t params = { 0 };
int result = 2; int result = 2;
FILE *ifp = 0;
FILE *ofp = 0;
char *output_filename = 0; char *output_filename = 0;
wav_reader_t *wavf = 0; pcm_reader_t *reader = 0;
HANDLE_AACENCODER encoder = 0; HANDLE_AACENCODER encoder = 0;
AACENC_InfoStruct aacinfo = { 0 }; AACENC_InfoStruct aacinfo = { 0 };
m4af_ctx_t *m4af = 0; m4af_ctx_t *m4af = 0;
const pcm_sample_description_t *sample_format; const pcm_sample_description_t *sample_format;
int downsampled_timescale = 0;
int frame_count = 0; int frame_count = 0;
struct stat stb = { 0 }; int sbr_mode = 0;
unsigned scale_shift = 0;
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
setbuf(stderr, 0); setbuf(stderr, 0);
@ -676,37 +780,23 @@ int main(int argc, char **argv)
if (parse_options(argc, argv, &params) < 0) if (parse_options(argc, argv, &params) < 0)
return 1; return 1;
if ((ifp = aacenc_fopen(params.input_filename, "rb")) == 0) { if ((reader = open_input(&params)) == 0)
aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.input_filename,
strerror(errno));
goto END; goto END;
sample_format = pcm_get_format(reader);
sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)&params);
if (sbr_mode && !aacenc_is_sbr_ratio_available()) {
fprintf(stderr, "WARNING: Only dual-rate SBR is available "
"for this version\n");
params.sbr_ratio = 2;
} }
if (fstat(fileno(ifp), &stb) == 0 && (stb.st_mode & S_IFMT) != S_IFREG) { scale_shift = aacenc_is_dual_rate_sbr((aacenc_param_t*)&params);
wav_io.seek = 0; params.sbr_signaling =
wav_io.tell = 0; (params.transport_format == TT_MP4_LOAS) ? 2 :
} (params.transport_format == TT_MP4_RAW) ? 1 : 0;
if (!params.is_raw) { if (sbr_mode && !scale_shift)
if ((wavf = wav_open(&wav_io, ifp, params.ignore_length)) == 0) { params.sbr_signaling = 2;
fprintf(stderr, "ERROR: broken / unsupported input file\n");
goto END;
}
} else {
int bytes_per_channel;
pcm_sample_description_t desc = { 0 };
if (parse_raw_spec(params.raw_format, &desc) < 0) {
fprintf(stderr, "ERROR: invalid raw-format spec\n");
goto END;
}
desc.sample_rate = params.raw_rate;
desc.channels_per_frame = params.raw_channels;
bytes_per_channel = (desc.bits_per_channel + 7) / 8;
desc.bytes_per_frame = params.raw_channels * bytes_per_channel;
if ((wavf = raw_open(&wav_io, ifp, &desc)) == 0) {
fprintf(stderr, "ERROR: failed to open raw input\n");
goto END;
}
}
sample_format = wav_get_format(wavf);
if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format, if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format,
&aacinfo) < 0) &aacinfo) < 0)
@ -718,52 +808,68 @@ int main(int argc, char **argv)
params.output_filename = output_filename; params.output_filename = output_filename;
} }
if ((ofp = aacenc_fopen(params.output_filename, "wb+")) == 0) { if ((params.output_fp = aacenc_fopen(params.output_filename, "wb+")) == 0) {
aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename, aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename,
strerror(errno)); strerror(errno));
goto END; goto END;
} }
handle_signals(); handle_signals();
if (!params.transport_format) { if (!params.transport_format) {
uint32_t scale; uint32_t scale;
unsigned framelen = aacinfo.frameLength; unsigned framelen = aacinfo.frameLength;
int sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)&params); scale = sample_format->sample_rate >> scale_shift;
int sig_mode = aacEncoder_GetParam(encoder, AACENC_SIGNALING_MODE); if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io,
if (sbr_mode && !sig_mode) params.output_fp)) < 0)
downsampled_timescale = 1;
scale = sample_format->sample_rate >> downsampled_timescale;
if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io, ofp)) < 0)
goto END; goto END;
m4af_set_decoder_specific_info(m4af, 0, aacinfo.confBuf, m4af_set_decoder_specific_info(m4af, 0,
aacinfo.confSize); aacinfo.confBuf, aacinfo.confSize);
m4af_set_fixed_frame_duration(m4af, 0, m4af_set_fixed_frame_duration(m4af, 0,
framelen >> downsampled_timescale); framelen >> scale_shift);
m4af_set_vbr_mode(m4af, 0, params.bitrate_mode);
m4af_set_priming_mode(m4af, params.gapless_mode + 1); m4af_set_priming_mode(m4af, params.gapless_mode + 1);
m4af_begin_write(m4af); m4af_begin_write(m4af);
} }
frame_count = encode(wavf, encoder, aacinfo.frameLength, ofp, m4af, if (scale_shift && (aacinfo.encoderDelay & 1)) {
!params.silent); /*
* Since odd delay cannot be exactly expressed in downsampled scale,
* we push one zero frame to the encoder here, to make delay even
*/
int16_t zero[8] = { 0 };
aacenc_frame_t frame = { 0 };
aac_encode_frame(encoder, sample_format, zero, 1, &frame);
free(frame.data);
}
frame_count = encode(&params, reader, encoder, aacinfo.frameLength, m4af);
if (frame_count < 0) if (frame_count < 0)
goto END; goto END;
if (m4af) { if (m4af) {
uint32_t delay = aacinfo.encoderDelay; uint32_t delay = aacinfo.encoderDelay;
int64_t frames_read = wav_get_position(wavf); uint32_t padding;
uint32_t padding = frame_count * aacinfo.frameLength int64_t frames_read = pcm_get_position(reader);
- frames_read - aacinfo.encoderDelay;
m4af_set_priming(m4af, 0, delay >> downsampled_timescale, if (sbr_mode && params.profile != AOT_ER_AAC_ELD &&
padding >> downsampled_timescale); !params.include_sbr_delay)
delay -= 481 << scale_shift;
if (scale_shift && (delay & 1))
++delay;
padding = frame_count * aacinfo.frameLength - frames_read - delay;
m4af_set_priming(m4af, 0, delay >> scale_shift, padding >> scale_shift);
if (finalize_m4a(m4af, &params, encoder) < 0) if (finalize_m4a(m4af, &params, encoder) < 0)
goto END; goto END;
} }
result = 0; result = 0;
END: END:
if (wavf) wav_teardown(&wavf); if (reader) pcm_teardown(&reader);
if (ifp) fclose(ifp); if (params.input_fp) fclose(params.input_fp);
if (m4af) m4af_teardown(&m4af); if (m4af) m4af_teardown(&m4af);
if (ofp) fclose(ofp); if (params.output_fp) fclose(params.output_fp);
if (encoder) aacEncClose(&encoder); if (encoder) aacEncClose(&encoder);
if (output_filename) free(output_filename); if (output_filename) free(output_filename);
if (params.tags.tag_table) free(params.tags.tag_table); if (params.tags.tag_table)
aacenc_free_tag_store(&params.tags);
if (params.source_tags.tag_table)
aacenc_free_tag_store(&params.source_tags);
return result; return result;
} }

View File

@ -1,3 +1,7 @@
/*
* Copyright (C) 2013 nu774
* For conditions of distribution and use, see copyright notice in COPYING
*/
#if HAVE_CONFIG_H #if HAVE_CONFIG_H
# include "config.h" # include "config.h"
#endif #endif
@ -50,12 +54,14 @@ static tag_key_mapping_t tag_mapping_table[] = {
{ "copyright", M4AF_TAG_COPYRIGHT }, { "copyright", M4AF_TAG_COPYRIGHT },
{ "date", M4AF_TAG_DATE }, { "date", M4AF_TAG_DATE },
{ "disc", M4AF_TAG_DISK }, { "disc", M4AF_TAG_DISK },
{ "disctotal", TAG_TOTAL_DISCS },
{ "discnumber", M4AF_TAG_DISK }, { "discnumber", M4AF_TAG_DISK },
{ "disctotal", TAG_TOTAL_DISCS },
{ "genre", M4AF_TAG_GENRE }, { "genre", M4AF_TAG_GENRE },
{ "grouping", M4AF_TAG_GROUPING }, { "grouping", M4AF_TAG_GROUPING },
{ "itunescompilation", M4AF_TAG_COMPILATION }, { "itunescompilation", M4AF_TAG_COMPILATION },
{ "lyrics", M4AF_TAG_LYRICS }, { "lyrics", M4AF_TAG_LYRICS },
{ "tempo", M4AF_TAG_TEMPO },
{ "recordeddate", M4AF_TAG_DATE },
{ "title", M4AF_TAG_TITLE }, { "title", M4AF_TAG_TITLE },
{ "titlesort", M4AF_FOURCC('s','o','n','m') }, { "titlesort", M4AF_FOURCC('s','o','n','m') },
{ "titlesortorder", M4AF_FOURCC('s','o','n','m') }, { "titlesortorder", M4AF_FOURCC('s','o','n','m') },
@ -95,6 +101,7 @@ uint32_t get_tag_fcc_from_name(const char *name)
return ent ? ent->fcc : 0; return ent ? ent->fcc : 0;
} }
static
char *aacenc_load_tag_from_file(const char *path, uint32_t *data_size) char *aacenc_load_tag_from_file(const char *path, uint32_t *data_size)
{ {
FILE *fp = 0; FILE *fp = 0;
@ -121,48 +128,152 @@ END:
return data; return data;
} }
void aacenc_param_add_itmf_entry(aacenc_tag_param_t *params, uint32_t tag, static
int aacenc_is_string_tag(uint32_t tag)
{
switch (tag) {
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_LYRICS:
case M4AF_TAG_TOOL:
case M4AF_TAG_ALBUM_ARTIST:
case M4AF_TAG_DESCRIPTION:
case M4AF_TAG_LONG_DESCRIPTION:
case M4AF_TAG_COPYRIGHT:
case M4AF_FOURCC('a','p','I','D'):
case M4AF_FOURCC('c','a','t','g'):
case M4AF_FOURCC('k','e','y','w'):
case M4AF_FOURCC('p','u','r','d'):
case M4AF_FOURCC('p','u','r','l'):
case M4AF_FOURCC('s','o','a','a'):
case M4AF_FOURCC('s','o','a','l'):
case M4AF_FOURCC('s','o','a','r'):
case M4AF_FOURCC('s','o','c','o'):
case M4AF_FOURCC('s','o','n','m'):
case M4AF_FOURCC('s','o','s','n'):
case M4AF_FOURCC('t','v','e','n'):
case M4AF_FOURCC('t','v','n','n'):
case M4AF_FOURCC('t','v','s','h'):
case M4AF_FOURCC('x','i','d',' '):
case M4AF_FOURCC('\xa9','e','n','c'):
case M4AF_FOURCC('\xa9','s','t','3'):
case M4AF_FOURCC('-','-','-','-'):
return 1;
}
return 0;
}
void aacenc_add_tag_to_store(aacenc_tag_store_t *store, uint32_t tag,
const char *key, const char *value, const char *key, const char *value,
uint32_t size, int is_file_name) uint32_t size, int is_file_name)
{ {
aacenc_tag_entry_t *entry; aacenc_tag_entry_t entry = { 0 };
char *dp = 0;
if (!is_file_name && !size) if (!is_file_name && !size)
return; return;
if (params->tag_count == params->tag_table_capacity) { entry.tag = tag;
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;
}
entry = params->tag_table + params->tag_count;
entry->tag = tag;
if (tag == M4AF_FOURCC('-','-','-','-')) if (tag == M4AF_FOURCC('-','-','-','-'))
entry->name = key; entry.name = (char *)key;
entry->data = value;
entry->data_size = size; if (is_file_name) {
entry->is_file_name = is_file_name; entry.data = dp = aacenc_load_tag_from_file(value, &size);
params->tag_count++; entry.data_size = size;
} else if (aacenc_is_string_tag(tag)) {
entry.data = dp = aacenc_to_utf8(value);
entry.data_size = strlen(entry.data);
} else {
entry.data = (char *)value;
entry.data_size = size;
}
aacenc_add_tag_entry_to_store(store, &entry);
free(dp);
}
void aacenc_add_tag_entry_to_store(void *ctx, const aacenc_tag_entry_t *tag)
{
aacenc_tag_store_t *store = ctx;
aacenc_tag_entry_t *entry;
if (store->tag_count == store->tag_table_capacity) {
unsigned newsize = store->tag_table_capacity;
newsize = newsize ? newsize * 2 : 1;
store->tag_table =
realloc(store->tag_table, newsize * sizeof(aacenc_tag_entry_t));
store->tag_table_capacity = newsize;
}
entry = store->tag_table + store->tag_count;
entry->tag = tag->tag;
entry->data_size = tag->data_size;
entry->name = tag->name ? strdup(tag->name) : 0;
entry->data = malloc(tag->data_size + 1);
memcpy(entry->data, tag->data, tag->data_size);
entry->data[tag->data_size] = 0;
store->tag_count++;
} }
static static
void tag_put_number_pair(m4af_ctx_t *m4af, uint32_t fcc, void tag_put_number_pair(aacenc_translate_generic_text_tag_ctx_t *ctx,
const char *snumber, const char *stotal) uint32_t fcc, unsigned number, unsigned total)
{ {
unsigned number = 0, total = 0;
char buf[128]; char buf[128];
aacenc_tag_entry_t entry = { 0 }; aacenc_tag_entry_t entry = { 0 };
if (snumber) sscanf(snumber, "%u", &number);
if (stotal) sscanf(stotal, "%u", &total);
if (number) { if (number) {
if (total) sprintf(buf, "%u/%u", number, total); if (total) sprintf(buf, "%u/%u", number, total);
else sprintf(buf, "%u", number); else sprintf(buf, "%u", number);
entry.tag = fcc; entry.tag = fcc;
entry.data = buf; entry.data = buf;
entry.data_size = strlen(buf); entry.data_size = strlen(buf);
aacenc_put_tag_entry(m4af, &entry); ctx->add(ctx->add_ctx, &entry);
}
}
void aacenc_translate_generic_text_tag(void *pctx, const char *key,
const char *val, uint32_t size)
{
aacenc_translate_generic_text_tag_ctx_t *ctx = pctx;
aacenc_tag_entry_t entry = { 0 };
uint32_t fcc;
/*
* Since track/disc number pair (number and total) can be stored within
* either single tag or separately, we cannot instantly translate
* them in one-to-one manner.
* Instead, we keep and store them until all tags are processed,
* then finally submit.
*/
if (!key) {
/* use null key as flushing signal */
tag_put_number_pair(ctx, M4AF_TAG_TRACK, ctx->track, ctx->track_total);
tag_put_number_pair(ctx, M4AF_TAG_DISK, ctx->disc, ctx->disc_total);
return;
}
if (!val || !size)
return;
if ((fcc = get_tag_fcc_from_name(key)) == 0)
return;
switch (fcc) {
case TAG_TOTAL_DISCS:
sscanf(val, "%d", &ctx->disc_total); break;
case TAG_TOTAL_TRACKS:
sscanf(val, "%d", &ctx->track_total); break;
case M4AF_TAG_DISK:
sscanf(val, "%d/%d", &ctx->disc, &ctx->disc_total); break;
case M4AF_TAG_TRACK:
sscanf(val, "%d/%d", &ctx->track, &ctx->track_total); break;
default:
{
entry.tag = fcc;
entry.data = (char *)val;
entry.data_size = (size == ~0U) ? strlen(val) : size;
ctx->add(ctx->add_ctx, &entry);
}
} }
} }
@ -188,7 +299,7 @@ const char *aacenc_json_object_get_string(JSON_Object *obj, const char *key,
return val; return val;
} }
void aacenc_put_tags_from_json(m4af_ctx_t *m4af, const char *json_filename) void aacenc_write_tags_from_json(m4af_ctx_t *m4af, const char *json_filename)
{ {
char *data = 0; char *data = 0;
JSON_Value *json = 0; JSON_Value *json = 0;
@ -197,11 +308,10 @@ void aacenc_put_tags_from_json(m4af_ctx_t *m4af, const char *json_filename)
uint32_t data_size; uint32_t data_size;
char *json_dot_path; char *json_dot_path;
char *filename = 0; char *filename = 0;
char *disc = 0; aacenc_translate_generic_text_tag_ctx_t ctx = { 0 };
char *track = 0;
char *total_discs = 0; ctx.add = aacenc_write_tag_entry;
char *total_tracks = 0; ctx.add_ctx = m4af;
aacenc_tag_entry_t entry = { 0 };
filename = strdup(json_filename); filename = strdup(json_filename);
if ((json_dot_path = strchr(filename, '?')) != 0) if ((json_dot_path = strchr(filename, '?')) != 0)
@ -226,59 +336,36 @@ void aacenc_put_tags_from_json(m4af_ctx_t *m4af, const char *json_filename)
char buf[256]; char buf[256];
const char *key = json_object_get_name(root, i); const char *key = json_object_get_name(root, i);
const char *val = aacenc_json_object_get_string(root, key, buf); const char *val = aacenc_json_object_get_string(root, key, buf);
uint32_t fcc = get_tag_fcc_from_name(key); if (val) aacenc_translate_generic_text_tag(&ctx, key, val, ~0U);
if (!val || !fcc)
continue;
switch (fcc) {
case TAG_TOTAL_DISCS:
total_discs = realloc(total_discs, strlen(val) + 1);
strcpy(total_discs, val);
break;
case TAG_TOTAL_TRACKS:
total_tracks = realloc(total_tracks, strlen(val) + 1);
strcpy(total_tracks, val);
break;
case M4AF_TAG_DISK:
disc = realloc(disc, strlen(val) + 1);
strcpy(disc, val);
break;
case M4AF_TAG_TRACK:
track = realloc(track, strlen(val) + 1);
strcpy(track, val);
break;
default:
{
entry.tag = fcc;
entry.data = val;
entry.data_size = strlen(val);
aacenc_put_tag_entry(m4af, &entry);
} }
} aacenc_translate_generic_text_tag(&ctx, 0, 0, 0);
}
tag_put_number_pair(m4af, M4AF_TAG_TRACK, track, total_tracks);
tag_put_number_pair(m4af, M4AF_TAG_DISK, disc, total_discs);
DONE: DONE:
if (track) free(track);
if (disc) free(disc);
if (total_tracks) free(total_tracks);
if (total_discs) free(total_discs);
if (data) free(data); if (data) free(data);
if (filename) free(filename); if (filename) free(filename);
if (json) json_value_free(json); if (json) json_value_free(json);
} }
void aacenc_put_tag_entry(m4af_ctx_t *m4af, const aacenc_tag_entry_t *tag) void aacenc_free_tag_store(aacenc_tag_store_t *store)
{ {
if (store->tag_table) {
unsigned i;
for (i = 0; i < store->tag_count; ++i) {
aacenc_tag_entry_t *ent = &store->tag_table[i];
free(ent->name);
free(ent->data);
}
free(store->tag_table);
store->tag_table = 0;
store->tag_count = 0;
}
}
void aacenc_write_tag_entry(void *ctx, const aacenc_tag_entry_t *tag)
{
m4af_ctx_t *m4af = ctx;
unsigned m, n = 0; unsigned m, n = 0;
const char *data = tag->data; const char *data = tag->data;
uint32_t data_size = tag->data_size;
char *file_contents = 0;
if (tag->is_file_name) {
data = file_contents = aacenc_load_tag_from_file(tag->data, &data_size);
if (!data) return;
}
switch (tag->tag) { switch (tag->tag) {
case M4AF_TAG_TRACK: case M4AF_TAG_TRACK:
if (sscanf(data, "%u/%u", &m, &n) >= 1) if (sscanf(data, "%u/%u", &m, &n) >= 1)
@ -334,59 +421,22 @@ void aacenc_put_tag_entry(m4af_ctx_t *m4af, const aacenc_tag_entry_t *tag)
data_type = M4AF_PNG; data_type = M4AF_PNG;
if (data_type) if (data_type)
m4af_add_itmf_short_tag(m4af, tag->tag, data_type, m4af_add_itmf_short_tag(m4af, tag->tag, data_type,
data, data_size); data, tag->data_size);
break; break;
} }
case M4AF_FOURCC('-','-','-','-'): case M4AF_FOURCC('-','-','-','-'):
{ {
char *u8 = aacenc_to_utf8(data); m4af_add_itmf_long_tag(m4af, tag->name, data);
m4af_add_itmf_long_tag(m4af, tag->name, u8);
free(u8);
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_LYRICS:
case M4AF_TAG_TOOL:
case M4AF_TAG_ALBUM_ARTIST:
case M4AF_TAG_DESCRIPTION:
case M4AF_TAG_LONG_DESCRIPTION:
case M4AF_TAG_COPYRIGHT:
case M4AF_FOURCC('a','p','I','D'):
case M4AF_FOURCC('c','a','t','g'):
case M4AF_FOURCC('k','e','y','w'):
case M4AF_FOURCC('p','u','r','d'):
case M4AF_FOURCC('p','u','r','l'):
case M4AF_FOURCC('s','o','a','a'):
case M4AF_FOURCC('s','o','a','l'):
case M4AF_FOURCC('s','o','a','r'):
case M4AF_FOURCC('s','o','c','o'):
case M4AF_FOURCC('s','o','n','m'):
case M4AF_FOURCC('s','o','s','n'):
case M4AF_FOURCC('t','v','e','n'):
case M4AF_FOURCC('t','v','n','n'):
case M4AF_FOURCC('t','v','s','h'):
case M4AF_FOURCC('x','i','d',' '):
case M4AF_FOURCC('\xa9','e','n','c'):
case M4AF_FOURCC('\xa9','s','t','3'):
{
char *u8 = aacenc_to_utf8(data);
m4af_add_itmf_string_tag(m4af, tag->tag, u8);
free(u8);
break; break;
} }
default: default:
if (aacenc_is_string_tag(tag->tag))
m4af_add_itmf_string_tag(m4af, tag->tag, data);
else
fprintf(stderr, "WARNING: unknown/unsupported tag: %c%c%c%c\n", fprintf(stderr, "WARNING: unknown/unsupported tag: %c%c%c%c\n",
tag->tag >> 24, (tag->tag >> 16) & 0xff, tag->tag >> 24, (tag->tag >> 16) & 0xff,
(tag->tag >> 8) & 0xff, tag->tag & 0xff); (tag->tag >> 8) & 0xff, tag->tag & 0xff);
} }
if (file_contents) free(file_contents);
} }

View File

@ -1,28 +1,48 @@
/*
* Copyright (C) 2013 nu774
* For conditions of distribution and use, see copyright notice in COPYING
*/
#ifndef METADATA_H #ifndef METADATA_H
#define METADATA_H #define METADATA_H
#include "m4af.h"
typedef struct aacenc_tag_entry_t { typedef struct aacenc_tag_entry_t {
uint32_t tag; uint32_t tag;
const char *name; char *name;
const char *data; char *data;
uint32_t data_size; uint32_t data_size;
int is_file_name;
} aacenc_tag_entry_t; } aacenc_tag_entry_t;
typedef struct aacenc_tag_param_t { typedef struct aacenc_tag_store_t {
aacenc_tag_entry_t *tag_table; aacenc_tag_entry_t *tag_table;
unsigned tag_count; unsigned tag_count;
unsigned tag_table_capacity; unsigned tag_table_capacity;
} aacenc_tag_param_t; } aacenc_tag_store_t;
char *aacenc_load_tag_from_file(const char *path, uint32_t *data_size); typedef struct aacenc_translate_generic_text_tag_ctx_t {
unsigned track, track_total, disc, disc_total;
void (*add)(void *, const aacenc_tag_entry_t *);
void *add_ctx;
} aacenc_translate_generic_text_tag_ctx_t;
void aacenc_param_add_itmf_entry(aacenc_tag_param_t *params, uint32_t tag, typedef void (*aacenc_tag_callback_t)(void *ctx, const char *key,
const char *value, uint32_t size);
void aacenc_translate_generic_text_tag(void *ctx, const char *key,
const char *val, uint32_t size);
void aacenc_add_tag_to_store(aacenc_tag_store_t *store, uint32_t tag,
const char *key, const char *value, const char *key, const char *value,
uint32_t size, int is_file_name); uint32_t size, int is_file_name);
void aacenc_put_tags_from_json(m4af_ctx_t *m4af, const char *json_filename); void aacenc_add_tag_entry_to_store(void *store, const aacenc_tag_entry_t *tag);
void aacenc_put_tag_entry(m4af_ctx_t *m4af, const aacenc_tag_entry_t *tag); void aacenc_free_tag_store(aacenc_tag_store_t *store);
void aacenc_write_tags_from_json(m4af_ctx_t *m4af, const char *json_filename);
void aacenc_write_tag_entry(void *m4af, const aacenc_tag_entry_t *tag);
#endif #endif

118
src/pcm_reader.h Normal file
View File

@ -0,0 +1,118 @@
/*
* Copyright (C) 2013 nu774
* For conditions of distribution and use, see copyright notice in COPYING
*/
#ifndef PCM_READER_H
#define PCM_READER_H
#include "lpcm.h"
typedef struct pcm_reader_t pcm_reader_t;
typedef struct pcm_reader_vtbl_t {
const pcm_sample_description_t *(*get_format)(pcm_reader_t *);
int64_t (*get_length)(pcm_reader_t *);
int64_t (*get_position)(pcm_reader_t *);
int (*read_frames)(pcm_reader_t *, void *, unsigned);
void (*teardown)(pcm_reader_t **);
} pcm_reader_vtbl_t;
struct pcm_reader_t {
pcm_reader_vtbl_t *vtbl;
};
typedef int (*pcm_read_callback)(void *cookie, void *data, uint32_t count);
typedef int (*pcm_seek_callback)(void *cookie, int64_t off, int whence);
typedef int64_t (*pcm_tell_callback)(void *cookie);
typedef struct pcm_io_vtbl_t {
pcm_read_callback read;
pcm_seek_callback seek;
pcm_tell_callback tell;
} pcm_io_vtbl_t;
typedef struct pcm_io_context_t {
pcm_io_vtbl_t *vtbl;
void *cookie;
} pcm_io_context_t;
static inline
const pcm_sample_description_t *pcm_get_format(pcm_reader_t *r)
{
return r->vtbl->get_format(r);
}
static inline
int64_t pcm_get_length(pcm_reader_t *r)
{
return r->vtbl->get_length(r);
}
static inline
int64_t pcm_get_position(pcm_reader_t *r)
{
return r->vtbl->get_position(r);
}
static inline
int64_t pcm_read_frames(pcm_reader_t *r, void *data, unsigned nframes)
{
return r->vtbl->read_frames(r, data, nframes);
}
static inline
void pcm_teardown(pcm_reader_t **r)
{
(*r)->vtbl->teardown(r);
}
static inline
uint32_t bitcount(uint32_t bits)
{
bits = (bits & 0x55555555) + (bits >> 1 & 0x55555555);
bits = (bits & 0x33333333) + (bits >> 2 & 0x33333333);
bits = (bits & 0x0f0f0f0f) + (bits >> 4 & 0x0f0f0f0f);
bits = (bits & 0x00ff00ff) + (bits >> 8 & 0x00ff00ff);
return (bits & 0x0000ffff) + (bits >>16 & 0x0000ffff);
}
#define TRY_IO(expr) \
do { \
if ((expr)) goto FAIL; \
} while (0)
#define ENSURE(expr) \
do { \
if (!(expr)) goto FAIL;\
} while (0)
int pcm_read(pcm_io_context_t *io, void *buffer, uint32_t size);
int pcm_skip(pcm_io_context_t *io, int64_t count);
static inline int pcm_seek(pcm_io_context_t *io, int64_t off, int whence)
{
return io->vtbl->seek ? io->vtbl->seek(io->cookie, off, whence) : -1;
}
static inline int64_t pcm_tell(pcm_io_context_t *io)
{
return io->vtbl->tell ? io->vtbl->tell(io->cookie) : -1;
}
int pcm_read16le(pcm_io_context_t *io, uint16_t *value);
int pcm_read16be(pcm_io_context_t *io, uint16_t *value);
int pcm_read32le(pcm_io_context_t *io, uint32_t *value);
int pcm_read32be(pcm_io_context_t *io, uint32_t *value);
int pcm_read64le(pcm_io_context_t *io, uint64_t *value);
int pcm_read64be(pcm_io_context_t *io, uint64_t *value);
int pcm_scanl(pcm_io_context_t *io, const char *fmt, ...);
int pcm_scanb(pcm_io_context_t *io, const char *fmt, ...);
int apple_chan_chunk(pcm_io_context_t *io, uint32_t chunk_size,
pcm_sample_description_t *fmt, uint8_t *mapping);
pcm_reader_t *pcm_open_sint16_converter(pcm_reader_t *reader);
pcm_reader_t *extrapolater_open(pcm_reader_t *reader);
#endif

356
src/pcm_readhelper.c Normal file
View File

@ -0,0 +1,356 @@
/*
* 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 "pcm_reader.h"
#include "m4af_endian.h"
#include "catypes.h"
int pcm_read(pcm_io_context_t *io, void *buffer, uint32_t size)
{
int rc;
uint32_t count = 0;
do {
rc = io->vtbl->read(io->cookie, buffer, size - count);
if (rc > 0)
count += rc;
} while (rc > 0 && count < size);
return count > 0 ? count : rc;
}
int pcm_skip(pcm_io_context_t *io, int64_t count)
{
char buff[8192];
int rc;
pcm_io_vtbl_t *vp = io->vtbl;
if (count == 0 || pcm_seek(io, count, SEEK_CUR) >= 0)
return 0;
do {
if ((rc = vp->read(io->cookie, buff, count > 8192 ? 8192 : count)) > 0)
count -= rc;
} while (rc > 0 && count > 0);
return count == 0 ? 0 : -1;
}
int pcm_read16le(pcm_io_context_t *io, uint16_t *value)
{
if (pcm_read(io, value, 2) == 2) {
*value = m4af_ltoh16(*value);
return 0;
}
return -1;
}
int pcm_read16be(pcm_io_context_t *io, uint16_t *value)
{
if (pcm_read(io, value, 2) == 2) {
*value = m4af_btoh16(*value);
return 0;
}
return -1;
}
int pcm_read32le(pcm_io_context_t *io, uint32_t *value)
{
if (pcm_read(io, value, 4) == 4) {
*value = m4af_ltoh32(*value);
return 0;
}
return -1;
}
int pcm_read32be(pcm_io_context_t *io, uint32_t *value)
{
if (pcm_read(io, value, 4) == 4) {
*value = m4af_btoh32(*value);
return 0;
}
return -1;
}
int pcm_read64le(pcm_io_context_t *io, uint64_t *value)
{
if (pcm_read(io, value, 8) == 8) {
*value = m4af_ltoh64(*value);
return 0;
}
return -1;
}
int pcm_read64be(pcm_io_context_t *io, uint64_t *value)
{
if (pcm_read(io, value, 8) == 8) {
*value = m4af_btoh64(*value);
return 0;
}
return -1;
}
int pcm_scanl(pcm_io_context_t *io, const char *fmt, ...)
{
int c, count = 0;
va_list ap;
va_start(ap, fmt);
while ((c = *fmt++)) {
switch (c) {
case 'S':
TRY_IO(pcm_read16le(io, va_arg(ap, uint16_t*)));
++count;
break;
case 'L':
TRY_IO(pcm_read32le(io, va_arg(ap, uint32_t*)));
++count;
break;
case 'Q':
TRY_IO(pcm_read64le(io, va_arg(ap, uint64_t*)));
++count;
break;
}
}
FAIL:
va_end(ap);
return count;
}
int pcm_scanb(pcm_io_context_t *io, const char *fmt, ...)
{
int c, count = 0;
va_list ap;
va_start(ap, fmt);
while ((c = *fmt++)) {
switch (c) {
case 'S':
TRY_IO(pcm_read16be(io, va_arg(ap, uint16_t*)));
++count;
break;
case 'L':
TRY_IO(pcm_read32be(io, va_arg(ap, uint32_t*)));
++count;
break;
case 'Q':
TRY_IO(pcm_read64be(io, va_arg(ap, uint64_t*)));
++count;
break;
}
}
FAIL:
va_end(ap);
return count;
}
static
int channel_compare(const void *a, const void *b)
{
return (*(const uint8_t **)a)[0] - (*(const uint8_t **)b)[0];
}
void apple_translate_channel_labels(uint8_t *channels, unsigned n)
{
unsigned i;
char *has_side = strpbrk((char*)channels, "\x0A\x0B");
for (i = 0; i < n; ++i) {
switch (channels[i]) {
case kAudioChannelLabel_LeftSurround:
case kAudioChannelLabel_RightSurround:
if (!has_side) channels[i] += 5; // map to SL/SR
break;
case kAudioChannelLabel_RearSurroundLeft:
case kAudioChannelLabel_RearSurroundRight:
if (!has_side) channels[i] -= 28; // map to BL/BR
break;
case kAudioChannelLabel_Mono:
channels[i] = kAudioChannelLabel_Center;
break;
}
}
}
int apple_chan_chunk(pcm_io_context_t *io, uint32_t chunk_size,
pcm_sample_description_t *fmt, uint8_t *mapping)
{
/*
* Although FDK encoder supports upto 5.1ch, we handle upto
* 8 channels here.
*/
uint32_t i, mChannelLayoutTag, mChannelBitmap, mNumberChannelDescriptions;
uint32_t mask = 0;
const uint32_t nchannels = fmt->channels_per_frame;
uint8_t channels[9] = { 0 };
uint8_t *index[8] = { 0 };
const char *layout = 0;
ENSURE(chunk_size >= 12);
TRY_IO(pcm_scanb(io, "LLL", &mChannelLayoutTag, &mChannelBitmap,
&mNumberChannelDescriptions) != 3);
switch (mChannelLayoutTag) {
case kAudioChannelLayoutTag_UseChannelBitmap:
ENSURE(bitcount(mask) == nchannels);
TRY_IO(pcm_skip(io, chunk_size - 12));
fmt->channel_mask = mChannelBitmap;
for (i = 0; i < nchannels; ++i)
mapping[i] = i;
return 0;
case kAudioChannelLayoutTag_UseChannelDescriptions:
ENSURE(mNumberChannelDescriptions == nchannels);
ENSURE(chunk_size >= 12 + nchannels * 20);
for (i = 0; i < mNumberChannelDescriptions; ++i) {
uint32_t mChannelLabel;
TRY_IO(pcm_read32be(io, &mChannelLabel));
ENSURE(mChannelLabel && mChannelLabel <= 0xff);
channels[i] = mChannelLabel;
TRY_IO(pcm_skip(io, 16));
}
TRY_IO(pcm_skip(io, chunk_size - 12 - nchannels * 20));
apple_translate_channel_labels(channels, nchannels);
for (i = 0; i < nchannels; ++i)
if (channels[i] > kAudioChannelLabel_TopBackLeft)
goto FAIL;
break;
default:
ENSURE((mChannelLayoutTag & 0xffff) == nchannels);
TRY_IO(pcm_skip(io, chunk_size - 12));
switch (mChannelLayoutTag) {
/* 1ch */
case kAudioChannelLayoutTag_Mono:
layout = "\x03"; break;
/* 1.1ch */
case kAudioChannelLayoutTag_AC3_1_0_1:
layout = "\x03\x04"; break;
/* 2ch */
case kAudioChannelLayoutTag_Stereo:
case kAudioChannelLayoutTag_MatrixStereo:
case kAudioChannelLayoutTag_Binaural:
layout = "\x01\x02"; break;
/* 2.1ch */
case kAudioChannelLayoutTag_DVD_4:
layout = "\x01\x02\x04"; break;
/* 3ch */
case kAudioChannelLayoutTag_MPEG_3_0_A:
layout = "\x01\x02\x03"; break;
case kAudioChannelLayoutTag_AC3_3_0:
layout = "\x01\x03\x02"; break;
case kAudioChannelLayoutTag_MPEG_3_0_B:
layout = "\x03\x01\x02"; break;
case kAudioChannelLayoutTag_ITU_2_1:
layout = "\x01\x02\x09"; break;
/* 3.1ch */
case kAudioChannelLayoutTag_DVD_10:
layout = "\x01\x02\x03\x04"; break;
case kAudioChannelLayoutTag_AC3_3_0_1:
layout = "\x01\x03\x02\x04"; break;
case kAudioChannelLayoutTag_DVD_5:
layout = "\x01\x02\x04\x09"; break;
case kAudioChannelLayoutTag_AC3_2_1_1:
layout = "\x01\x02\x09\x04"; break;
/* 4ch */
case kAudioChannelLayoutTag_Quadraphonic:
case kAudioChannelLayoutTag_ITU_2_2:
layout = "\x01\x02\x0A\x0B"; break;
case kAudioChannelLayoutTag_MPEG_4_0_A:
layout = "\x01\x02\x03\x09"; break;
case kAudioChannelLayoutTag_MPEG_4_0_B:
layout = "\x03\x01\x02\x09"; break;
case kAudioChannelLayoutTag_AC3_3_1:
layout = "\x01\x03\x02\x09"; break;
/* 4.1ch */
case kAudioChannelLayoutTag_DVD_6:
layout = "\x01\x02\x04\x0A\x0B"; break;
case kAudioChannelLayoutTag_DVD_18:
layout = "\x01\x02\x0A\x0B\x04"; break;
case kAudioChannelLayoutTag_DVD_11:
layout = "\x01\x02\x03\x04\x09"; break;
case kAudioChannelLayoutTag_AC3_3_1_1:
layout = "\x01\x03\x02\x09\x04"; break;
/* 5ch */
case kAudioChannelLayoutTag_MPEG_5_0_A:
layout = "\x01\x02\x03\x0A\x0B"; break;
case kAudioChannelLayoutTag_Pentagonal:
case kAudioChannelLayoutTag_MPEG_5_0_B:
layout = "\x01\x02\x0A\x0B\x03"; break;
case kAudioChannelLayoutTag_MPEG_5_0_C:
layout = "\x01\x03\x02\x0A\x0B"; break;
case kAudioChannelLayoutTag_MPEG_5_0_D:
layout = "\x03\x01\x02\x0A\x0B"; break;
/* 5.1ch */
case kAudioChannelLayoutTag_MPEG_5_1_A:
layout = "\x01\x02\x03\x04\x0A\x0B"; break;
case kAudioChannelLayoutTag_MPEG_5_1_B:
layout = "\x01\x02\x0A\x0B\x03\x04"; break;
case kAudioChannelLayoutTag_MPEG_5_1_C:
layout = "\x01\x03\x02\x0A\x0B\x04"; break;
case kAudioChannelLayoutTag_MPEG_5_1_D:
layout = "\x03\x01\x02\x0A\x0B\x04"; break;
/* 6ch */
case kAudioChannelLayoutTag_Hexagonal:
case kAudioChannelLayoutTag_AudioUnit_6_0:
layout = "\x01\x02\x0A\x0B\x03\x09"; break;
case kAudioChannelLayoutTag_AAC_6_0:
layout = "\x03\x01\x02\x0A\x0B\x09"; break;
/* 6.1ch */
case kAudioChannelLayoutTag_MPEG_6_1_A:
layout = "\x01\x02\x03\x04\x0A\x0B\x09"; break;
case kAudioChannelLayoutTag_AAC_6_1:
layout = "\x03\x01\x02\x0A\x0B\x09\x04"; break;
/* 7ch */
case kAudioChannelLayoutTag_AudioUnit_7_0:
layout = "\x01\x02\x0A\x0B\x03\x05\x06"; break;
case kAudioChannelLayoutTag_AudioUnit_7_0_Front:
layout = "\x01\x02\x0A\x0B\x03\x07\x08"; break;
case kAudioChannelLayoutTag_AAC_7_0:
layout = "\x03\x01\x02\x0A\x0B\x05\x06"; break;
/* 7.1ch */
case kAudioChannelLayoutTag_MPEG_7_1_A:
layout = "\x01\x02\x03\x04\x0A\x0B\x07\x08"; break;
case kAudioChannelLayoutTag_MPEG_7_1_B:
layout = "\x03\x07\x08\x01\x02\x05\x06\x04"; break;
case kAudioChannelLayoutTag_MPEG_7_1_C:
layout = "\x01\x02\x03\x04\x0A\x0B\x05\x06"; break;
case kAudioChannelLayoutTag_Emagic_Default_7_1:
layout = "\x01\x02\x0A\x0B\x03\x04\x07\x08"; break;
/* 8ch */
case kAudioChannelLayoutTag_Octagonal:
layout = "\x01\x02\x05\x06\x03\x09\x0A\x0B"; break;
case kAudioChannelLayoutTag_AAC_Octagonal:
layout = "\x03\x01\x02\x0A\x0B\x05\x06\x09"; break;
default:
goto FAIL;
}
strcpy((char*)channels, layout);
}
for (i = 0; i < nchannels; ++i)
mask |= 1 << (channels[i] - 1);
fmt->channel_mask = mask;
ENSURE(bitcount(mask) == nchannels);
for (i = 0; i < nchannels; ++i)
index[i] = channels + i;
qsort(index, nchannels, sizeof(char*), channel_compare);
for (i = 0; i < nchannels; ++i)
mapping[i] = index[i] - channels;
return 0;
FAIL:
return -1;
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (C) 2013 nu774
* For conditions of distribution and use, see copyright notice in COPYING
*/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#if HAVE_STDINT_H
# include <stdint.h>
#endif
#include "pcm_reader.h"
typedef struct pcm_sint16_converter_t {
pcm_reader_vtbl_t *vtbl;
pcm_reader_t *src;
pcm_sample_description_t format;
void *pivot;
unsigned capacity;
} pcm_sint16_converter_t;
static inline pcm_reader_t *get_source(pcm_reader_t *reader)
{
return ((pcm_sint16_converter_t *)reader)->src;
}
static const
pcm_sample_description_t *get_format(pcm_reader_t *reader)
{
return &((pcm_sint16_converter_t *)reader)->format;
}
static int64_t get_length(pcm_reader_t *reader)
{
return pcm_get_length(get_source(reader));
}
static int64_t get_position(pcm_reader_t *reader)
{
return pcm_get_position(get_source(reader));
}
static int read_frames(pcm_reader_t *reader, void *buffer, unsigned nframes)
{
pcm_sint16_converter_t *self = (pcm_sint16_converter_t *)reader;
const pcm_sample_description_t *sfmt = pcm_get_format(self->src);
unsigned bytes = nframes * sfmt->bytes_per_frame;
if (self->capacity < bytes) {
void *p = realloc(self->pivot, bytes);
if (!p) return -1;
self->pivot = p;
self->capacity = bytes;
}
nframes = pcm_read_frames(self->src, self->pivot, nframes);
if (pcm_convert_to_native_sint16(sfmt, self->pivot, nframes, buffer) < 0)
return -1;
return nframes;
}
static void teardown(pcm_reader_t **reader)
{
pcm_sint16_converter_t *self = (pcm_sint16_converter_t *)*reader;
pcm_teardown(&self->src);
free(self->pivot);
free(self);
*reader = 0;
}
static pcm_reader_vtbl_t my_vtable = {
get_format, get_length, get_position, read_frames, teardown
};
pcm_reader_t *pcm_open_sint16_converter(pcm_reader_t *reader)
{
pcm_sint16_converter_t *self = 0;
pcm_sample_description_t *fmt;
if ((self = calloc(1, sizeof(pcm_sint16_converter_t))) == 0)
return 0;
self->src = reader;
self->vtbl = &my_vtable;
memcpy(&self->format, pcm_get_format(reader), sizeof(self->format));
fmt = &self->format;
#if WORDS_BIGENDIAN
fmt->sample_type = PCM_TYPE_SINT_BE;
#else
fmt->sample_type = PCM_TYPE_SINT;
#endif
fmt->bits_per_channel = 16;
fmt->bytes_per_frame = 2 * fmt->channels_per_frame;
return (pcm_reader_t *)self;
}

View File

@ -15,35 +15,18 @@
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include "wav_reader.h" #include "wav_reader.h"
#include "m4af_endian.h"
#define RIFF_FOURCC(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24)) #define RIFF_FOURCC(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
#define TRY_IO(expr) \ typedef struct wav_reader_t {
do { \ pcm_reader_vtbl_t *vtbl;
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; pcm_sample_description_t sample_format;
int64_t length; int64_t length;
int64_t position; int64_t position;
int32_t data_offset; int32_t data_offset;
int ignore_length; int ignore_length;
int last_error; pcm_io_context_t io;
wav_io_context_t io; } wav_reader_t;
void *io_cookie;
};
static const uint8_t WAV_GUID_PCM[] = { static const uint8_t WAV_GUID_PCM[] = {
1, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71 1, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71
@ -52,177 +35,46 @@ static const uint8_t WAV_GUID_FLOAT[] = {
3, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71 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) static const pcm_sample_description_t *wav_get_format(pcm_reader_t *reader)
{ {
return &reader->sample_format; return &((wav_reader_t *)reader)->sample_format;
} }
int64_t wav_get_length(wav_reader_t *reader) static int64_t wav_get_length(pcm_reader_t *reader)
{ {
return reader->length; return ((wav_reader_t *)reader)->length;
} }
int64_t wav_get_position(wav_reader_t *reader) static int64_t wav_get_position(pcm_reader_t *reader)
{ {
return reader->position; return ((wav_reader_t *)reader)->position;
} }
void wav_teardown(wav_reader_t **reader) static void wav_teardown(pcm_reader_t **reader)
{ {
free(*reader); free(*reader);
*reader = 0; *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, int64_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_seek(wav_reader_t *reader, int64_t off, int whence)
{
int rc;
if (reader->last_error)
return -1;
if (!reader->io.seek)
goto FAIL;
if ((rc = reader->io.seek(reader->io_cookie, off, whence)) < 0)
goto FAIL;
return 0;
FAIL:
reader->last_error = WAV_IO_ERROR;
return -1;
}
static
int64_t riff_tell(wav_reader_t *reader)
{
int64_t off;
if (reader->last_error || !reader->io.tell)
return -1;
off = reader->io.tell(reader->io_cookie);
if (off < 0) {
reader->last_error = WAV_IO_ERROR;
return -1;
}
return off;
}
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 static
uint32_t riff_next_chunk(wav_reader_t *reader, uint32_t *chunk_size) uint32_t riff_next_chunk(wav_reader_t *reader, uint32_t *chunk_size)
{ {
uint32_t fcc; uint32_t fcc;
if (riff_scan(reader, "LL", &fcc, chunk_size) == 2) return (pcm_scanl(&reader->io, "LL", &fcc, chunk_size) == 2) ? fcc : 0;
return fcc;
return 0;
} }
int wav_read_frames(wav_reader_t *reader, void *buffer, unsigned nframes) static
int wav_read_frames(pcm_reader_t *preader, void *buffer, unsigned nframes)
{ {
int rc; int rc;
unsigned nbytes; unsigned nbytes;
wav_reader_t *reader = (wav_reader_t *)preader;
if (!reader->ignore_length && nframes > reader->length - reader->position) if (!reader->ignore_length && nframes > reader->length - reader->position)
nframes = reader->length - reader->position; nframes = reader->length - reader->position;
nbytes = nframes * reader->sample_format.bytes_per_frame; nbytes = nframes * reader->sample_format.bytes_per_frame;
if (nbytes) { if (nbytes) {
if ((rc = riff_read(reader, buffer, nbytes)) < 0) if ((rc = pcm_read(&reader->io, buffer, nbytes)) < 0)
return -1; return -1;
nframes = rc / reader->sample_format.bytes_per_frame; nframes = rc / reader->sample_format.bytes_per_frame;
reader->position += nframes; reader->position += nframes;
@ -237,12 +89,10 @@ int riff_ds64(wav_reader_t *reader, int64_t *length)
uint64_t riff_size, sample_count; uint64_t riff_size, sample_count;
fcc = riff_next_chunk(reader, &chunk_size); fcc = riff_next_chunk(reader, &chunk_size);
ASSERT_FORMAT(reader, ENSURE(fcc == RIFF_FOURCC('d','s','6','4') && chunk_size >= 28);
fcc == RIFF_FOURCC('d','s','6','4') && chunk_size >= 28); TRY_IO(pcm_scanl(&reader->io, "QQQL",
TRY_IO(riff_scan(reader, "QQQL",
&riff_size, length, &sample_count, &table_size) != 4); &riff_size, length, &sample_count, &table_size) != 4);
TRY_IO(riff_skip(reader, (chunk_size - 27) & ~1)); TRY_IO(pcm_skip(&reader->io, (chunk_size - 27) & ~1));
reader->data_offset += (chunk_size + 9) & ~1;
FAIL: FAIL:
return -1; return -1;
} }
@ -256,41 +106,34 @@ int wav_fmt(wav_reader_t *reader, uint32_t size)
uint8_t guid[16]; uint8_t guid[16];
int is_float = 0; int is_float = 0;
ASSERT_FORMAT(reader, size >= 16); ENSURE(size >= 16);
TRY_IO(riff_scan(reader, "SSLLSS", &wFormatTag, &nChannels, TRY_IO(pcm_scanl(&reader->io, "SSLLSS", &wFormatTag, &nChannels,
&nSamplesPerSec, &nAvgBytesPerSec, &nBlockAlign, &nSamplesPerSec, &nAvgBytesPerSec, &nBlockAlign,
&wBitsPerSample) != 6); &wBitsPerSample) != 6);
wValidBitsPerSample = wBitsPerSample; wValidBitsPerSample = wBitsPerSample;
if (wFormatTag != 1 && wFormatTag != 3 && wFormatTag != 0xfffe) { ENSURE(wFormatTag == 1 || wFormatTag == 3 || wFormatTag == 0xfffe);
reader->last_error = WAV_UNSUPPORTED_FORMAT; ENSURE(nChannels && nSamplesPerSec && nAvgBytesPerSec &&
goto FAIL;
}
ASSERT_FORMAT(reader,
nChannels && nSamplesPerSec && nAvgBytesPerSec &&
nBlockAlign && wBitsPerSample && !(wBitsPerSample & 7) && nBlockAlign && wBitsPerSample && !(wBitsPerSample & 7) &&
nBlockAlign == nChannels * wBitsPerSample / 8); nBlockAlign == nChannels * wBitsPerSample / 8);
if (wFormatTag == 3) if (wFormatTag == 3)
is_float = 1; is_float = 1;
if (wFormatTag != 0xfffe) if (wFormatTag != 0xfffe)
TRY_IO(riff_skip(reader, (size - 15) & ~1)); TRY_IO(pcm_skip(&reader->io, (size - 15) & ~1));
else { else {
ASSERT_FORMAT(reader, size >= 40); ENSURE(size >= 40);
TRY_IO(riff_scan(reader, "SSL", TRY_IO(pcm_scanl(&reader->io, "SSL",
&cbSize, &wValidBitsPerSample, &dwChannelMask) != 3); &cbSize, &wValidBitsPerSample, &dwChannelMask) != 3);
TRY_IO(riff_read(reader, guid, 16) != 16); TRY_IO(pcm_read(&reader->io, guid, 16) != 16);
if (memcmp(guid, WAV_GUID_FLOAT, 16) == 0) if (memcmp(guid, WAV_GUID_FLOAT, 16) == 0)
is_float = 1; is_float = 1;
else if (memcmp(guid, WAV_GUID_PCM, 16) != 0) { else if (memcmp(guid, WAV_GUID_PCM, 16) != 0)
reader->last_error = WAV_UNSUPPORTED_FORMAT;
goto FAIL; goto FAIL;
} ENSURE(wValidBitsPerSample && wValidBitsPerSample <= wBitsPerSample);
ASSERT_FORMAT(reader, TRY_IO(pcm_skip(&reader->io, (size - 39) & ~1));
wValidBitsPerSample &&
wValidBitsPerSample <= wBitsPerSample);
TRY_IO(riff_skip(reader, (size - 39) & ~1));
} }
reader->sample_format.sample_rate = nSamplesPerSec; reader->sample_format.sample_rate = nSamplesPerSec;
reader->sample_format.bits_per_channel = wValidBitsPerSample; reader->sample_format.bits_per_channel = wValidBitsPerSample;
@ -315,13 +158,10 @@ int wav_parse(wav_reader_t *reader, int64_t *data_length)
*data_length = 0; *data_length = 0;
container = riff_next_chunk(reader, &chunk_size); container = riff_next_chunk(reader, &chunk_size);
if (container != RIFF_FOURCC('R','I','F','F') && ENSURE(container == RIFF_FOURCC('R','I','F','F') ||
container != RIFF_FOURCC('R','F','6','4')) container == RIFF_FOURCC('R','F','6','4'));
goto FAIL; TRY_IO(pcm_read32le(&reader->io, &fcc));
TRY_IO(riff_read32(reader, &fcc)); ENSURE(fcc == RIFF_FOURCC('W','A','V','E'));
if (fcc != RIFF_FOURCC('W','A','V','E'))
goto FAIL;
reader->data_offset = 12;
if (container == RIFF_FOURCC('R','F','6','4')) if (container == RIFF_FOURCC('R','F','6','4'))
riff_ds64(reader, data_length); riff_ds64(reader, data_length);
@ -332,12 +172,11 @@ int wav_parse(wav_reader_t *reader, int64_t *data_length)
} else if (fcc == RIFF_FOURCC('d','a','t','a')) { } else if (fcc == RIFF_FOURCC('d','a','t','a')) {
if (container == RIFF_FOURCC('R','I','F','F')) if (container == RIFF_FOURCC('R','I','F','F'))
*data_length = chunk_size; *data_length = chunk_size;
reader->data_offset += 8; reader->data_offset = pcm_tell(&reader->io);
break; break;
} else { } else {
TRY_IO(riff_skip(reader, (chunk_size + 1) & ~1)); TRY_IO(pcm_skip(&reader->io, (chunk_size + 1) & ~1));
} }
reader->data_offset += (chunk_size + 9) & ~1;
} }
if (fcc == RIFF_FOURCC('d','a','t','a')) if (fcc == RIFF_FOURCC('d','a','t','a'))
return 0; return 0;
@ -345,8 +184,15 @@ FAIL:
return -1; return -1;
} }
wav_reader_t *wav_open(wav_io_context_t *io_ctx, void *io_cookie, static pcm_reader_vtbl_t wav_vtable = {
int ignore_length) wav_get_format,
wav_get_length,
wav_get_position,
wav_read_frames,
wav_teardown
};
pcm_reader_t *wav_open(pcm_io_context_t *io, int ignore_length)
{ {
wav_reader_t *reader = 0; wav_reader_t *reader = 0;
int64_t data_length; int64_t data_length;
@ -354,8 +200,7 @@ wav_reader_t *wav_open(wav_io_context_t *io_ctx, void *io_cookie,
if ((reader = calloc(1, sizeof(wav_reader_t))) == 0) if ((reader = calloc(1, sizeof(wav_reader_t))) == 0)
return 0; return 0;
memcpy(&reader->io, io_ctx, sizeof(wav_io_context_t)); memcpy(&reader->io, io, sizeof(pcm_io_context_t));
reader->io_cookie = io_cookie;
reader->ignore_length = ignore_length; reader->ignore_length = ignore_length;
if (wav_parse(reader, &data_length) < 0) { if (wav_parse(reader, &data_length) < 0) {
free(reader); free(reader);
@ -367,37 +212,34 @@ wav_reader_t *wav_open(wav_io_context_t *io_ctx, void *io_cookie,
else else
reader->length = data_length / bpf; reader->length = data_length / bpf;
if (reader->length == INT64_MAX && reader->io.seek && reader->io.tell) { if (reader->length == INT64_MAX) {
if (reader->io.seek(reader->io_cookie, 0, SEEK_END) >= 0) { if (pcm_seek(&reader->io, 0, SEEK_END) >= 0) {
int64_t size = reader->io.tell(reader->io_cookie); int64_t size = pcm_tell(&reader->io);
if (size > 0) if (size > 0)
reader->length = (size - reader->data_offset) / bpf; reader->length = (size - reader->data_offset) / bpf;
reader->io.seek(reader->io_cookie, reader->data_offset, SEEK_SET); pcm_seek(&reader->io, reader->data_offset, SEEK_SET);
} }
} }
return reader; reader->vtbl = &wav_vtable;
return (pcm_reader_t *)reader;
} }
wav_reader_t *raw_open(wav_io_context_t *io_ctx, void *io_cookie, pcm_reader_t *raw_open(pcm_io_context_t *io,
const pcm_sample_description_t *desc) const pcm_sample_description_t *desc)
{ {
wav_reader_t *reader = 0; wav_reader_t *reader = 0;
if ((reader = calloc(1, sizeof(wav_reader_t))) == 0) if ((reader = calloc(1, sizeof(wav_reader_t))) == 0)
return 0; return 0;
memcpy(&reader->io, io_ctx, sizeof(wav_io_context_t)); memcpy(&reader->io, io, sizeof(pcm_io_context_t));
memcpy(&reader->sample_format, desc, sizeof(pcm_sample_description_t)); memcpy(&reader->sample_format, desc, sizeof(pcm_sample_description_t));
reader->io_cookie = io_cookie; if (pcm_seek(&reader->io, 0, SEEK_END) >= 0) {
if (io_ctx->seek && io_ctx->tell) { int64_t size = pcm_tell(&reader->io);
if (reader->io.seek(reader->io_cookie, 0, SEEK_END) >= 0) {
int64_t size = reader->io.tell(reader->io_cookie);
if (size > 0) if (size > 0)
reader->length = size / desc->bytes_per_frame; reader->length = size / reader->sample_format.bytes_per_frame;
reader->io.seek(reader->io_cookie, reader->data_offset, SEEK_SET); pcm_seek(&reader->io, 0, SEEK_SET);
}
} else } else
reader->length = INT64_MAX; reader->length = INT64_MAX;
return reader; reader->vtbl = &wav_vtable;
return (pcm_reader_t *)reader;
} }

View File

@ -6,34 +6,10 @@
#define WAV_READER_H #define WAV_READER_H
#include "lpcm.h" #include "lpcm.h"
#include "pcm_reader.h"
enum wav_error_code { pcm_reader_t *wav_open(pcm_io_context_t *io, int ignore_length);
WAV_IO_ERROR = 1, pcm_reader_t *raw_open(pcm_io_context_t *io,
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 int64_t (*wav_tell_callback)(void *cookie);
typedef struct wav_io_context_t {
wav_read_callback read;
wav_seek_callback seek;
wav_tell_callback tell;
} 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);
wav_reader_t *raw_open(wav_io_context_t *io_ctx, void *io_cookie,
const pcm_sample_description_t *desc); const pcm_sample_description_t *desc);
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 #endif

View File

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