Reduces the decoder stack use by removing the pcm_silk buffer in fixed-point

We only keep when concealing less than 10ms with SILK.
This commit is contained in:
Jean-Marc Valin 2014-01-06 17:43:20 -05:00
parent 14ca4ed682
commit 4d07b1357e
4 changed files with 79 additions and 36 deletions

View file

@ -108,6 +108,10 @@ typedef opus_val32 celt_ener;
#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) #define ABS16(x) ((x) < 0 ? (-(x)) : (x))
#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) #define ABS32(x) ((x) < 0 ? (-(x)) : (x))
static OPUS_INLINE opus_int16 SAT16(opus_int32 x) {
return x > 32767 ? 32767 : x < -32768 ? -32768 : (opus_int16)x;
}
#ifdef FIXED_DEBUG #ifdef FIXED_DEBUG
#include "fixed_debug.h" #include "fixed_debug.h"
#else #else

View file

@ -134,7 +134,8 @@ int celt_decoder_get_size(int channels);
int celt_decoder_init(CELTDecoder *st, opus_int32 sampling_rate, int channels); int celt_decoder_init(CELTDecoder *st, opus_int32 sampling_rate, int channels);
int celt_decode_with_ec(OpusCustomDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_val16 * OPUS_RESTRICT pcm, int frame_size, ec_dec *dec); int celt_decode_with_ec(OpusCustomDecoder * OPUS_RESTRICT st, const unsigned char *data,
int len, opus_val16 * OPUS_RESTRICT pcm, int frame_size, ec_dec *dec, int accum);
#define celt_encoder_ctl opus_custom_encoder_ctl #define celt_encoder_ctl opus_custom_encoder_ctl
#define celt_decoder_ctl opus_custom_decoder_ctl #define celt_decoder_ctl opus_custom_decoder_ctl

View file

