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

20 Commits

Author SHA1 Message Date
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
20 changed files with 683 additions and 104 deletions

View File

@ -1,6 +1,26 @@
2013-10-25 nu774 <honeycomb77@gmail.com>
* update ChangeLog [HEAD]
* 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> 2013-10-22 nu774 <honeycomb77@gmail.com>
* bump version [HEAD] * bump version [v0.3.3]
* fixed bogus sgpd written on --gapless-mode=1 and 2 * fixed bogus sgpd written on --gapless-mode=1 and 2

View File

@ -98,6 +98,8 @@ copy ..\fdk-aac\libSYS\include\machine_type.h include\fdk-aac\ </Command>
<ClCompile Include="..\src\aacenc.c" /> <ClCompile Include="..\src\aacenc.c" />
<ClCompile Include="..\src\caf_reader.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" />
@ -111,7 +113,10 @@ copy ..\fdk-aac\libSYS\include\machine_type.h include\fdk-aac\ </Command>
<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" />

View File

@ -24,6 +24,12 @@
<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>
@ -62,6 +68,9 @@
<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>

View File

@ -6,6 +6,8 @@ bin_PROGRAMS = fdkaac
fdkaac_SOURCES = \ fdkaac_SOURCES = \
src/aacenc.c \ src/aacenc.c \
src/caf_reader.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 \

View File

@ -10,6 +10,7 @@
#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_active(const aacenc_param_t *params) int aacenc_is_sbr_active(const aacenc_param_t *params)
@ -25,6 +26,68 @@ int aacenc_is_sbr_active(const aacenc_param_t *params)
return 0; return 0;
} }
static const unsigned aacenc_sampling_freq_tab[] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000, 7350, 0, 0, 0
};
static
unsigned sampling_freq_index(unsigned rate)
{
unsigned i;
for (i = 0; aacenc_sampling_freq_tab[i]; ++i)
if (aacenc_sampling_freq_tab[i] == rate)
return i;
return 0xf;
}
/*
* Append backward compatible SBR/PS signaling to implicit signaling ASC,
* if SBR/PS is present.
*/
int aacenc_mp4asc(const aacenc_param_t *params,
const uint8_t *asc, uint32_t ascsize,
uint8_t *outasc, uint32_t *outsize)
{
unsigned asc_sfreq = aacenc_sampling_freq_tab[(asc[0]&0x7)<<1 |asc[1]>>7];
switch (params->profile) {
case AOT_SBR:
case AOT_PS:
if (*outsize < ascsize + 3)
return -1;
memcpy(outasc, asc, ascsize);
/* syncExtensionType:11 (value:0x2b7) */
outasc[ascsize+0] = 0x2b << 1;
outasc[ascsize+1] = 0x7 << 5;
/* extensionAudioObjectType:5 (value:5)*/
outasc[ascsize+1] |= 5;
/* sbrPresentFlag:1 (value:1) */
outasc[ascsize+2] = 0x80;
/* extensionSamplingFrequencyIndex:4 */
outasc[ascsize+2] |= sampling_freq_index(asc_sfreq << 1) << 3;
if (params->profile == AOT_SBR) {
*outsize = ascsize + 3;
break;
}
if (*outsize < ascsize + 5)
return -1;
/* syncExtensionType:11 (value:0x548) */
outasc[ascsize+2] |= 0x5;
outasc[ascsize+3] = 0x48;
/* psPresentFlag:1 (value:1) */
outasc[ascsize+4] = 0x80;
*outsize = ascsize + 5;
break;
default:
if (*outsize < ascsize)
return -1;
memcpy(outasc, asc, ascsize);
*outsize = ascsize;
}
return 0;
}
static static
int aacenc_channel_mode(const pcm_sample_description_t *format) int aacenc_channel_mode(const pcm_sample_description_t *format)
{ {
@ -124,7 +187,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 +205,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 +232,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

@ -24,8 +24,17 @@ 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_active(const aacenc_param_t *params); int aacenc_is_sbr_active(const aacenc_param_t *params);
int aacenc_mp4asc(const aacenc_param_t *params,
const uint8_t *asc, uint32_t ascsize,
uint8_t *outasc, uint32_t *outsize);
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 +42,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

View File

@ -193,12 +193,10 @@ int caf_parse(caf_reader_t *reader, int64_t *data_length)
} else if (fcc == M4AF_FOURCC('d','a','t','a')) { } else if (fcc == M4AF_FOURCC('d','a','t','a')) {
TRY_IO(pcm_skip(&reader->io, 4)); /* mEditCount */ TRY_IO(pcm_skip(&reader->io, 4)); /* mEditCount */
*data_length = (chunk_size == ~0ULL) ? chunk_size : chunk_size - 4; *data_length = (chunk_size == ~0ULL) ? chunk_size : chunk_size - 4;
reader->data_offset += 12; reader->data_offset = pcm_tell(&reader->io);
break; break;
} else } else
TRY_IO(pcm_skip(&reader->io, chunk_size)); TRY_IO(pcm_skip(&reader->io, chunk_size));
reader->data_offset += (chunk_size + 8);
} }
ENSURE(reader->sample_format.channels_per_frame); ENSURE(reader->sample_format.channels_per_frame);
ENSURE(fcc == M4AF_FOURCC('d','a','t','a')); ENSURE(fcc == M4AF_FOURCC('d','a','t','a'));

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)
{ {

View File

@ -31,6 +31,36 @@ 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); int16_t *result);

