blob: 6f0679c280747de53d14cbef2dd21bf9c5551023 [file] [log] [blame]
/*
** Copyright 2003-2010, VisualOn, Inc.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
/*******************************************************************************
File: psy_main.c
Content: Psychoacoustic major functions
*******************************************************************************/
#include "typedef.h"
#include "basic_op.h"
#include "oper_32b.h"
#include "psy_const.h"
#include "block_switch.h"
#include "transform.h"
#include "spreading.h"
#include "pre_echo_control.h"
#include "band_nrg.h"
#include "psy_configuration.h"
#include "psy_data.h"
#include "ms_stereo.h"
#include "interface.h"
#include "psy_main.h"
#include "grp_data.h"
#include "tns_func.h"
#include "memalign.h"
#define UNUSED(x) (void)(x)
/* long start short stop */
static Word16 blockType2windowShape[] = {KBD_WINDOW,SINE_WINDOW,SINE_WINDOW,KBD_WINDOW};
/*
forward definitions
*/
static Word16 advancePsychLong(PSY_DATA* psyData,
TNS_DATA* tnsData,
PSY_CONFIGURATION_LONG *hPsyConfLong,
PSY_OUT_CHANNEL* psyOutChannel,
Word32 *pScratchTns,
const TNS_DATA *tnsData2,
const Word16 ch);
static Word16 advancePsychLongMS (PSY_DATA psyData[MAX_CHANNELS],
const PSY_CONFIGURATION_LONG *hPsyConfLong);
static Word16 advancePsychShort(PSY_DATA* psyData,
TNS_DATA* tnsData,
const PSY_CONFIGURATION_SHORT *hPsyConfShort,
PSY_OUT_CHANNEL* psyOutChannel,
Word32 *pScratchTns,
const TNS_DATA *tnsData2,
const Word16 ch);
static Word16 advancePsychShortMS (PSY_DATA psyData[MAX_CHANNELS],
const PSY_CONFIGURATION_SHORT *hPsyConfShort);
/*****************************************************************************
*
* function name: PsyNew
* description: allocates memory for psychoacoustic
* returns: an error code
* input: pointer to a psych handle
*
*****************************************************************************/
Word16 PsyNew(PSY_KERNEL *hPsy, Word32 nChan, VO_MEM_OPERATOR *pMemOP)
{
Word16 i;
Word32 *mdctSpectrum;
Word32 *scratchTNS;
Word16 *mdctDelayBuffer;
mdctSpectrum = (Word32 *)mem_malloc(pMemOP, nChan * FRAME_LEN_LONG * sizeof(Word32), 32, VO_INDEX_ENC_AAC);
if(NULL == mdctSpectrum)
return 1;
scratchTNS = (Word32 *)mem_malloc(pMemOP, nChan * FRAME_LEN_LONG * sizeof(Word32), 32, VO_INDEX_ENC_AAC);
if(NULL == scratchTNS)
{
return 1;
}
mdctDelayBuffer = (Word16 *)mem_malloc(pMemOP, nChan * BLOCK_SWITCHING_OFFSET * sizeof(Word16), 32, VO_INDEX_ENC_AAC);
if(NULL == mdctDelayBuffer)
{
return 1;
}
for (i=0; i<nChan; i++){
hPsy->psyData[i].mdctDelayBuffer = mdctDelayBuffer + i*BLOCK_SWITCHING_OFFSET;
hPsy->psyData[i].mdctSpectrum = mdctSpectrum + i*FRAME_LEN_LONG;
}
hPsy->pScratchTns = scratchTNS;
return 0;
}
/*****************************************************************************
*
* function name: PsyDelete
* description: allocates memory for psychoacoustic
* returns: an error code
*
*****************************************************************************/
Word16 PsyDelete(PSY_KERNEL *hPsy, VO_MEM_OPERATOR *pMemOP)
{
Word32 nch;
if(hPsy)
{
if(hPsy->psyData[0].mdctDelayBuffer)
mem_free(pMemOP, hPsy->psyData[0].mdctDelayBuffer, VO_INDEX_ENC_AAC);
if(hPsy->psyData[0].mdctSpectrum)
mem_free(pMemOP, hPsy->psyData[0].mdctSpectrum, VO_INDEX_ENC_AAC);
for (nch=0; nch<MAX_CHANNELS; nch++){
hPsy->psyData[nch].mdctDelayBuffer = NULL;
hPsy->psyData[nch].mdctSpectrum = NULL;
}
if(hPsy->pScratchTns)
{
mem_free(pMemOP, hPsy->pScratchTns, VO_INDEX_ENC_AAC);
hPsy->pScratchTns = NULL;
}
}
return 0;
}
/*****************************************************************************
*
* function name: PsyOutNew
* description: allocates memory for psyOut struc
* returns: an error code
* input: pointer to a psych handle
*
*****************************************************************************/
Word16 PsyOutNew(PSY_OUT *hPsyOut, VO_MEM_OPERATOR *pMemOP)
{
pMemOP->Set(VO_INDEX_ENC_AAC, hPsyOut, 0, sizeof(PSY_OUT));
/*
alloc some more stuff, tbd
*/
return 0;
}
/*****************************************************************************
*
* function name: PsyOutDelete
* description: allocates memory for psychoacoustic
* returns: an error code
*
*****************************************************************************/
Word16 PsyOutDelete(PSY_OUT *hPsyOut, VO_MEM_OPERATOR *pMemOP)
{
UNUSED(hPsyOut);
UNUSED(pMemOP);
return 0;
}
/*****************************************************************************
*
* function name: psyMainInit
* description: initializes psychoacoustic
* returns: an error code
*
*****************************************************************************/
Word16 psyMainInit(PSY_KERNEL *hPsy,
Word32 sampleRate,
Word32 bitRate,
Word16 channels,
Word16 tnsMask,
Word16 bandwidth)
{
Word16 ch, err;
Word32 channelBitRate = bitRate/channels;
err = InitPsyConfigurationLong(channelBitRate,
sampleRate,
bandwidth,
&(hPsy->psyConfLong));
if (!err) {
hPsy->sampleRateIdx = hPsy->psyConfLong.sampRateIdx;
err = InitTnsConfigurationLong(bitRate, sampleRate, channels,
&hPsy->psyConfLong.tnsConf, &hPsy->psyConfLong, tnsMask&2);
}
if (!err)
err = InitPsyConfigurationShort(channelBitRate,
sampleRate,
bandwidth,
&hPsy->psyConfShort);
if (!err) {
err = InitTnsConfigurationShort(bitRate, sampleRate, channels,
&hPsy->psyConfShort.tnsConf, &hPsy->psyConfShort, tnsMask&1);
}
if (!err)
for(ch=0;ch < channels;ch++){
InitBlockSwitching(&hPsy->psyData[ch].blockSwitchingControl,
bitRate, channels);
InitPreEchoControl(hPsy->psyData[ch].sfbThresholdnm1,
hPsy->psyConfLong.sfbCnt,
hPsy->psyConfLong.sfbThresholdQuiet);
hPsy->psyData[ch].mdctScalenm1 = 0;
}
return(err);
}
/*****************************************************************************
*
* function name: psyMain
* description: psychoacoustic main function
* returns: an error code
*
* This function assumes that enough input data is in the modulo buffer.
*
*****************************************************************************/
Word16 psyMain(Word16 nChannels,
ELEMENT_INFO *elemInfo,
Word16 *timeSignal,
PSY_DATA psyData[MAX_CHANNELS],
TNS_DATA tnsData[MAX_CHANNELS],
PSY_CONFIGURATION_LONG *hPsyConfLong,
PSY_CONFIGURATION_SHORT *hPsyConfShort,
PSY_OUT_CHANNEL psyOutChannel[MAX_CHANNELS],
PSY_OUT_ELEMENT *psyOutElement,
Word32 *pScratchTns,
Word32 sampleRate)
{
Word16 maxSfbPerGroup[MAX_CHANNELS];
Word16 mdctScalingArray[MAX_CHANNELS];
Word16 ch; /* counts through channels */
Word16 sfb; /* counts through scalefactor bands */
Word16 line; /* counts through lines */
Word16 channels;
Word16 maxScale;
channels = elemInfo->nChannelsInEl;
maxScale = 0;
/* block switching */
for(ch = 0; ch < channels; ch++) {
BlockSwitching(&psyData[ch].blockSwitchingControl,
timeSignal+elemInfo->ChannelIndex[ch],
sampleRate,
nChannels);
}
/* synch left and right block type */
SyncBlockSwitching(&psyData[0].blockSwitchingControl,
&psyData[1].blockSwitchingControl,
channels);
/* transform
and get maxScale (max mdctScaling) for all channels */
for(ch=0; ch<channels; ch++) {
Transform_Real(psyData[ch].mdctDelayBuffer,
timeSignal+elemInfo->ChannelIndex[ch],
nChannels,
psyData[ch].mdctSpectrum,
&(mdctScalingArray[ch]),
psyData[ch].blockSwitchingControl.windowSequence);
maxScale = max(maxScale, mdctScalingArray[ch]);
}
/* common scaling for all channels */
for (ch=0; ch<channels; ch++) {
Word16 scaleDiff = maxScale - mdctScalingArray[ch];
if (scaleDiff > 0) {
Word32 *Spectrum = psyData[ch].mdctSpectrum;
for(line=0; line<FRAME_LEN_LONG; line++) {
*Spectrum = (*Spectrum) >> scaleDiff;
Spectrum++;
}
}
psyData[ch].mdctScale = maxScale;
}
for (ch=0; ch<channels; ch++) {
if(psyData[ch].blockSwitchingControl.windowSequence != SHORT_WINDOW) {
/* update long block parameter */
advancePsychLong(&psyData[ch],
&tnsData[ch],
hPsyConfLong,
&psyOutChannel[ch],
pScratchTns,
&tnsData[1 - ch],
ch);
/* determine maxSfb */
for (sfb=hPsyConfLong->sfbCnt-1; sfb>=0; sfb--) {
for (line=hPsyConfLong->sfbOffset[sfb+1] - 1; line>=hPsyConfLong->sfbOffset[sfb]; line--) {
if (psyData[ch].mdctSpectrum[line] != 0) break;
}
if (line >= hPsyConfLong->sfbOffset[sfb]) break;
}
maxSfbPerGroup[ch] = sfb + 1;
/* Calc bandwise energies for mid and side channel
Do it only if 2 channels exist */
if (ch == 1)
advancePsychLongMS(psyData, hPsyConfLong);
}
else {
advancePsychShort(&psyData[ch],
&tnsData[ch],
hPsyConfShort,
&psyOutChannel[ch],
pScratchTns,
&tnsData[1 - ch],
ch);
/* Calc bandwise energies for mid and side channel
Do it only if 2 channels exist */
if (ch == 1)
advancePsychShortMS (psyData, hPsyConfShort);
}
}
/* group short data */
for(ch=0; ch<channels; ch++) {
if (psyData[ch].blockSwitchingControl.windowSequence == SHORT_WINDOW) {
groupShortData(psyData[ch].mdctSpectrum,
pScratchTns,
&psyData[ch].sfbThreshold,
&psyData[ch].sfbEnergy,
&psyData[ch].sfbEnergyMS,
&psyData[ch].sfbSpreadedEnergy,
hPsyConfShort->sfbCnt,
hPsyConfShort->sfbOffset,
hPsyConfShort->sfbMinSnr,
psyOutElement->groupedSfbOffset[ch],
&maxSfbPerGroup[ch],
psyOutElement->groupedSfbMinSnr[ch],
psyData[ch].blockSwitchingControl.noOfGroups,
psyData[ch].blockSwitchingControl.groupLen);
}
}
#if (MAX_CHANNELS>1)
/*
stereo Processing
*/
if (channels == 2) {
psyOutElement->toolsInfo.msDigest = MS_NONE;
maxSfbPerGroup[0] = maxSfbPerGroup[1] = max(maxSfbPerGroup[0], maxSfbPerGroup[1]);
if (psyData[0].blockSwitchingControl.windowSequence != SHORT_WINDOW)
MsStereoProcessing(psyData[0].sfbEnergy.sfbLong,
psyData[1].sfbEnergy.sfbLong,
psyData[0].sfbEnergyMS.sfbLong,
psyData[1].sfbEnergyMS.sfbLong,
psyData[0].mdctSpectrum,
psyData[1].mdctSpectrum,
psyData[0].sfbThreshold.sfbLong,
psyData[1].sfbThreshold.sfbLong,
psyData[0].sfbSpreadedEnergy.sfbLong,
psyData[1].sfbSpreadedEnergy.sfbLong,
(Word16*)&psyOutElement->toolsInfo.msDigest,
(Word16*)psyOutElement->toolsInfo.msMask,
hPsyConfLong->sfbCnt,
hPsyConfLong->sfbCnt,
maxSfbPerGroup[0],
(const Word16*)hPsyConfLong->sfbOffset);
else
MsStereoProcessing(psyData[0].sfbEnergy.sfbLong,
psyData[1].sfbEnergy.sfbLong,
psyData[0].sfbEnergyMS.sfbLong,
psyData[1].sfbEnergyMS.sfbLong,
psyData[0].mdctSpectrum,
psyData[1].mdctSpectrum,
psyData[0].sfbThreshold.sfbLong,
psyData[1].sfbThreshold.sfbLong,
psyData[0].sfbSpreadedEnergy.sfbLong,
psyData[1].sfbSpreadedEnergy.sfbLong,
(Word16*)&psyOutElement->toolsInfo.msDigest,
(Word16*)psyOutElement->toolsInfo.msMask,
psyData[0].blockSwitchingControl.noOfGroups*hPsyConfShort->sfbCnt,
hPsyConfShort->sfbCnt,
maxSfbPerGroup[0],
(const Word16*)psyOutElement->groupedSfbOffset[0]);
}
#endif /* (MAX_CHANNELS>1) */
/*
build output
*/
for(ch=0;ch<channels;ch++) {
if (psyData[ch].blockSwitchingControl.windowSequence != SHORT_WINDOW)
BuildInterface(psyData[ch].mdctSpectrum,
psyData[ch].mdctScale,
&psyData[ch].sfbThreshold,
&psyData[ch].sfbEnergy,
&psyData[ch].sfbSpreadedEnergy,
psyData[ch].sfbEnergySum,
psyData[ch].sfbEnergySumMS,
psyData[ch].blockSwitchingControl.windowSequence,
blockType2windowShape[psyData[ch].blockSwitchingControl.windowSequence],
hPsyConfLong->sfbCnt,
hPsyConfLong->sfbOffset,
maxSfbPerGroup[ch],
hPsyConfLong->sfbMinSnr,
psyData[ch].blockSwitchingControl.noOfGroups,
psyData[ch].blockSwitchingControl.groupLen,
&psyOutChannel[ch]);
else
BuildInterface(psyData[ch].mdctSpectrum,
psyData[ch].mdctScale,
&psyData[ch].sfbThreshold,
&psyData[ch].sfbEnergy,
&psyData[ch].sfbSpreadedEnergy,
psyData[ch].sfbEnergySum,
psyData[ch].sfbEnergySumMS,
SHORT_WINDOW,
SINE_WINDOW,
psyData[0].blockSwitchingControl.noOfGroups*hPsyConfShort->sfbCnt,
psyOutElement->groupedSfbOffset[ch],
maxSfbPerGroup[ch],
psyOutElement->groupedSfbMinSnr[ch],
psyData[ch].blockSwitchingControl.noOfGroups,
psyData[ch].blockSwitchingControl.groupLen,
&psyOutChannel[ch]);
}
return(0); /* no error */
}
/*****************************************************************************
*
* function name: advancePsychLong
* description: psychoacoustic for long blocks
*
*****************************************************************************/
static Word16 advancePsychLong(PSY_DATA* psyData,
TNS_DATA* tnsData,
PSY_CONFIGURATION_LONG *hPsyConfLong,
PSY_OUT_CHANNEL* psyOutChannel,
Word32 *pScratchTns,
const TNS_DATA* tnsData2,
const Word16 ch)
{
Word32 i;
Word32 normEnergyShift = (psyData->mdctScale + 1) << 1; /* in reference code, mdct spectrum must be multipied with 2, so +1 */
Word32 clipEnergy = hPsyConfLong->clipEnergy >> normEnergyShift;
Word32 *data0, *data1, tdata;
/* low pass */
data0 = psyData->mdctSpectrum + hPsyConfLong->lowpassLine;
for(i=hPsyConfLong->lowpassLine; i<FRAME_LEN_LONG; i++) {
*data0++ = 0;
}
/* Calc sfb-bandwise mdct-energies for left and right channel */
CalcBandEnergy( psyData->mdctSpectrum,
hPsyConfLong->sfbOffset,
hPsyConfLong->sfbActive,
psyData->sfbEnergy.sfbLong,
&psyData->sfbEnergySum.sfbLong);
/*
TNS detect
*/
TnsDetect(tnsData,
hPsyConfLong->tnsConf,
pScratchTns,
(const Word16*)hPsyConfLong->sfbOffset,
psyData->mdctSpectrum,
0,
psyData->blockSwitchingControl.windowSequence,
psyData->sfbEnergy.sfbLong);
/* TnsSync */
if (ch == 1) {
TnsSync(tnsData,
tnsData2,
hPsyConfLong->tnsConf,
0,
psyData->blockSwitchingControl.windowSequence);
}
/* Tns Encoder */
TnsEncode(&psyOutChannel->tnsInfo,
tnsData,
hPsyConfLong->sfbCnt,
hPsyConfLong->tnsConf,
hPsyConfLong->lowpassLine,
psyData->mdctSpectrum,
0,
psyData->blockSwitchingControl.windowSequence);
/* first part of threshold calculation */
data0 = psyData->sfbEnergy.sfbLong;
data1 = psyData->sfbThreshold.sfbLong;
for (i=hPsyConfLong->sfbCnt; i; i--) {
tdata = L_mpy_ls(*data0++, hPsyConfLong->ratio);
*data1++ = min(tdata, clipEnergy);
}
/* Calc sfb-bandwise mdct-energies for left and right channel again */
if (tnsData->dataRaw.tnsLong.subBlockInfo.tnsActive!=0) {
Word16 tnsStartBand = hPsyConfLong->tnsConf.tnsStartBand;
CalcBandEnergy( psyData->mdctSpectrum,
hPsyConfLong->sfbOffset+tnsStartBand,
hPsyConfLong->sfbActive - tnsStartBand,
psyData->sfbEnergy.sfbLong+tnsStartBand,
&psyData->sfbEnergySum.sfbLong);
data0 = psyData->sfbEnergy.sfbLong;
tdata = psyData->sfbEnergySum.sfbLong;
for (i=0; i<tnsStartBand; i++)
tdata += *data0++;
psyData->sfbEnergySum.sfbLong = tdata;
}
/* spreading energy */
SpreadingMax(hPsyConfLong->sfbCnt,
hPsyConfLong->sfbMaskLowFactor,
hPsyConfLong->sfbMaskHighFactor,
psyData->sfbThreshold.sfbLong);
/* threshold in quiet */
data0 = psyData->sfbThreshold.sfbLong;
data1 = hPsyConfLong->sfbThresholdQuiet;
for (i=hPsyConfLong->sfbCnt; i; i--)
{
*data0 = max(*data0, (*data1 >> normEnergyShift));
data0++; data1++;
}
/* preecho control */
if (psyData->blockSwitchingControl.windowSequence == STOP_WINDOW) {
data0 = psyData->sfbThresholdnm1;
for (i=hPsyConfLong->sfbCnt; i; i--) {
*data0++ = MAX_32;
}
psyData->mdctScalenm1 = 0;
}
PreEchoControl( psyData->sfbThresholdnm1,
hPsyConfLong->sfbCnt,
hPsyConfLong->maxAllowedIncreaseFactor,
hPsyConfLong->minRemainingThresholdFactor,
psyData->sfbThreshold.sfbLong,
psyData->mdctScale,
psyData->mdctScalenm1);
psyData->mdctScalenm1 = psyData->mdctScale;
if (psyData->blockSwitchingControl.windowSequence== START_WINDOW) {
data0 = psyData->sfbThresholdnm1;
for (i=hPsyConfLong->sfbCnt; i; i--) {
*data0++ = MAX_32;
}
psyData->mdctScalenm1 = 0;
}
/* apply tns mult table on cb thresholds */
ApplyTnsMultTableToRatios(hPsyConfLong->tnsConf.tnsRatioPatchLowestCb,
hPsyConfLong->tnsConf.tnsStartBand,
tnsData->dataRaw.tnsLong.subBlockInfo,
psyData->sfbThreshold.sfbLong);
/* spreaded energy */
data0 = psyData->sfbSpreadedEnergy.sfbLong;
data1 = psyData->sfbEnergy.sfbLong;
for (i=hPsyConfLong->sfbCnt; i; i--) {
//psyData->sfbSpreadedEnergy.sfbLong[i] = psyData->sfbEnergy.sfbLong[i];
*data0++ = *data1++;
}
/* spreading energy */
SpreadingMax(hPsyConfLong->sfbCnt,
hPsyConfLong->sfbMaskLowFactorSprEn,
hPsyConfLong->sfbMaskHighFactorSprEn,
psyData->sfbSpreadedEnergy.sfbLong);
return 0;
}
/*****************************************************************************
*
* function name: advancePsychLongMS
* description: update mdct-energies for left add or minus right channel
* for long block
*
*****************************************************************************/
static Word16 advancePsychLongMS (PSY_DATA psyData[MAX_CHANNELS],
const PSY_CONFIGURATION_LONG *hPsyConfLong)
{
CalcBandEnergyMS(psyData[0].mdctSpectrum,
psyData[1].mdctSpectrum,
hPsyConfLong->sfbOffset,
hPsyConfLong->sfbActive,
psyData[0].sfbEnergyMS.sfbLong,
&psyData[0].sfbEnergySumMS.sfbLong,
psyData[1].sfbEnergyMS.sfbLong,
&psyData[1].sfbEnergySumMS.sfbLong);
return 0;
}
/*****************************************************************************
*
* function name: advancePsychShort
* description: psychoacoustic for short blocks
*
*****************************************************************************/
static Word16 advancePsychShort(PSY_DATA* psyData,
TNS_DATA* tnsData,
const PSY_CONFIGURATION_SHORT *hPsyConfShort,
PSY_OUT_CHANNEL* psyOutChannel,
Word32 *pScratchTns,
const TNS_DATA *tnsData2,
const Word16 ch)
{
Word32 w;
Word32 normEnergyShift = (psyData->mdctScale + 1) << 1; /* in reference code, mdct spectrum must be multipied with 2, so +1 */
Word32 clipEnergy = hPsyConfShort->clipEnergy >> normEnergyShift;
Word32 wOffset = 0;
Word32 *data0;
const Word32 *data1;
for(w = 0; w < TRANS_FAC; w++) {
Word32 i, tdata;
/* low pass */
data0 = psyData->mdctSpectrum + wOffset + hPsyConfShort->lowpassLine;
for(i=hPsyConfShort->lowpassLine; i<FRAME_LEN_SHORT; i++){
*data0++ = 0;
}
/* Calc sfb-bandwise mdct-energies for left and right channel */
CalcBandEnergy( psyData->mdctSpectrum+wOffset,
hPsyConfShort->sfbOffset,
hPsyConfShort->sfbActive,
psyData->sfbEnergy.sfbShort[w],
&psyData->sfbEnergySum.sfbShort[w]);
/*
TNS
*/
TnsDetect(tnsData,
hPsyConfShort->tnsConf,
pScratchTns,
(const Word16*)hPsyConfShort->sfbOffset,
psyData->mdctSpectrum+wOffset,
w,
psyData->blockSwitchingControl.windowSequence,
psyData->sfbEnergy.sfbShort[w]);
/* TnsSync */
if (ch == 1) {
TnsSync(tnsData,
tnsData2,
hPsyConfShort->tnsConf,
w,
psyData->blockSwitchingControl.windowSequence);
}
TnsEncode(&psyOutChannel->tnsInfo,
tnsData,
hPsyConfShort->sfbCnt,
hPsyConfShort->tnsConf,
hPsyConfShort->lowpassLine,
psyData->mdctSpectrum+wOffset,
w,
psyData->blockSwitchingControl.windowSequence);
/* first part of threshold calculation */
data0 = psyData->sfbThreshold.sfbShort[w];
data1 = psyData->sfbEnergy.sfbShort[w];
for (i=hPsyConfShort->sfbCnt; i; i--) {
tdata = L_mpy_ls(*data1++, hPsyConfShort->ratio);
*data0++ = min(tdata, clipEnergy);
}
/* Calc sfb-bandwise mdct-energies for left and right channel again */
if (tnsData->dataRaw.tnsShort.subBlockInfo[w].tnsActive != 0) {
Word16 tnsStartBand = hPsyConfShort->tnsConf.tnsStartBand;
CalcBandEnergy( psyData->mdctSpectrum+wOffset,
hPsyConfShort->sfbOffset+tnsStartBand,
(hPsyConfShort->sfbActive - tnsStartBand),
psyData->sfbEnergy.sfbShort[w]+tnsStartBand,
&psyData->sfbEnergySum.sfbShort[w]);
tdata = psyData->sfbEnergySum.sfbShort[w];
data0 = psyData->sfbEnergy.sfbShort[w];
for (i=tnsStartBand; i; i--)
tdata += *data0++;
psyData->sfbEnergySum.sfbShort[w] = tdata;
}
/* spreading */
SpreadingMax(hPsyConfShort->sfbCnt,
hPsyConfShort->sfbMaskLowFactor,
hPsyConfShort->sfbMaskHighFactor,
psyData->sfbThreshold.sfbShort[w]);
/* threshold in quiet */
data0 = psyData->sfbThreshold.sfbShort[w];
data1 = hPsyConfShort->sfbThresholdQuiet;
for (i=hPsyConfShort->sfbCnt; i; i--)
{
*data0 = max(*data0, (*data1 >> normEnergyShift));
data0++; data1++;
}
/* preecho */
PreEchoControl( psyData->sfbThresholdnm1,
hPsyConfShort->sfbCnt,
hPsyConfShort->maxAllowedIncreaseFactor,
hPsyConfShort->minRemainingThresholdFactor,
psyData->sfbThreshold.sfbShort[w],
psyData->mdctScale,
w==0 ? psyData->mdctScalenm1 : psyData->mdctScale);
/* apply tns mult table on cb thresholds */
ApplyTnsMultTableToRatios( hPsyConfShort->tnsConf.tnsRatioPatchLowestCb,
hPsyConfShort->tnsConf.tnsStartBand,
tnsData->dataRaw.tnsShort.subBlockInfo[w],
psyData->sfbThreshold.sfbShort[w]);
/* spreaded energy */
data0 = psyData->sfbSpreadedEnergy.sfbShort[w];
data1 = psyData->sfbEnergy.sfbShort[w];
for (i=hPsyConfShort->sfbCnt; i; i--) {
*data0++ = *data1++;
}
SpreadingMax(hPsyConfShort->sfbCnt,
hPsyConfShort->sfbMaskLowFactorSprEn,
hPsyConfShort->sfbMaskHighFactorSprEn,
psyData->sfbSpreadedEnergy.sfbShort[w]);
wOffset += FRAME_LEN_SHORT;
} /* for TRANS_FAC */
psyData->mdctScalenm1 = psyData->mdctScale;
return 0;
}
/*****************************************************************************
*
* function name: advancePsychShortMS
* description: update mdct-energies for left add or minus right channel
* for short block
*
*****************************************************************************/
static Word16 advancePsychShortMS (PSY_DATA psyData[MAX_CHANNELS],
const PSY_CONFIGURATION_SHORT *hPsyConfShort)
{
Word32 w, wOffset;
wOffset = 0;
for(w=0; w<TRANS_FAC; w++) {
CalcBandEnergyMS(psyData[0].mdctSpectrum+wOffset,
psyData[1].mdctSpectrum+wOffset,
hPsyConfShort->sfbOffset,
hPsyConfShort->sfbActive,
psyData[0].sfbEnergyMS.sfbShort[w],
&psyData[0].sfbEnergySumMS.sfbShort[w],
psyData[1].sfbEnergyMS.sfbShort[w],
&psyData[1].sfbEnergySumMS.sfbShort[w]);
wOffset += FRAME_LEN_SHORT;
}
return 0;
}