@ -191,7 +191,7 @@ static OPUS_INLINE opus_val16 SIG2WORD16(celt_sig x)
static static
#endif #endif
void deemphasis(celt_sig *in[], opus_val16 *pcm, int N, int C, int downsample, const opus_val16 *coef, void deemphasis(celt_sig *in[], opus_val16 *pcm, int N, int C, int downsample, const opus_val16 *coef,
celt_sig *mem) celt_sig *mem, int accum)
{ {
int c; int c;
int Nd; int Nd;
@ -199,7 +199,10 @@ void deemphasis(celt_sig *in[], opus_val16 *pcm, int N, int C, int downsample, c
opus_val16 coef0; opus_val16 coef0;
VARDECL(celt_sig, scratch); VARDECL(celt_sig, scratch);
SAVE_STACK; SAVE_STACK;
#ifndef FIXED_POINT
(void)accum;
celt_assert(accum==0);
#endif
ALLOC(scratch, N, celt_sig); ALLOC(scratch, N, celt_sig);
coef0 = coef[0]; coef0 = coef[0];
Nd = N/downsample; Nd = N/downsample;
@ -238,11 +241,24 @@ void deemphasis(celt_sig *in[], opus_val16 *pcm, int N, int C, int downsample, c
apply_downsampling=1; apply_downsampling=1;
} else { } else {
/* Shortcut for the standard (non-custom modes) case */ /* Shortcut for the standard (non-custom modes) case */
for (j=0;j<N;j++) #ifdef FIXED_POINT
if (accum)
{ {
celt_sig tmp = x[j] + m + VERY_SMALL; for (j=0;j<N;j++)
m = MULT16_32_Q15(coef0, tmp); {
y[j*C] = SCALEOUT(SIG2WORD16(tmp)); celt_sig tmp = x[j] + m + VERY_SMALL;
m = MULT16_32_Q15(coef0, tmp);
y[j*C] = SAT16(ADD32(y[j*C], SCALEOUT(SIG2WORD16(tmp))));
}
} else
#endif
{
for (j=0;j<N;j++)
{
celt_sig tmp = x[j] + m + VERY_SMALL;
m = MULT16_32_Q15(coef0, tmp);
y[j*C] = SCALEOUT(SIG2WORD16(tmp));
}
} }
} }
mem[c] = m; mem[c] = m;
@ -250,8 +266,17 @@ void deemphasis(celt_sig *in[], opus_val16 *pcm, int N, int C, int downsample, c
if (apply_downsampling) if (apply_downsampling)
{ {
/* Perform down-sampling */ /* Perform down-sampling */
for (j=0;j<Nd;j++) #ifdef FIXED_POINT
y[j*C] = SCALEOUT(SIG2WORD16(scratch[j*downsample])); if (accum)
{
for (j=0;j<Nd;j++)
y[j*C] = SAT16(ADD32(y[j*C], SCALEOUT(SIG2WORD16(scratch[j*downsample]))));
} else
#endif
{
for (j=0;j<Nd;j++)
y[j*C] = SCALEOUT(SIG2WORD16(scratch[j*downsample]));
}
} }
} while (++c<C); } while (++c<C);
RESTORE_STACK; RESTORE_STACK;
@ -378,7 +403,8 @@ static void tf_decode(int start, int end, int isTransient, int *tf_res, int LM,
pitch of 480 Hz. */ pitch of 480 Hz. */
#define PLC_PITCH_LAG_MIN (100) #define PLC_PITCH_LAG_MIN (100)
static void celt_decode_lost(CELTDecoder * OPUS_RESTRICT st, opus_val16 * OPUS_RESTRICT pcm, int N, int LM) static void celt_decode_lost(CELTDecoder * OPUS_RESTRICT st, opus_val16 * OPUS_RESTRICT pcm,
int N, int LM, int accum)
{ {
int c; int c;
int i; int i;
@ -680,15 +706,15 @@ static void celt_decode_lost(CELTDecoder * OPUS_RESTRICT st, opus_val16 * OPUS_R
} while (++c<C); } while (++c<C);
} }
deemphasis(out_syn, pcm, N, C, downsample, deemphasis(out_syn, pcm, N, C, downsample, mode->preemph, st->preemph_memD, accum);
mode->preemph, st->preemph_memD);
st->loss_count = loss_count+1; st->loss_count = loss_count+1;
RESTORE_STACK; RESTORE_STACK;
} }
int celt_decode_with_ec(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_val16 * OPUS_RESTRICT pcm, int frame_size, ec_dec *dec) int celt_decode_with_ec(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data,
int len, opus_val16 * OPUS_RESTRICT pcm, int frame_size, ec_dec *dec, int accum)
{ {
int c, i, N; int c, i, N;
int spread_decision; int spread_decision;
@ -803,7 +829,7 @@ int celt_decode_with_ec(CELTDecoder * OPUS_RESTRICT st, const unsigned char *dat
if (data == NULL || len<=1) if (data == NULL || len<=1)
{ {
celt_decode_lost(st, pcm, N, LM); celt_decode_lost(st, pcm, N, LM, accum);
RESTORE_STACK; RESTORE_STACK;
return frame_size/st->downsample; return frame_size/st->downsample;
} }
@ -1030,7 +1056,7 @@ int celt_decode_with_ec(CELTDecoder * OPUS_RESTRICT st, const unsigned char *dat
st->rng = dec->rng; st->rng = dec->rng;
/* We reuse freq[] as scratch space for the de-emphasis */ /* We reuse freq[] as scratch space for the de-emphasis */
deemphasis(out_syn, pcm, N, CC, st->downsample, mode->preemph, st->preemph_memD); deemphasis(out_syn, pcm, N, CC, st->downsample, mode->preemph, st->preemph_memD, accum);
st->loss_count = 0; st->loss_count = 0;
RESTORE_STACK; RESTORE_STACK;
if (ec_tell(dec) > 8*len) if (ec_tell(dec) > 8*len)
@ -1046,7 +1072,7 @@ int celt_decode_with_ec(CELTDecoder * OPUS_RESTRICT st, const unsigned char *dat
#ifdef FIXED_POINT #ifdef FIXED_POINT
int opus_custom_decode(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_int16 * OPUS_RESTRICT pcm, int frame_size) int opus_custom_decode(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_int16 * OPUS_RESTRICT pcm, int frame_size)
{ {
return celt_decode_with_ec(st, data, len, pcm, frame_size, NULL); return celt_decode_with_ec(st, data, len, pcm, frame_size, NULL, 0);
} }
#ifndef DISABLE_FLOAT_API #ifndef DISABLE_FLOAT_API
@ -1063,7 +1089,7 @@ int opus_custom_decode_float(CELTDecoder * OPUS_RESTRICT st, const unsigned char
N = frame_size; N = frame_size;
ALLOC(out, C*N, opus_int16); ALLOC(out, C*N, opus_int16);
ret=celt_decode_with_ec(st, data, len, out, frame_size, NULL); ret=celt_decode_with_ec(st, data, len, out, frame_size, NULL, 0);
if (ret>0) if (ret>0)
for (j=0;j<C*ret;j++) for (j=0;j<C*ret;j++)
pcm[j]=out[j]*(1.f/32768.f); pcm[j]=out[j]*(1.f/32768.f);
@ -1077,7 +1103,7 @@ int opus_custom_decode_float(CELTDecoder * OPUS_RESTRICT st, const unsigned char
int opus_custom_decode_float(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, float * OPUS_RESTRICT pcm, int frame_size) int opus_custom_decode_float(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, float * OPUS_RESTRICT pcm, int frame_size)
{ {
return celt_decode_with_ec(st, data, len, pcm, frame_size, NULL); return celt_decode_with_ec(st, data, len, pcm, frame_size, NULL, 0);
} }
int opus_custom_decode(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_int16 * OPUS_RESTRICT pcm, int frame_size) int opus_custom_decode(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_int16 * OPUS_RESTRICT pcm, int frame_size)
@ -1093,7 +1119,7 @@ int opus_custom_decode(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data
N = frame_size; N = frame_size;
ALLOC(out, C*N, celt_sig); ALLOC(out, C*N, celt_sig);
ret=celt_decode_with_ec(st, data, len, out, frame_size, NULL); ret=celt_decode_with_ec(st, data, len, out, frame_size, NULL, 0);
if (ret>0) if (ret>0)
for (j=0;j<C*ret;j++) for (j=0;j<C*ret;j++)

View file

@ -77,12 +77,6 @@ struct OpusDecoder {
opus_uint32 rangeFinal; opus_uint32 rangeFinal;
}; };
#ifdef FIXED_POINT
static OPUS_INLINE opus_int16 SAT16(opus_int32 x) {
return x > 32767 ? 32767 : x < -32768 ? -32768 : (opus_int16)x;
}
#endif
int opus_decoder_get_size(int channels) int opus_decoder_get_size(int channels)
{ {
@ -215,7 +209,7 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
VARDECL(opus_val16, pcm_transition_silk); VARDECL(opus_val16, pcm_transition_silk);
int pcm_transition_celt_size; int pcm_transition_celt_size;
VARDECL(opus_val16, pcm_transition_celt); VARDECL(opus_val16, pcm_transition_celt);
opus_val16 *pcm_transition; opus_val16 *pcm_transition=NULL;
int redundant_audio_size; int redundant_audio_size;
VARDECL(opus_val16, redundant_audio); VARDECL(opus_val16, redundant_audio);
@ -230,6 +224,7 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
int F2_5, F5, F10, F20; int F2_5, F5, F10, F20;
const opus_val16 *window; const opus_val16 *window;
opus_uint32 redundant_rng = 0; opus_uint32 redundant_rng = 0;
int celt_accum;
ALLOC_STACK; ALLOC_STACK;
silk_dec = (char*)st+st->silk_dec_offset; silk_dec = (char*)st+st->silk_dec_offset;
@ -295,6 +290,14 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
} }
} }
/* In fixed-point, we can tell CELT to do the accumulation on top of the
SILK PCM buffer. This saves some stack space. */
#ifdef FIXED_POINT
celt_accum = (mode != MODE_CELT_ONLY) && (frame_size >= F10);
#else
celt_accum = 0;
#endif
pcm_transition_silk_size = ALLOC_NONE; pcm_transition_silk_size = ALLOC_NONE;
pcm_transition_celt_size = ALLOC_NONE; pcm_transition_celt_size = ALLOC_NONE;
if (data!=NULL && st->prev_mode > 0 && ( if (data!=NULL && st->prev_mode > 0 && (
@ -325,14 +328,20 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
} }
/* Don't allocate any memory when in CELT-only mode */ /* Don't allocate any memory when in CELT-only mode */
pcm_silk_size = (mode != MODE_CELT_ONLY) ? IMAX(F10, frame_size)*st->channels : ALLOC_NONE; pcm_silk_size = (mode != MODE_CELT_ONLY && !celt_accum) ? IMAX(F10, frame_size)*st->channels : ALLOC_NONE;
ALLOC(pcm_silk, pcm_silk_size, opus_int16); ALLOC(pcm_silk, pcm_silk_size, opus_int16);
/* SILK processing */ /* SILK processing */
if (mode != MODE_CELT_ONLY) if (mode != MODE_CELT_ONLY)
{ {
int lost_flag, decoded_samples; int lost_flag, decoded_samples;
opus_int16 *pcm_ptr = pcm_silk; opus_int16 *pcm_ptr;
#ifdef FIXED_POINT
if (celt_accum)
pcm_ptr = pcm;
else
#endif
pcm_ptr = pcm_silk;
if (st->prev_mode==MODE_CELT_ONLY) if (st->prev_mode==MODE_CELT_ONLY)
silk_InitDecoder( silk_dec ); silk_InitDecoder( silk_dec );
@ -462,7 +471,7 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
{ {
celt_decoder_ctl(celt_dec, CELT_SET_START_BAND(0)); celt_decoder_ctl(celt_dec, CELT_SET_START_BAND(0));
celt_decode_with_ec(celt_dec, data+len, redundancy_bytes, celt_decode_with_ec(celt_dec, data+len, redundancy_bytes,
redundant_audio, F5, NULL); redundant_audio, F5, NULL, 0);
celt_decoder_ctl(celt_dec, OPUS_GET_FINAL_RANGE(&redundant_rng)); celt_decoder_ctl(celt_dec, OPUS_GET_FINAL_RANGE(&redundant_rng));
} }
@ -477,25 +486,28 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
celt_decoder_ctl(celt_dec, OPUS_RESET_STATE); celt_decoder_ctl(celt_dec, OPUS_RESET_STATE);
/* Decode CELT */ /* Decode CELT */
celt_ret = celt_decode_with_ec(celt_dec, decode_fec ? NULL : data, celt_ret = celt_decode_with_ec(celt_dec, decode_fec ? NULL : data,
len, pcm, celt_frame_size, &dec); len, pcm, celt_frame_size, &dec, celt_accum);
} else { } else {
unsigned char silence[2] = {0xFF, 0xFF}; unsigned char silence[2] = {0xFF, 0xFF};
for (i=0;i<frame_size*st->channels;i++) if (!celt_accum)
pcm[i] = 0; {
for (i=0;i<frame_size*st->channels;i++)
pcm[i] = 0;
}
/* For hybrid -> SILK transitions, we let the CELT MDCT /* For hybrid -> SILK transitions, we let the CELT MDCT
do a fade-out by decoding a silence frame */ do a fade-out by decoding a silence frame */
if (st->prev_mode == MODE_HYBRID && !(redundancy && celt_to_silk && st->prev_redundancy) ) if (st->prev_mode == MODE_HYBRID && !(redundancy && celt_to_silk && st->prev_redundancy) )
{ {
celt_decoder_ctl(celt_dec, CELT_SET_START_BAND(0)); celt_decoder_ctl(celt_dec, CELT_SET_START_BAND(0));
celt_decode_with_ec(celt_dec, silence, 2, pcm, F2_5, NULL); celt_decode_with_ec(celt_dec, silence, 2, pcm, F2_5, NULL, celt_accum);
} }
} }
if (mode != MODE_CELT_ONLY) if (mode != MODE_CELT_ONLY && !celt_accum)
{ {
#ifdef FIXED_POINT #ifdef FIXED_POINT
for (i=0;i<frame_size*st->channels;i++) for (i=0;i<frame_size*st->channels;i++)
pcm[i] = SAT16(pcm[i] + pcm_silk[i]); pcm[i] = SAT16(ADD32(pcm[i], pcm_silk[i]));
#else #else
for (i=0;i<frame_size*st->channels;i++) for (i=0;i<frame_size*st->channels;i++)
pcm[i] = pcm[i] + (opus_val16)((1.f/32768.f)*pcm_silk[i]); pcm[i] = pcm[i] + (opus_val16)((1.f/32768.f)*pcm_silk[i]);
@ -514,7 +526,7 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
celt_decoder_ctl(celt_dec, OPUS_RESET_STATE); celt_decoder_ctl(celt_dec, OPUS_RESET_STATE);
celt_decoder_ctl(celt_dec, CELT_SET_START_BAND(0)); celt_decoder_ctl(celt_dec, CELT_SET_START_BAND(0));
celt_decode_with_ec(celt_dec, data+len, redundancy_bytes, redundant_audio, F5, NULL); celt_decode_with_ec(celt_dec, data+len, redundancy_bytes, redundant_audio, F5, NULL, 0);
celt_decoder_ctl(celt_dec, OPUS_GET_FINAL_RANGE(&redundant_rng)); celt_decoder_ctl(celt_dec, OPUS_GET_FINAL_RANGE(&redundant_rng));
smooth_fade(pcm+st->channels*(frame_size-F2_5), redundant_audio+st->channels*F2_5, smooth_fade(pcm+st->channels*(frame_size-F2_5), redundant_audio+st->channels*F2_5,
pcm+st->channels*(frame_size-F2_5), F2_5, st->channels, window, st->Fs); pcm+st->channels*(frame_size-F2_5), F2_5, st->channels, window, st->Fs);