816 lines
28 KiB
C
816 lines
28 KiB
C
/*
|
|
** 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;
|
|
}
|