View File

@ -864,7 +864,6 @@ 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\026" /* size: 22 */ "\0\0\0\026" /* size: 22 */
"sgpd" /* type */ "sgpd" /* type */
@ -1050,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

@ -133,10 +133,6 @@ PROGNAME " %s\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 Enable ELD-SBR (AAC ELD only)\n"
" -s, --sbr-signaling <n> SBR signaling mode\n"
" 0: Implicit, backward compatible(default)\n"
" 1: Explicit SBR and implicit PS\n"
" 2: Explicit hierarchical signaling\n"
" -f, --transport-format <n> Transport format\n" " -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"
@ -153,7 +149,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"
@ -208,6 +207,7 @@ typedef struct aacenc_param_ex_t {
char *output_filename; char *output_filename;
FILE *output_fp; 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;
@ -230,6 +230,7 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
int ch; int ch;
unsigned n; unsigned 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')
@ -247,12 +248,12 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
{ "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", no_argument, 0, 'L' },
{ "sbr-signaling", 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 },
@ -326,13 +327,6 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
case 'L': case 'L':
params->lowdelay_sbr = 1; params->lowdelay_sbr = 1;
break; break;
case 's':
if (sscanf(optarg, "%u", &n) != 1 || n > 2) {
fprintf(stderr, "invalid arg for sbr-signaling\n");
return -1;
}
params->sbr_signaling = n;
break;
case 'f': case 'f':
if (sscanf(optarg, "%u", &n) != 1) { if (sscanf(optarg, "%u", &n) != 1) {
fprintf(stderr, "invalid arg for transport-format\n"); fprintf(stderr, "invalid arg for transport-format\n");
@ -360,6 +354,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;
@ -475,16 +472,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;
} }
@ -492,51 +488,76 @@ int write_sample(FILE *ofp, m4af_ctx_t *m4af,
} }
static static
int encode(pcm_reader_t *reader, 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)
{ {
int16_t *ibuf = 0; int16_t *ibuf = 0, *ip;
uint8_t *obuf = 0; aacenc_frame_t obuf[2] = {{ 0 }}, *obp;
uint32_t olen; unsigned flip = 0;
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 *fmt = pcm_get_format(reader); const pcm_sample_description_t *fmt = pcm_get_format(reader);
ibuf = malloc(frame_length * fmt->bytes_per_frame); ibuf = malloc(frame_length * fmt->bytes_per_frame);
aacenc_progress_init(&progress, pcm_get_length(reader), fmt->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 = pcm_read_frames(reader, 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;
} }
if (show_progress) if (!params->silent)
aacenc_progress_update(&progress, pcm_get_position(reader), aacenc_progress_update(&progress, pcm_get_position(reader),
fmt->sample_rate * 2); fmt->sample_rate * 2);
} }
if ((consumed = aac_encode_frame(encoder, fmt, ibuf, nread, ip = ibuf;
&obuf, &olen, &osize)) < 0) remaining = nread;
goto END; do {
if (olen > 0) { obp = &obuf[flip];
if (write_sample(ofp, m4af, obuf, olen, frame_length) < 0) consumed = aac_encode_frame(encoder, fmt, ip, remaining, obp);
if (consumed < 0) goto END;
if (consumed == 0 && obp->size == 0) goto DONE;
if (obp->size == 0) break;
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, pcm_get_position(reader));
rc = frames_written; rc = frames_written;
END: END:
if (ibuf) free(ibuf); if (ibuf) free(ibuf);
if (obuf) free(obuf); if (obuf[0].data) free(obuf[0].data);
if (obuf[1].data) free(obuf[1].data);
return rc; return rc;
} }
@ -610,7 +631,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;
} }
@ -654,7 +675,7 @@ int parse_raw_spec(const char *spec, pcm_sample_description_t *desc)
static pcm_io_vtbl_t pcm_io_vtbl = { static pcm_io_vtbl_t pcm_io_vtbl = {
read_callback, seek_callback, tell_callback read_callback, seek_callback, tell_callback
}; };
static pcm_io_vtbl_t pcm_io_vtbl_noseek = { read_callback, 0, 0 }; static pcm_io_vtbl_t pcm_io_vtbl_noseek = { read_callback, 0, tell_callback };
static static
pcm_reader_t *open_input(aacenc_param_ex_t *params) pcm_reader_t *open_input(aacenc_param_ex_t *params)
@ -669,7 +690,7 @@ pcm_reader_t *open_input(aacenc_param_ex_t *params)
goto END; goto END;
} }
io.cookie = params->input_fp; io.cookie = params->input_fp;
if (fstat(fileno(io.cookie), &stb) == 0 if (fstat(fileno(params->input_fp), &stb) == 0
&& (stb.st_mode & S_IFMT) == S_IFREG) && (stb.st_mode & S_IFMT) == S_IFREG)
io.vtbl = &pcm_io_vtbl; io.vtbl = &pcm_io_vtbl;
else else
@ -712,10 +733,13 @@ pcm_reader_t *open_input(aacenc_param_ex_t *params)
} }
break; break;
default: default:
fprintf(stderr, "ERROR: unsupported input file\n");
goto END; goto END;
} }
} }
return pcm_open_sint16_converter(reader); if ((reader = pcm_open_sint16_converter(reader)) != 0)
reader = extrapolater_open(reader);
return reader;
END: END:
return 0; return 0;
} }
@ -736,6 +760,7 @@ int main(int argc, char **argv)
const pcm_sample_description_t *sample_format; const pcm_sample_description_t *sample_format;
int downsampled_timescale = 0; int downsampled_timescale = 0;
int frame_count = 0; int frame_count = 0;
int sbr_mode = 0;
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
setbuf(stderr, 0); setbuf(stderr, 0);
@ -748,6 +773,17 @@ int main(int argc, char **argv)
sample_format = pcm_get_format(reader); sample_format = pcm_get_format(reader);
/*
* We use explicit/hierarchical signaling for LOAS.
* Other than that, we request implicit signaling to FDK library, then
* append explicit/backward-compatible signaling to ASC in case of MP4FF.
*
* Explicit/backward-compatible signaling of SBR is the most recommended
* way in MPEG4 part3 spec, and seems the only way supported by iTunes.
* Since FDK library does not support it, we have to do it on our side.
*/
params.sbr_signaling = (params.transport_format == TT_MP4_LOAS) ? 2 : 0;
if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format, if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format,
&aacinfo) < 0) &aacinfo) < 0)
goto END; goto END;
@ -764,34 +800,51 @@ int main(int argc, char **argv)
goto END; goto END;
} }
handle_signals(); handle_signals();
sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)&params);
if (!params.transport_format) { if (!params.transport_format) {
uint32_t scale; uint32_t scale;
uint8_t mp4asc[32];
uint32_t ascsize = sizeof(mp4asc);
unsigned framelen = aacinfo.frameLength; unsigned framelen = aacinfo.frameLength;
int sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)&params); if (sbr_mode)
int sig_mode = aacEncoder_GetParam(encoder, AACENC_SIGNALING_MODE);
if (sbr_mode && !sig_mode)
downsampled_timescale = 1; downsampled_timescale = 1;
scale = sample_format->sample_rate >> downsampled_timescale; scale = sample_format->sample_rate >> downsampled_timescale;
if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io, if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io,
params.output_fp)) < 0) params.output_fp)) < 0)
goto END; goto END;
m4af_set_decoder_specific_info(m4af, 0, aacinfo.confBuf, aacenc_mp4asc((aacenc_param_t*)&params, aacinfo.confBuf,
aacinfo.confSize); aacinfo.confSize, mp4asc, &ascsize);
m4af_set_decoder_specific_info(m4af, 0, mp4asc, ascsize);
m4af_set_fixed_frame_duration(m4af, 0, m4af_set_fixed_frame_duration(m4af, 0,
framelen >> downsampled_timescale); framelen >> downsampled_timescale);
m4af_set_vbr_mode(m4af, 0, params.bitrate_mode); 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(reader, encoder, aacinfo.frameLength, if (sbr_mode && (aacinfo.encoderDelay & 1)) {
params.output_fp, m4af, !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;
uint32_t padding;
int64_t frames_read = pcm_get_position(reader); int64_t frames_read = pcm_get_position(reader);
uint32_t padding = frame_count * aacinfo.frameLength
- frames_read - aacinfo.encoderDelay; if (sbr_mode && params.profile != AOT_ER_AAC_ELD &&
!params.include_sbr_delay)
delay -= 481 << 1;
if (sbr_mode && (delay & 1))
++delay;
padding = frame_count * aacinfo.frameLength - frames_read - delay;
m4af_set_priming(m4af, 0, delay >> downsampled_timescale, m4af_set_priming(m4af, 0, delay >> downsampled_timescale,
padding >> downsampled_timescale); padding >> downsampled_timescale);
if (finalize_m4a(m4af, &params, encoder) < 0) if (finalize_m4a(m4af, &params, encoder) < 0)

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
@ -56,7 +60,8 @@ static tag_key_mapping_t tag_mapping_table[] = {
{ "grouping", M4AF_TAG_GROUPING }, { "grouping", M4AF_TAG_GROUPING },
{ "itunescompilation", M4AF_TAG_COMPILATION }, { "itunescompilation", M4AF_TAG_COMPILATION },
{ "lyrics", M4AF_TAG_LYRICS }, { "lyrics", M4AF_TAG_LYRICS },
{ "performer", M4AF_TAG_ARTIST }, { "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') },

View File

@ -1,3 +1,7 @@
/*
* 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

View File

@ -1,3 +1,7 @@
/*
* Copyright (C) 2013 nu774
* For conditions of distribution and use, see copyright notice in COPYING
*/
#ifndef PCM_READER_H #ifndef PCM_READER_H
#define PCM_READER_H #define PCM_READER_H
@ -85,7 +89,7 @@ uint32_t bitcount(uint32_t bits)
int pcm_read(pcm_io_context_t *io, void *buffer, uint32_t size); int pcm_read(pcm_io_context_t *io, void *buffer, uint32_t size);
int pcm_skip(pcm_io_context_t *io, int64_t count); int pcm_skip(pcm_io_context_t *io, int64_t count);
static int pcm_seek(pcm_io_context_t *io, int64_t off, int whence) 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; return io->vtbl->seek ? io->vtbl->seek(io->cookie, off, whence) : -1;
} }
@ -109,4 +113,6 @@ int apple_chan_chunk(pcm_io_context_t *io, uint32_t chunk_size,
pcm_reader_t *pcm_open_sint16_converter(pcm_reader_t *reader); pcm_reader_t *pcm_open_sint16_converter(pcm_reader_t *reader);
pcm_reader_t *extrapolater_open(pcm_reader_t *reader);
#endif #endif

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

View File

@ -93,7 +93,6 @@ int riff_ds64(wav_reader_t *reader, int64_t *length)
TRY_IO(pcm_scanl(&reader->io, "QQQL", TRY_IO(pcm_scanl(&reader->io, "QQQL",
&riff_size, length, &sample_count, &table_size) != 4); &riff_size, length, &sample_count, &table_size) != 4);
TRY_IO(pcm_skip(&reader->io, (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;
} }
@ -163,7 +162,6 @@ int wav_parse(wav_reader_t *reader, int64_t *data_length)
container == RIFF_FOURCC('R','F','6','4')); container == RIFF_FOURCC('R','F','6','4'));
TRY_IO(pcm_read32le(&reader->io, &fcc)); TRY_IO(pcm_read32le(&reader->io, &fcc));
ENSURE(fcc == RIFF_FOURCC('W','A','V','E')); ENSURE(fcc == RIFF_FOURCC('W','A','V','E'));
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);
@ -174,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(pcm_skip(&reader->io, (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;

View File

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