| /* |
| ** 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 Word16 |
| IIRFilter(const Word16 in, const Word32 coeff[], Word32 states[]); |
| |
| 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 |
| */ |
| 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; i<TRANS_FAC; i++) { |
| blockSwitchingControl->groupLen[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; i<MAX_NO_OF_GROUPS; i++) { |
| blockSwitchingControl->groupLen[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; w<BLOCK_SWITCH_WINDOWS; w++) { |
| blockSwitchingControl->windowNrg[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; w<BLOCK_SWITCH_WINDOWS; w++) { |
| Word32 enM1_Tmp, accWindowNrg_Tmp, windowNrgF_Tmp; |
| Word16 enM1_Shf, accWindowNrg_Shf, windowNrgF_Shf; |
| |
| accWindowNrg_Shf = norm_l(blockSwitchingControl->accWindowNrg); |
| 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<windowLen; i++) { |
| Word32 accu1, accu2, accu3; |
| Word32 out; |
| tempUnfiltered = timeSignal[tidx]; |
| tidx = tidx + chIncrement; |
| |
| accu1 = L_mpy_ls(Coeff1, tempUnfiltered); |
| accu2 = fixmul( Coeff0, states1 ); |
| accu3 = accu1 - states0; |
| out = accu3 - accu2; |
| |
| states0 = accu1; |
| states1 = out; |
| |
| tempFiltered = extract_h(out); |
| accuUE += (tempUnfiltered * tempUnfiltered) >> 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 |
| |
| /***************************************************************************** |
| * |
| * function name: IIRFilter |
| * description: calculate the iir-filter for an array |
| * returns: the result after iir-filter |
| * |
| **********************************************************************************/ |
| static Word16 IIRFilter(const Word16 in, const Word32 coeff[], Word32 states[]) |
| { |
| Word32 accu1, accu2, accu3; |
| Word32 out; |
| |
| accu1 = L_mpy_ls(coeff[1], in); |
| accu3 = accu1 - states[0]; |
| accu2 = fixmul( coeff[0], states[1] ); |
| out = accu3 - accu2; |
| |
| states[0] = accu1; |
| states[1] = out; |
| |
| return round16(out); |
| } |
| |
| |
| 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; i<TRANS_FAC; i++) { |
| blockSwitchingControlLeft->groupLen[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; i<TRANS_FAC; i++) { |
| blockSwitchingControlLeft->groupLen[i] = 0; |
| blockSwitchingControlRight->groupLen[i] = 0; |
| } |
| } |
| else { |
| |
| if (blockSwitchingControlLeft->maxWindowNrg > blockSwitchingControlRight->maxWindowNrg) { |
| /* Left Channel wins */ |
| blockSwitchingControlRight->noOfGroups = blockSwitchingControlLeft->noOfGroups; |
| for (i=0; i<TRANS_FAC; i++) { |
| blockSwitchingControlRight->groupLen[i] = blockSwitchingControlLeft->groupLen[i]; |
| } |
| } |
| else { |
| /* Right Channel wins */ |
| blockSwitchingControlLeft->noOfGroups = blockSwitchingControlRight->noOfGroups; |
| for (i=0; i<TRANS_FAC; i++) { |
| blockSwitchingControlLeft->groupLen[i] = blockSwitchingControlRight->groupLen[i]; |
| } |
| } |
| } |
| } /*endif Mono or Stereo */ |
| |
| return(TRUE); |
| } |