/* ** 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: block_switch.c Content: Block switching functions *******************************************************************************/ #include "typedef.h" #include "basic_op.h" #include "oper_32b.h" #include "psy_const.h" #include "block_switch.h" #define ENERGY_SHIFT (8 - 1) /**************** internal function prototypes ***********/ static Word32 SrchMaxWithIndex(const Word32 *in, Word16 *index, Word16 n); Word32 CalcWindowEnergy(BLOCK_SWITCHING_CONTROL *blockSwitchingControl, Word16 *timeSignal, Word16 chIncrement, Word16 windowLen); /****************** Constants *****************************/ /* IIR high pass coeffs */ const Word32 hiPassCoeff[BLOCK_SWITCHING_IIR_LEN] = { 0xbec8b439, 0x609d4952 /* -0.5095f, 0.7548f */ }; static const Word32 accWindowNrgFac = 0x26666666; /* factor for accumulating filtered window energies 0.3 */ static const Word32 oneMinusAccWindowNrgFac = 0x5999999a; /* 0.7 */ static const Word32 invAttackRatioHighBr = 0x0ccccccd; /* inverted lower ratio limit for attacks 0.1*/ static const Word32 invAttackRatioLowBr = 0x072b020c; /* 0.056 */ static const Word32 minAttackNrg = 0x00001e84; /* minimum energy for attacks 1e+6 */ /****************** Routines ****************************/ /***************************************************************************** * * function name: InitBlockSwitching * description: init Block Switching parameter. * returns: TRUE if success * **********************************************************************************/ Word16 InitBlockSwitching(BLOCK_SWITCHING_CONTROL *blockSwitchingControl, const Word32 bitRate, const Word16 nChannels) { /* select attackRatio */ if ((sub(nChannels,1)==0 && L_sub(bitRate, 24000) > 0) || (sub(nChannels,1)>0 && bitRate > (nChannels * 16000))) { blockSwitchingControl->invAttackRatio = invAttackRatioHighBr; } else { blockSwitchingControl->invAttackRatio = invAttackRatioLowBr; } return(TRUE); } static Word16 suggestedGroupingTable[TRANS_FAC][MAX_NO_OF_GROUPS] = { /* Attack in Window 0 */ {1, 3, 3, 1}, /* Attack in Window 1 */ {1, 1, 3, 3}, /* Attack in Window 2 */ {2, 1, 3, 2}, /* Attack in Window 3 */ {3, 1, 3, 1}, /* Attack in Window 4 */ {3, 1, 1, 3}, /* Attack in Window 5 */ {3, 2, 1, 2}, /* Attack in Window 6 */ {3, 3, 1, 1}, /* Attack in Window 7 */ {3, 3, 1, 1} }; /***************************************************************************** * * function name: BlockSwitching * description: detect this frame whether there is an attack * returns: TRUE if success * **********************************************************************************/ Word16 BlockSwitching(BLOCK_SWITCHING_CONTROL *blockSwitchingControl, Word16 *timeSignal, Word32 sampleRate, Word16 chIncrement) { Word32 i, w; Word32 enM1, enMax; /* Reset grouping info */ for (i=0; igroupLen[i] = 0; } /* Search for position and amplitude of attack in last frame (1 windows delay) */ blockSwitchingControl->maxWindowNrg = SrchMaxWithIndex( &blockSwitchingControl->windowNrg[0][BLOCK_SWITCH_WINDOWS-1], &blockSwitchingControl->attackIndex, BLOCK_SWITCH_WINDOWS); blockSwitchingControl->attackIndex = blockSwitchingControl->lastAttackIndex; /* Set grouping info */ blockSwitchingControl->noOfGroups = MAX_NO_OF_GROUPS; for (i=0; igroupLen[i] = suggestedGroupingTable[blockSwitchingControl->attackIndex][i]; } /* if the samplerate is less than 16000, it should be all the short block, avoid pre&post echo */ if(sampleRate >= 16000) { /* Save current window energy as last window energy */ for (w=0; wwindowNrg[0][w] = blockSwitchingControl->windowNrg[1][w]; blockSwitchingControl->windowNrgF[0][w] = blockSwitchingControl->windowNrgF[1][w]; } /* Calculate unfiltered and filtered energies in subwindows and combine to segments */ CalcWindowEnergy(blockSwitchingControl, timeSignal, chIncrement, BLOCK_SWITCH_WINDOW_LEN); /* reset attack */ blockSwitchingControl->attack = FALSE; enMax = 0; enM1 = blockSwitchingControl->windowNrgF[0][BLOCK_SWITCH_WINDOWS-1]; for (w=0; waccWindowNrg); enM1_Shf = norm_l(enM1); windowNrgF_Shf = norm_l(blockSwitchingControl->windowNrgF[1][w]); accWindowNrg_Tmp = blockSwitchingControl->accWindowNrg << accWindowNrg_Shf; enM1_Tmp = enM1 << enM1_Shf; windowNrgF_Tmp = blockSwitchingControl->windowNrgF[1][w] << windowNrgF_Shf; /* a sliding average of the previous energies */ blockSwitchingControl->accWindowNrg = (fixmul(oneMinusAccWindowNrgFac, accWindowNrg_Tmp) >> accWindowNrg_Shf) + (fixmul(accWindowNrgFac, enM1_Tmp) >> enM1_Shf); /* if the energy with the ratio is bigger than the average, and the attack and short block */ if ((fixmul(windowNrgF_Tmp, blockSwitchingControl->invAttackRatio) >> windowNrgF_Shf) > blockSwitchingControl->accWindowNrg ) { blockSwitchingControl->attack = TRUE; blockSwitchingControl->lastAttackIndex = w; } enM1 = blockSwitchingControl->windowNrgF[1][w]; enMax = max(enMax, enM1); } if (enMax < minAttackNrg) { blockSwitchingControl->attack = FALSE; } } else { blockSwitchingControl->attack = TRUE; } /* Check if attack spreads over frame border */ if ((!blockSwitchingControl->attack) && (blockSwitchingControl->lastattack)) { if (blockSwitchingControl->attackIndex == TRANS_FAC-1) { blockSwitchingControl->attack = TRUE; } blockSwitchingControl->lastattack = FALSE; } else { blockSwitchingControl->lastattack = blockSwitchingControl->attack; } blockSwitchingControl->windowSequence = blockSwitchingControl->nextwindowSequence; if (blockSwitchingControl->attack) { blockSwitchingControl->nextwindowSequence = SHORT_WINDOW; } else { blockSwitchingControl->nextwindowSequence = LONG_WINDOW; } /* update short block group */ if (blockSwitchingControl->nextwindowSequence == SHORT_WINDOW) { if (blockSwitchingControl->windowSequence== LONG_WINDOW) { blockSwitchingControl->windowSequence = START_WINDOW; } if (blockSwitchingControl->windowSequence == STOP_WINDOW) { blockSwitchingControl->windowSequence = SHORT_WINDOW; blockSwitchingControl->noOfGroups = 3; blockSwitchingControl->groupLen[0] = 3; blockSwitchingControl->groupLen[1] = 3; blockSwitchingControl->groupLen[2] = 2; } } /* update block type */ if (blockSwitchingControl->nextwindowSequence == LONG_WINDOW) { if (blockSwitchingControl->windowSequence == SHORT_WINDOW) { blockSwitchingControl->nextwindowSequence = STOP_WINDOW; } } return(TRUE); } /***************************************************************************** * * function name: SrchMaxWithIndex * description: search for the biggest value in an array * returns: the max value * **********************************************************************************/ static Word32 SrchMaxWithIndex(const Word32 in[], Word16 *index, Word16 n) { Word32 max; Word32 i, idx; /* Search maximum value in array and return index and value */ max = 0; idx = 0; for (i = 0; i < n; i++) { if (in[i+1] > max) { max = in[i+1]; idx = i; } } *index = idx; return(max); } /***************************************************************************** * * function name: CalcWindowEnergy * description: calculate the energy before iir-filter and after irr-filter * returns: TRUE if success * **********************************************************************************/ #ifndef ARMV5E Word32 CalcWindowEnergy(BLOCK_SWITCHING_CONTROL *blockSwitchingControl, Word16 *timeSignal, Word16 chIncrement, Word16 windowLen) { Word32 w, i, wOffset, tidx, ch; Word32 accuUE, accuFE; Word32 tempUnfiltered; Word32 tempFiltered; Word32 states0, states1; Word32 Coeff0, Coeff1; states0 = blockSwitchingControl->iirStates[0]; states1 = blockSwitchingControl->iirStates[1]; Coeff0 = hiPassCoeff[0]; Coeff1 = hiPassCoeff[1]; tidx = 0; for (w=0; w < BLOCK_SWITCH_WINDOWS; w++) { accuUE = 0; accuFE = 0; for(i=0; i> ENERGY_SHIFT; accuFE += (tempFiltered * tempFiltered) >> ENERGY_SHIFT; } blockSwitchingControl->windowNrg[1][w] = accuUE; blockSwitchingControl->windowNrgF[1][w] = accuFE; } blockSwitchingControl->iirStates[0] = states0; blockSwitchingControl->iirStates[1] = states1; return(TRUE); } #endif static Word16 synchronizedBlockTypeTable[4][4] = { /* LONG_WINDOW START_WINDOW SHORT_WINDOW STOP_WINDOW */ /* LONG_WINDOW */{LONG_WINDOW, START_WINDOW, SHORT_WINDOW, STOP_WINDOW}, /* START_WINDOW */{START_WINDOW, START_WINDOW, SHORT_WINDOW, SHORT_WINDOW}, /* SHORT_WINDOW */{SHORT_WINDOW, SHORT_WINDOW, SHORT_WINDOW, SHORT_WINDOW}, /* STOP_WINDOW */{STOP_WINDOW, SHORT_WINDOW, SHORT_WINDOW, STOP_WINDOW} }; /***************************************************************************** * * function name: SyncBlockSwitching * description: update block type and group value * returns: TRUE if success * **********************************************************************************/ Word16 SyncBlockSwitching(BLOCK_SWITCHING_CONTROL *blockSwitchingControlLeft, BLOCK_SWITCHING_CONTROL *blockSwitchingControlRight, const Word16 nChannels) { Word16 i; Word16 patchType = LONG_WINDOW; if (nChannels == 1) { /* Mono */ if (blockSwitchingControlLeft->windowSequence != SHORT_WINDOW) { blockSwitchingControlLeft->noOfGroups = 1; blockSwitchingControlLeft->groupLen[0] = 1; for (i=1; igroupLen[i] = 0; } } } else { /* Stereo common Window */ patchType = synchronizedBlockTypeTable[patchType][blockSwitchingControlLeft->windowSequence]; patchType = synchronizedBlockTypeTable[patchType][blockSwitchingControlRight->windowSequence]; /* Set synchronized Blocktype */ blockSwitchingControlLeft->windowSequence = patchType; blockSwitchingControlRight->windowSequence = patchType; /* Synchronize grouping info */ if(patchType != SHORT_WINDOW) { /* Long Blocks */ /* Set grouping info */ blockSwitchingControlLeft->noOfGroups = 1; blockSwitchingControlRight->noOfGroups = 1; blockSwitchingControlLeft->groupLen[0] = 1; blockSwitchingControlRight->groupLen[0] = 1; for (i=1; igroupLen[i] = 0; blockSwitchingControlRight->groupLen[i] = 0; } } else { if (blockSwitchingControlLeft->maxWindowNrg > blockSwitchingControlRight->maxWindowNrg) { /* Left Channel wins */ blockSwitchingControlRight->noOfGroups = blockSwitchingControlLeft->noOfGroups; for (i=0; igroupLen[i] = blockSwitchingControlLeft->groupLen[i]; } } else { /* Right Channel wins */ blockSwitchingControlLeft->noOfGroups = blockSwitchingControlRight->noOfGroups; for (i=0; igroupLen[i] = blockSwitchingControlRight->groupLen[i]; } } } } /*endif Mono or Stereo */ return(TRUE); }