blob: 1c0fd42383721f544b24c7a4978483cfd254e6dd [file] [log] [blame]
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
WebRtcIlbcfix_EnhancerInterface.c
******************************************************************/
#include <string.h>
#include "defines.h"
#include "constants.h"
#include "xcorr_coef.h"
#include "enhancer.h"
#include "hp_output.h"
/*----------------------------------------------------------------*
* interface for enhancer
*---------------------------------------------------------------*/
size_t WebRtcIlbcfix_EnhancerInterface( /* (o) Estimated lag in end of in[] */
int16_t *out, /* (o) enhanced signal */
int16_t *in, /* (i) unenhanced signal */
IlbcDecoder *iLBCdec_inst /* (i) buffers etc */
){
size_t iblock;
size_t lag=20, tlag=20;
size_t inLen=iLBCdec_inst->blockl+120;
int16_t scale, scale1;
size_t plc_blockl;
int16_t *enh_buf;
size_t *enh_period;
int32_t tmp1, tmp2, max;
size_t new_blocks;
int16_t *enh_bufPtr1;
size_t i;
size_t k;
int16_t EnChange;
int16_t SqrtEnChange;
int16_t inc;
int16_t win;
int16_t *tmpW16ptr;
size_t startPos;
int16_t *plc_pred;
int16_t *target, *regressor;
int16_t max16;
int shifts;
int32_t ener;
int16_t enerSh;
int16_t corrSh;
size_t ind;
int16_t sh;
size_t start, stop;
/* Stack based */
int16_t totsh[3];
int16_t downsampled[(BLOCKL_MAX+120)>>1]; /* length 180 */
int32_t corr32[50];
int32_t corrmax[3];
int16_t corr16[3];
int16_t en16[3];
size_t lagmax[3];
plc_pred = downsampled; /* Reuse memory since plc_pred[ENH_BLOCKL] and
downsampled are non overlapping */
enh_buf=iLBCdec_inst->enh_buf;
enh_period=iLBCdec_inst->enh_period;
/* Copy in the new data into the enhancer buffer */
memmove(enh_buf, &enh_buf[iLBCdec_inst->blockl],
(ENH_BUFL - iLBCdec_inst->blockl) * sizeof(*enh_buf));
WEBRTC_SPL_MEMCPY_W16(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl], in,
iLBCdec_inst->blockl);
/* Set variables that are dependent on frame size */
if (iLBCdec_inst->mode==30) {
plc_blockl=ENH_BLOCKL;
new_blocks=3;
startPos=320; /* Start position for enhancement
(640-new_blocks*ENH_BLOCKL-80) */
} else {
plc_blockl=40;
new_blocks=2;
startPos=440; /* Start position for enhancement
(640-new_blocks*ENH_BLOCKL-40) */
}
/* Update the pitch prediction for each enhancer block, move the old ones */
memmove(enh_period, &enh_period[new_blocks],
(ENH_NBLOCKS_TOT - new_blocks) * sizeof(*enh_period));
WebRtcSpl_DownsampleFast(
enh_buf+ENH_BUFL-inLen, /* Input samples */
inLen + ENH_BUFL_FILTEROVERHEAD,
downsampled,
inLen / 2,
(int16_t*)WebRtcIlbcfix_kLpFiltCoefs, /* Coefficients in Q12 */
FILTERORDER_DS_PLUS1, /* Length of filter (order-1) */
FACTOR_DS,
DELAY_DS);
/* Estimate the pitch in the down sampled domain. */
for(iblock = 0; iblock<new_blocks; iblock++){
/* references */
target = downsampled + 60 + iblock * ENH_BLOCKL_HALF;
regressor = target - 10;
/* scaling */
max16 = WebRtcSpl_MaxAbsValueW16(&regressor[-50], ENH_BLOCKL_HALF + 50 - 1);
shifts = WebRtcSpl_GetSizeInBits((uint32_t)(max16 * max16)) - 25;
shifts = WEBRTC_SPL_MAX(0, shifts);
/* compute cross correlation */
WebRtcSpl_CrossCorrelation(corr32, target, regressor, ENH_BLOCKL_HALF, 50,
shifts, -1);
/* Find 3 highest correlations that should be compared for the
highest (corr*corr)/ener */
for (i=0;i<2;i++) {
lagmax[i] = WebRtcSpl_MaxIndexW32(corr32, 50);
corrmax[i] = corr32[lagmax[i]];
start = WEBRTC_SPL_MAX(2, lagmax[i]) - 2;
stop = WEBRTC_SPL_MIN(47, lagmax[i]) + 2;
for (k = start; k <= stop; k++) {
corr32[k] = 0;
}
}
lagmax[2] = WebRtcSpl_MaxIndexW32(corr32, 50);
corrmax[2] = corr32[lagmax[2]];
/* Calculate normalized corr^2 and ener */
for (i=0;i<3;i++) {
corrSh = 15-WebRtcSpl_GetSizeInBits(corrmax[i]);
ener = WebRtcSpl_DotProductWithScale(regressor - lagmax[i],
regressor - lagmax[i],
ENH_BLOCKL_HALF, shifts);
enerSh = 15-WebRtcSpl_GetSizeInBits(ener);
corr16[i] = (int16_t)WEBRTC_SPL_SHIFT_W32(corrmax[i], corrSh);
corr16[i] = (int16_t)((corr16[i] * corr16[i]) >> 16);
en16[i] = (int16_t)WEBRTC_SPL_SHIFT_W32(ener, enerSh);
totsh[i] = enerSh - (corrSh << 1);
}
/* Compare lagmax[0..3] for the (corr^2)/ener criteria */
ind = 0;
for (i=1; i<3; i++) {
if (totsh[ind] > totsh[i]) {
sh = WEBRTC_SPL_MIN(31, totsh[ind]-totsh[i]);
if (corr16[ind] * en16[i] < (corr16[i] * en16[ind]) >> sh) {
ind = i;
}
} else {
sh = WEBRTC_SPL_MIN(31, totsh[i]-totsh[ind]);
if ((corr16[ind] * en16[i]) >> sh < corr16[i] * en16[ind]) {
ind = i;
}
}
}
lag = lagmax[ind] + 10;
/* Store the estimated lag in the non-downsampled domain */
enh_period[ENH_NBLOCKS_TOT - new_blocks + iblock] = lag * 8;
/* Store the estimated lag for backward PLC */
if (iLBCdec_inst->prev_enh_pl==1) {
if (!iblock) {
tlag = lag * 2;
}
} else {
if (iblock==1) {
tlag = lag * 2;
}
}
lag *= 2;
}
if ((iLBCdec_inst->prev_enh_pl==1)||(iLBCdec_inst->prev_enh_pl==2)) {
/* Calculate the best lag of the new frame
This is used to interpolate backwards and mix with the PLC'd data
*/
/* references */
target=in;
regressor=in+tlag-1;
/* scaling */
max16 = WebRtcSpl_MaxAbsValueW16(regressor, plc_blockl + 3 - 1);
if (max16>5000)
shifts=2;
else
shifts=0;
/* compute cross correlation */
WebRtcSpl_CrossCorrelation(corr32, target, regressor, plc_blockl, 3, shifts,
1);
/* find lag */
lag=WebRtcSpl_MaxIndexW32(corr32, 3);
lag+=tlag-1;
/* Copy the backward PLC to plc_pred */
if (iLBCdec_inst->prev_enh_pl==1) {
if (lag>plc_blockl) {
WEBRTC_SPL_MEMCPY_W16(plc_pred, &in[lag-plc_blockl], plc_blockl);
} else {
WEBRTC_SPL_MEMCPY_W16(&plc_pred[plc_blockl-lag], in, lag);
WEBRTC_SPL_MEMCPY_W16(
plc_pred, &enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl+lag],
(plc_blockl-lag));
}
} else {
size_t pos;
pos = plc_blockl;
while (lag<pos) {
WEBRTC_SPL_MEMCPY_W16(&plc_pred[pos-lag], in, lag);
pos = pos - lag;
}
WEBRTC_SPL_MEMCPY_W16(plc_pred, &in[lag-pos], pos);
}
if (iLBCdec_inst->prev_enh_pl==1) {
/* limit energy change
if energy in backward PLC is more than 4 times higher than the forward
PLC, then reduce the energy in the backward PLC vector:
sample 1...len-16 set energy of the to 4 times forward PLC
sample len-15..len interpolate between 4 times fw PLC and bw PLC energy
Note: Compared to floating point code there is a slight change,
the window is 16 samples long instead of 10 samples to simplify the
calculations
*/
max=WebRtcSpl_MaxAbsValueW16(
&enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl], plc_blockl);
max16=WebRtcSpl_MaxAbsValueW16(plc_pred, plc_blockl);
max = WEBRTC_SPL_MAX(max, max16);
scale=22-(int16_t)WebRtcSpl_NormW32(max);
scale=WEBRTC_SPL_MAX(scale,0);
tmp2 = WebRtcSpl_DotProductWithScale(
&enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl],
&enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl],
plc_blockl, scale);
tmp1 = WebRtcSpl_DotProductWithScale(plc_pred, plc_pred,
plc_blockl, scale);
/* Check the energy difference */
if ((tmp1>0)&&((tmp1>>2)>tmp2)) {
/* EnChange is now guaranteed to be <0.5
Calculate EnChange=tmp2/tmp1 in Q16
*/
scale1=(int16_t)WebRtcSpl_NormW32(tmp1);
tmp1=WEBRTC_SPL_SHIFT_W32(tmp1, (scale1-16)); /* using 15 bits */
tmp2=WEBRTC_SPL_SHIFT_W32(tmp2, (scale1));
EnChange = (int16_t)WebRtcSpl_DivW32W16(tmp2,
(int16_t)tmp1);
/* Calculate the Sqrt of the energy in Q15 ((14+16)/2) */
SqrtEnChange = (int16_t)WebRtcSpl_SqrtFloor(EnChange << 14);
/* Multiply first part of vector with 2*SqrtEnChange */
WebRtcSpl_ScaleVector(plc_pred, plc_pred, SqrtEnChange, plc_blockl-16,
14);
/* Calculate increase parameter for window part (16 last samples) */
/* (1-2*SqrtEnChange)/16 in Q15 */
inc = 2048 - (SqrtEnChange >> 3);
win=0;
tmpW16ptr=&plc_pred[plc_blockl-16];
for (i=16;i>0;i--) {
*tmpW16ptr = (int16_t)(
(*tmpW16ptr * (SqrtEnChange + (win >> 1))) >> 14);
/* multiply by (2.0*SqrtEnChange+win) */
win += inc;
tmpW16ptr++;
}
}
/* Make the linear interpolation between the forward PLC'd data
and the backward PLC'd data (from the new frame)
*/
if (plc_blockl==40) {
inc=400; /* 1/41 in Q14 */
} else { /* plc_blockl==80 */
inc=202; /* 1/81 in Q14 */
}
win=0;
enh_bufPtr1=&enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl];
for (i=0; i<plc_blockl; i++) {
win+=inc;
*enh_bufPtr1 = (int16_t)((*enh_bufPtr1 * win) >> 14);
*enh_bufPtr1 += (int16_t)(
((16384 - win) * plc_pred[plc_blockl - 1 - i]) >> 14);
enh_bufPtr1--;
}
} else {
int16_t *synt = &downsampled[LPC_FILTERORDER];
enh_bufPtr1=&enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl];
WEBRTC_SPL_MEMCPY_W16(enh_bufPtr1, plc_pred, plc_blockl);
/* Clear fileter memory */
WebRtcSpl_MemSetW16(iLBCdec_inst->syntMem, 0, LPC_FILTERORDER);
WebRtcSpl_MemSetW16(iLBCdec_inst->hpimemy, 0, 4);
WebRtcSpl_MemSetW16(iLBCdec_inst->hpimemx, 0, 2);
/* Initialize filter memory by filtering through 2 lags */
WEBRTC_SPL_MEMCPY_W16(&synt[-LPC_FILTERORDER], iLBCdec_inst->syntMem,
LPC_FILTERORDER);
WebRtcSpl_FilterARFastQ12(
enh_bufPtr1,
synt,
&iLBCdec_inst->old_syntdenum[
(iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1)],
LPC_FILTERORDER+1, lag);
WEBRTC_SPL_MEMCPY_W16(&synt[-LPC_FILTERORDER], &synt[lag-LPC_FILTERORDER],
LPC_FILTERORDER);
WebRtcIlbcfix_HpOutput(synt, (int16_t*)WebRtcIlbcfix_kHpOutCoefs,
iLBCdec_inst->hpimemy, iLBCdec_inst->hpimemx,
lag);
WebRtcSpl_FilterARFastQ12(
enh_bufPtr1, synt,
&iLBCdec_inst->old_syntdenum[
(iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1)],
LPC_FILTERORDER+1, lag);
WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->syntMem, &synt[lag-LPC_FILTERORDER],
LPC_FILTERORDER);
WebRtcIlbcfix_HpOutput(synt, (int16_t*)WebRtcIlbcfix_kHpOutCoefs,
iLBCdec_inst->hpimemy, iLBCdec_inst->hpimemx,
lag);
}
}
/* Perform enhancement block by block */
for (iblock = 0; iblock<new_blocks; iblock++) {
WebRtcIlbcfix_Enhancer(out + iblock * ENH_BLOCKL,
enh_buf,
ENH_BUFL,
iblock * ENH_BLOCKL + startPos,
enh_period,
WebRtcIlbcfix_kEnhPlocs, ENH_NBLOCKS_TOT);
}
return (lag);
}