405 lines
13 KiB
C
405 lines
13 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: 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; 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, tidx;
|
|
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
|
|
|
|
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);
|
|
}
|