/* ** 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" /* 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; ipsyData[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; nchpsyData[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) { hPsyOut=NULL; 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; chChannelIndex[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 0) { Word32 *Spectrum = psyData[ch].mdctSpectrum; for(line=0; line> scaleDiff; Spectrum++; } } psyData[ch].mdctScale = maxScale; } for (ch=0; chsfbCnt-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; chsfbCnt, 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;chsfbCnt, 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; imdctSpectrum, 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; isfbEnergySum.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; imdctSpectrum+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; wsfbOffset, 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; }