- VAD algo is actually the variant described in G729 Appendix II + move headers to src filespull/2/head
| @ -1,31 +1 @@ | |||
| SUBDIRS = bcg729 | |||
| include_HEADERS= adaptativeCodebookSearch.h \ | |||
| basicOperationsMacros.h \ | |||
| codebooks.h \ | |||
| codecParameters.h \ | |||
| computeAdaptativeCodebookGain.h \ | |||
| computeLP.h \ | |||
| computeWeightedSpeech.h \ | |||
| decodeAdaptativeCodeVector.h \ | |||
| decodeFixedCodeVector.h \ | |||
| decodeGains.h \ | |||
| decodeLSP.h \ | |||
| findOpenLoopPitchDelay.h \ | |||
| fixedCodebookSearch.h \ | |||
| fixedPointMacros.h \ | |||
| floatingPointMacros.h \ | |||
| g729FixedPointMath.h \ | |||
| gainQuantization.h \ | |||
| interpolateqLSP.h \ | |||
| LP2LSPConversion.h \ | |||
| LPSynthesisFilter.h \ | |||
| LSPQuantization.h \ | |||
| postFilter.h \ | |||
| postProcessing.h \ | |||
| preProcessing.h \ | |||
| qLSP2LP.h \ | |||
| typedef.h \ | |||
| utils.h | |||
| EXTRA_DIST=$(include_HEADERS) | |||
| @ -1,32 +0,0 @@ | |||
| /* | |||
| computeLP.h | |||
| Copyright (C) 2011 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef COMPUTELP_H | |||
| #define COMPUTELP_H | |||
| /*****************************************************************************/ | |||
| /* computeLP : As described in spec 3.2.1 and 3.2.2 : Windowing, */ | |||
| /* Autocorrelation and Levinson-Durbin algorithm */ | |||
| /* parameters: */ | |||
| /* -(i) signal: 240 samples in Q0, the last 40 are from next frame */ | |||
| /* -(o) LPCoefficientsQ12: 10 LP coefficients in Q12 */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| void computeLP(word16_t signal[], word16_t LPCoefficientsQ12[]); | |||
| #endif /* ifndef COMPUTELP_H */ | |||
| @ -0,0 +1,294 @@ | |||
| /* | |||
| cng.c | |||
| Comfort Noise Generation | |||
| Copyright (C) 2015 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include "typedef.h" | |||
| #include "codecParameters.h" | |||
| #include "basicOperationsMacros.h" | |||
| #include "utils.h" | |||
| #include "cng.h" | |||
| #include "decodeAdaptativeCodeVector.h" | |||
| #include "decodeLSP.h" | |||
| #include "interpolateqLSP.h" | |||
| #include "qLSP2LP.h" | |||
| #include "g729FixedPointMath.h" | |||
| #include "codebooks.h" | |||
| /* buffers allocation */ | |||
| static const word16_t SIDqLSPInitialValues[NB_LSP_COEFF] = {31441, 27566, 21458, 13612, 4663, -4663, -13612, -21458, -27566, -31441}; /* in Q0.15 the initials values for the previous qLSP buffer */ | |||
| bcg729CNGChannelContextStruct *initBcg729CNGChannel() { | |||
| /* create the context structure */ | |||
| bcg729CNGChannelContextStruct *CNGChannelContext = malloc(sizeof(bcg729CNGChannelContextStruct)); | |||
| memset(CNGChannelContext, 0, sizeof(*CNGChannelContext)); | |||
| memcpy(CNGChannelContext->qLSP, SIDqLSPInitialValues, NB_LSP_COEFF*sizeof(word16_t)); /* initialise the previousqLSP buffer */ | |||
| return CNGChannelContext; | |||
| } | |||
| /*******************************************************************************************/ | |||
| /* computeComfortNoiseExcitationVector : as is spec B4.4 and B4.5 */ | |||
| /* parameters: */ | |||
| /* -(i): targetGain : the gain from ea B.19 in Q3 */ | |||
| /* -(i/o): randomGeneratorSeed : used to get a pseudo random number, is updated */ | |||
| /* -(i/o): excitationVector in Q0, accessed in range [-L_PAST_EXCITATION,L_FRAME-1] */ | |||
| /* [-154,79] */ | |||
| /* */ | |||
| /*******************************************************************************************/ | |||
| void computeComfortNoiseExcitationVector(word16_t targetGain, uint16_t *randomGeneratorSeed, word16_t *excitationVector) { | |||
| int16_t fracPitchDelay,intPitchDelay; | |||
| uint16_t randomNumberBuffer; | |||
| uint8_t subframeIndex = 0; | |||
| int i,j; | |||
| /* shall we check targetGain is 0?? and set excitation vector[0,L_FRAME[ to 0 in this case?*/ | |||
| for (subframeIndex=0; subframeIndex<L_FRAME; subframeIndex+=L_SUBFRAME) { /* process 2 subframes */ | |||
| word16_t gaussianRandomExcitation[L_SUBFRAME]; | |||
| word32_t Eg = 0; | |||
| word32_t Gg = 0; /* gain to be applied to the randomly generated gaussian to reach a Eg of 1/4*L_SUBFRAME*targetGain(note this doesn't appears in spec, only in ITU code)*/ | |||
| word16_t Ga = 0; /* gain to be applied to adaptative subframe excitation in Q0.15 */ | |||
| word32_t Ea = 0; | |||
| word32_t Ei = 0; /* inter excitation : Sum[0..39] adaptativeExcitation*fixedExcitation, defined in eq B21*/ | |||
| word32_t K = 0; | |||
| word32_t Gf = 0; /* gain to be applied to the fixed codebook excitation */ | |||
| word32_t x2 = 0; /* second root of the 2nd degre equation solved to get Gf */ | |||
| word16_t sign[4]; /* sign of the impulses of fixed codebook excitation */ | |||
| word16_t position[4]; /* positions of the impulses of fixed codebook excitation */ | |||
| uint8_t deltaScaleFactor=0; | |||
| word64_t delta; | |||
| /* generate pseudo random pitch delay in range [40,103] for adaptative codebook */ | |||
| randomNumberBuffer = pseudoRandom(randomGeneratorSeed); /* get 16 bits of pseudoRandom value*/ | |||
| fracPitchDelay = (int16_t)(randomNumberBuffer&0x0003)-1; /* fraction part of the pitch delay shall be -1, 0 or 1 */ | |||
| if (fracPitchDelay==2) fracPitchDelay = 0; | |||
| randomNumberBuffer = randomNumberBuffer>>2; /* 14 random bits left */ | |||
| intPitchDelay = (randomNumberBuffer&0x003F) + 40; /* intPitchDelay in [40,103] */ | |||
| randomNumberBuffer = randomNumberBuffer>>6; /* 8 random bits left */ | |||
| /* generate pseudo random sign and position for fixed codebook */ | |||
| position[0] = (randomNumberBuffer&0x0007)*5; | |||
| randomNumberBuffer = randomNumberBuffer>>3; /* 5 random bits left */ | |||
| sign[0] = randomNumberBuffer&0x0001; | |||
| randomNumberBuffer = randomNumberBuffer>>1; /* 4 random bits left */ | |||
| position[1] = (randomNumberBuffer&0x0007)*5+1; | |||
| randomNumberBuffer = randomNumberBuffer>>3; /* 1 random bits left */ | |||
| sign[1] = randomNumberBuffer&0x0001, | |||
| randomNumberBuffer = pseudoRandom(randomGeneratorSeed); /* get 16 bits of pseudoRandom value*/ | |||
| position[2] = (randomNumberBuffer&0x0007)*5+2; | |||
| randomNumberBuffer = randomNumberBuffer>>3; /* 13 random bits left */ | |||
| sign[2] = randomNumberBuffer&0x0001; | |||
| randomNumberBuffer = randomNumberBuffer>>1; /* 12 random bits left */ | |||
| position[3] = (randomNumberBuffer&0x0001)+3; /*j+3*/ | |||
| randomNumberBuffer = randomNumberBuffer>>1; /* 11 random bits left */ | |||
| position[3] += (randomNumberBuffer&0x0007)*5; | |||
| randomNumberBuffer = randomNumberBuffer>>3; /* 8 random bits left */ | |||
| sign[3] = randomNumberBuffer&0x0001; | |||
| randomNumberBuffer = randomNumberBuffer>>1; /* 7 random bits left */ | |||
| /* randomly generate Ga : adaptative gain eqB.22: max is 0,5 */ | |||
| Ga = (pseudoRandom(randomGeneratorSeed)&0x1fff)<<1; /* get 16 bits of pseudoRandom value but make sure it is < 0.5 in Q15 */ | |||
| /* generate gaussian random excitation : get generation algo from ITU code, no reference to this in the spec... | |||
| * Compute also the subframe energy(Sum [0..39] excitation^2) and then scale the sample to get an subframe average energy to targetGain/4 */ | |||
| for (i=0; i<L_SUBFRAME; i++) { | |||
| word32_t tmpBuffer=0; | |||
| for (j=0; j<12; j++) { | |||
| tmpBuffer=ADD32(tmpBuffer, (word16_t)pseudoRandom(randomGeneratorSeed)); /* cast the unsigned 16 bits value to a signed one */ | |||
| } | |||
| gaussianRandomExcitation[i]=(word16_t)(SHR(tmpBuffer,7)); | |||
| Eg = MAC16_16(Eg, gaussianRandomExcitation[i], gaussianRandomExcitation[i]); | |||
| } | |||
| /* compute coefficient = 1/2*sqrt(L_SUBFRAME/gaussianRandomExcitationSubframeEnergy)*targetGain (always>0) - retrieved from ITU code, no mention of this in the spec */ | |||
| /* comments in code say 1/4*sqrt() but code actually implement 1/2... */ | |||
| Gg = MULT16_32_Q15(GAUSSIAN_EXCITATION_COEFF_FACTOR, g729InvSqrt_Q0Q31(Eg)); /* multiplicand in Q1.13 and Q0.31 -> result in Q1.29 */ | |||
| Gg = MULT16_32_Q15(targetGain, Gg); /* multiplicand in Q3 and Q1.29 -> result in Q17 */ | |||
| for (i=0; i<L_SUBFRAME; i++) { | |||
| /* operate on positive value only to avoid problem when shifting to 0 a negative one */ | |||
| if (gaussianRandomExcitation[i]<0) { | |||
| gaussianRandomExcitation[i] = -SATURATE(PSHR( MULT16_32_Q15(-gaussianRandomExcitation[i], Gg), 2), MAXINT16); /* gaussianRandom in Q0, targetGain in Q17 -> result in Q2, need to shift it by 2. */ | |||
| } else { | |||
| gaussianRandomExcitation[i] = PSHR( MULT16_32_Q15(gaussianRandomExcitation[i], Gg), 2); | |||
| } | |||
| } | |||
| /* generate random adaptative excitation and apply random gain Ga */ | |||
| computeAdaptativeCodebookVector(excitationVector+subframeIndex, fracPitchDelay, intPitchDelay); | |||
| for (i=0; i<L_SUBFRAME; i++) { | |||
| excitationVector[i+subframeIndex] = SATURATE(MULT16_16_P15(excitationVector[i+subframeIndex], Ga), MAXINT16); | |||
| } | |||
| /* add gaussian excitation to the adaptative one */ | |||
| for (i=0; i<L_SUBFRAME; i++) { | |||
| excitationVector[i+subframeIndex] = SATURATE(ADD32(excitationVector[i+subframeIndex], gaussianRandomExcitation[i]),MAXINT16); | |||
| } | |||
| /* Compute Gf: fixed excitation gain salving equation B.21 in form 4*Gf^2 + 2I*Ga*Gf + Ea*Ga^2 - K = 0 (4Gf^2 + 2bGf + c = 0)*/ | |||
| /* compute Ea*Ga^2 */ | |||
| for (i=0; i<L_SUBFRAME; i++) { | |||
| Ea = MAC16_16(Ea, excitationVector[i+subframeIndex], excitationVector[i+subframeIndex]); /* Ea actually contains Ea*Ga^2 as we already applied gain Ga on adaptative excitation, in Q0 */ | |||
| } | |||
| /* compute Ei = I*Ga : fixed codebook hasn't been scaled yet, so it just 4 signed pulses of height 1, just get these one in our computation, in Q0 */ | |||
| Ei = 0; | |||
| for (i=0; i<4; i++) { | |||
| if (sign[i] == 0) { /* negative impulse */ | |||
| Ei = SUB32(Ei,excitationVector[position[i]+subframeIndex]); | |||
| } else { /* positive impulse */ | |||
| Ei = ADD32(Ei,excitationVector[position[i]+subframeIndex]); | |||
| } | |||
| } | |||
| /* compute K = L_SUBRAME*targetGain in Q3 */ | |||
| K = MULT16_32(targetGain, SHR(MULT16_16(L_SUBFRAME, targetGain), 3)); | |||
| /* compute delta = b^2 -ac = Ei^2 + 4*(K-Ea) in Q0 */ | |||
| delta = ADD64(MULT32_32(Ei,Ei), SHR(SUB64(K,SHL64((word64_t)Ea,3)),1)); | |||
| if (delta<0) { | |||
| /* cancel adaptative excitation, keep gaussian one only */ | |||
| for (i=0; i<L_SUBFRAME; i++) { | |||
| excitationVector[i+subframeIndex] = gaussianRandomExcitation[i]; | |||
| } | |||
| /* compute again Ei and delta = Ei^2 + 3/4*K (from ITU code, no idea why) */ | |||
| for (i=0; i<4; i++) { | |||
| if (sign[i] == 0) { /* negative impulse */ | |||
| Ei = SUB32(Ei,excitationVector[position[i]+subframeIndex]); | |||
| } else { /* positive impulse */ | |||
| Ei = ADD32(Ei,excitationVector[position[i]+subframeIndex]); | |||
| } | |||
| } | |||
| delta = ADD64(MULT32_32(Ei,Ei), MULT16_32_P15(COEFF_K,K)); /* COEFF_K is 0.75 in Q15 */ | |||
| } | |||
| /* scale delta to have it fitting on 32 bits */ | |||
| deltaScaleFactor = 0; | |||
| while (delta>=0x0000000080000000) { | |||
| delta = delta>>1; | |||
| deltaScaleFactor++; | |||
| } | |||
| /* deltaScaleFactor must be even */ | |||
| if (deltaScaleFactor%2==1) { | |||
| delta = delta >> 1; | |||
| deltaScaleFactor++; | |||
| } | |||
| delta = g729Sqrt_Q0Q7((word32_t)delta); /* delta in Q(7-deltaScaleFactor/2)*/ | |||
| /* scale b (Ei) to the same scale */ | |||
| Ei = VSHR32(Ei, deltaScaleFactor/2-7); | |||
| /* compute the two roots and pick the one with smaller absolute value */ | |||
| /* roots are (-b +-sqrt(delta))/a. We always have a=4, divide by four when rescaling from Q(7-deltaScaleFactor) to Q0 the final result */ | |||
| Gf = SUB32(delta, Ei); /* x1 = -b + sqrt(delta) in Q(7-deltaScaleFactor) */ | |||
| x2 = -ADD32(delta, Ei); /* x2 = -b - sqrt(delta) in Q(7-deltaScaleFactor) */ | |||
| if (ABS(x2)<ABS(Gf)) Gf = x2; /* pick the smallest (in absolute value of the roots) */ | |||
| Gf = VSHR32(Gf, 2+7-deltaScaleFactor/2); /* scale back Gf in Q0 and divide by 4 (+2 in the right shift) to get the actual root */ | |||
| /* add fixed codebook excitation */ | |||
| for (i=0; i<4; i++) { | |||
| if (sign[i] == 0) { /* negative impulse */ | |||
| excitationVector[position[i]+subframeIndex] = SUB32(excitationVector[position[i]+subframeIndex], Gf); | |||
| } else { /* positive impulse */ | |||
| excitationVector[position[i]+subframeIndex] = ADD32(excitationVector[position[i]+subframeIndex], Gf); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /*******************************************************************************************/ | |||
| /* decodeSIDframe : as is spec B4.4 and B4.5 */ | |||
| /* first check if we have a SID frame or a missing/untransmitted frame */ | |||
| /* for SID frame get paremeters(gain and LSP) */ | |||
| /* Then generate excitation vector and update qLSP */ | |||
| /* parameters: */ | |||
| /* -(i/o):CNGChannelContext : context containing all informations needed for CNG */ | |||
| /* -(i): previousFrameIsActiveFlag: true if last decoded frame was an active one */ | |||
| /* -(i): bitStream: for SID frame contains received params as in spec B4.3, */ | |||
| /* NULL for missing/untransmitted frame */ | |||
| /* -(i/o): excitationVector in Q0, accessed in range [-L_PAST_EXCITATION,L_FRAME-1] */ | |||
| /* [-154,79] */ | |||
| /* -(o): LP: 20 LP coefficients in Q12 */ | |||
| /* -(i/o): previousqLSP : previous quantised LSP in Q0.15 (NB_LSP_COEFF values) */ | |||
| /* -(i/o): pseudoRandomSeed : seed used in the pseudo random number generator */ | |||
| /* -(i/o): previousLCodeWord: in Q2.13, buffer to store the last 4 frames codewords, */ | |||
| /* used to compute the current qLSF */ | |||
| /* */ | |||
| /*******************************************************************************************/ | |||
| void decodeSIDframe(bcg729CNGChannelContextStruct *CNGChannelContext, uint8_t previousFrameIsActiveFlag, uint8_t *bitStream, word16_t *excitationVector, word16_t *previousqLSP, word16_t *LP, uint16_t *pseudoRandomSeed, word16_t previousLCodeWord[MA_MAX_K][NB_LSP_COEFF]) { | |||
| int i; | |||
| word16_t interpolatedqLSP[NB_LSP_COEFF]; /* interpolated qLSP in Q0.15 */ | |||
| /* if this is a SID frame, decode received parameters */ | |||
| if (bitStream!=NULL) { | |||
| word16_t currentqLSF[NB_LSP_COEFF]; /* buffer to the current qLSF in Q2.13 */ | |||
| uint8_t L0 = (bitStream[0]>>7)&0x01; | |||
| uint8_t L1Index = (bitStream[0]>>2)&0x1F; | |||
| uint8_t L2Index = ((bitStream[0]&0x03)<<2) | ((bitStream[1]>>6)&0x03); | |||
| /* retrieve gain from codebook according to Gain parameter */ | |||
| CNGChannelContext->receivedSIDGain = SIDGainCodebook[((bitStream[1])>>1)&0x1F]; | |||
| /* Use L1 and L2(parameter for first and second stage vector of LSF quantizer) to retrieve LSP using subset index to address the complete L1 and L2L3 codebook */ | |||
| for (i=0; i<NB_LSP_COEFF/2; i++) { | |||
| currentqLSF[i]=ADD16(L1[L1SubsetIndex[L1Index]][i], L2L3[L2SubsetIndex[L2Index]][i]); | |||
| } | |||
| for (i=NB_LSP_COEFF/2; i<NB_LSP_COEFF; i++) { | |||
| currentqLSF[i]=ADD16(L1[L1SubsetIndex[L1Index]][i], L2L3[L3SubsetIndex[L2Index]][i]); | |||
| } | |||
| computeqLSF(currentqLSF, previousLCodeWord, L0, noiseMAPredictor, noiseMAPredictorSum); | |||
| /* convert qLSF to qLSP: qLSP = cos(qLSF) */ | |||
| for (i=0; i<NB_LSP_COEFF; i++) { | |||
| CNGChannelContext->qLSP[i] = g729Cos_Q13Q15(currentqLSF[i]); /* ouput in Q0.15 */ | |||
| } | |||
| } /* Note: Itu implementation have information to sort missing and untransmitted packets and perform reconstruction of missed SID packet when it detects it, we cannot differentiate lost vs untransmitted packet so we don't do it */ | |||
| /* compute the LP coefficients */ | |||
| interpolateqLSP(previousqLSP, CNGChannelContext->qLSP, interpolatedqLSP); | |||
| /* copy the current qLSP(SID ones) to previousqLSP buffer */ | |||
| for (i=0; i<NB_LSP_COEFF; i++) { | |||
| previousqLSP[i] = CNGChannelContext->qLSP[i]; | |||
| } | |||
| /* call the qLSP2LP function for first subframe */ | |||
| qLSP2LP(interpolatedqLSP, LP); | |||
| /* call the qLSP2LP function for second subframe */ | |||
| qLSP2LP(CNGChannelContext->qLSP, &(LP[NB_LSP_COEFF])); | |||
| /* apply target gain smoothing eq B.19 */ | |||
| if (previousFrameIsActiveFlag) { | |||
| /* this is the first SID frame use transmitted(or recomputed from last valid frame energy) SID gain */ | |||
| CNGChannelContext->smoothedSIDGain = CNGChannelContext->receivedSIDGain; | |||
| } else { /* otherwise 7/8 of last smoothed gain and 1/8 of last received gain */ | |||
| CNGChannelContext->smoothedSIDGain = SUB16(CNGChannelContext->smoothedSIDGain, (CNGChannelContext->smoothedSIDGain>>3)); | |||
| CNGChannelContext->smoothedSIDGain = ADD16(CNGChannelContext->smoothedSIDGain, (CNGChannelContext->receivedSIDGain>>3)); | |||
| } | |||
| /* get the excitation vector */ | |||
| computeComfortNoiseExcitationVector(CNGChannelContext->smoothedSIDGain, pseudoRandomSeed, excitationVector); | |||
| } | |||
| @ -0,0 +1,54 @@ | |||
| /* | |||
| cng.h | |||
| Copyright (C) 2011 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef CNG_H | |||
| #define CNG_H | |||
| /*****************************************************************************/ | |||
| /* initBcg729CNGChannel : create context structure and initialise it */ | |||
| /* return value : */ | |||
| /* - the CNG channel context data */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| bcg729CNGChannelContextStruct *initBcg729CNGChannel(); | |||
| void computeComfortNoiseExcitationVector(word16_t targetGain, uint16_t *randomGeneratorSeed, word16_t *excitationVector); | |||
| /*******************************************************************************************/ | |||
| /* decodeSIDframe : as is spec B4.4 and B4.5 */ | |||
| /* first check if we have a SID frame or a missing/untransmitted frame */ | |||
| /* for SID frame get paremeters(gain and LSP) */ | |||
| /* Then generate excitation vector and update qLSP */ | |||
| /* parameters: */ | |||
| /* -(i/o):CNGChannelContext : context containing all informations needed for CNG */ | |||
| /* -(i): previousFrameIsActiveFlag: true if last decoded frame was an active one */ | |||
| /* -(i): bitStream: for SID frame contains received params as in spec B4.3, */ | |||
| /* NULL for missing/untransmitted frame */ | |||
| /* -(i/o): excitationVector in Q0, accessed in range [-L_PAST_EXCITATION,L_FRAME-1] */ | |||
| /* [-154,79] */ | |||
| /* -(i/o): previousqLSP : previous quantised LSP in Q0.15 (NB_LSP_COEFF values) */ | |||
| /* -(o): LP: 20 LP coefficients in Q12 */ | |||
| /* -(i/o): pseudoRandomSeed : seed used in the pseudo random number generator */ | |||
| /* -(i/o): previousLCodeWord: in Q2.13, buffer to store the last 4 frames codewords, */ | |||
| /* used to compute the current qLSF */ | |||
| /* */ | |||
| /*******************************************************************************************/ | |||
| void decodeSIDframe(bcg729CNGChannelContextStruct *CNGChannelContext, uint8_t previousFrameIsActiveFlag, uint8_t *bitStream, word16_t *excitationVector, word16_t *previousqLSP, word16_t *LP, uint16_t *pseudoRandomSeed, word16_t previousLCodeWord[MA_MAX_K][NB_LSP_COEFF]); | |||
| #endif /* ifndef CNG_H */ | |||
| @ -0,0 +1,49 @@ | |||
| /* | |||
| computeLP.h | |||
| Copyright (C) 2011 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef COMPUTELP_H | |||
| #define COMPUTELP_H | |||
| /*****************************************************************************/ | |||
| /* autoCorrelation2LP: convert autocorrelation coefficients to LP using */ | |||
| /* Levinson-Durbin algo described in spec 3.2.2 */ | |||
| /* parameters : */ | |||
| /* -(i) autoCorrelationCoefficients : 11 values in variable scale */ | |||
| /* scale is not needed here as a division cancel it */ | |||
| /* -(o) LPCoefficientsQ12: 10 LP coefficients in Q12 */ | |||
| /* -(o) reflectionCoefficient: in Q31, k[1] generated during Levinson */ | |||
| /* Durbin LP coefficient generation and needed for VAD */ | |||
| /* -(o) residualEnergy : with the same scale factor as input */ | |||
| /* autoCorrelationCoefficients, needed by DTX */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| void autoCorrelation2LP(word32_t autoCorrelationCoefficients[], word16_t LPCoefficientsQ12[], word32_t *reflectionCoefficient, word32_t *residualEnergy); | |||
| /*****************************************************************************/ | |||
| /* computeLP : As described in spec 3.2.1 and 3.2.2 : Windowing, */ | |||
| /* Autocorrelation and Levinson-Durbin algorithm */ | |||
| /* parameters: */ | |||
| /* -(i) signal: 240 samples in Q0, the last 40 are from next frame */ | |||
| /* -(o) LPCoefficientsQ12: 10 LP coefficients in Q12 */ | |||
| /* -(o) reflectionCoefficient: in Q31, k[1] generated during Levinson */ | |||
| /* Durbin LP coefficient generation and needed for VAD */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| void computeLP(word16_t signal[], word16_t LPCoefficientsQ12[], word32_t *reflectionCoefficient, word32_t autoCorrelationCoefficients[], word32_t noLagAutocorrelationCoefficients[], int8_t *autoCorrelationCoefficientsScale, uint8_t autoCorrelationCoefficientsNumber); | |||
| #endif /* ifndef COMPUTELP_H */ | |||
| @ -0,0 +1,431 @@ | |||
| /* | |||
| dtx.c | |||
| Comfort Noise Generation | |||
| Copyright (C) 2015 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include "typedef.h" | |||
| #include "codecParameters.h" | |||
| #include "basicOperationsMacros.h" | |||
| #include "utils.h" | |||
| #include "dtx.h" | |||
| #include "computeLP.h" | |||
| #include "g729FixedPointMath.h" | |||
| #include "LP2LSPConversion.h" | |||
| #include "LSPQuantization.h" | |||
| #include "codebooks.h" | |||
| #include "cng.h" | |||
| #include "interpolateqLSP.h" | |||
| #include "qLSP2LP.h" | |||
| #define SID_FRAME 2 | |||
| #define UNTRANSMITTED_FRAME 0 | |||
| /*** local functions ***/ | |||
| static void sumAutocorrelationCoefficients(word32_t autoCorrelationCoefficients[][NB_LSP_COEFF+1], int8_t *autocorrelationCoefficientsScale, uint8_t nbElements, | |||
| word32_t *autoCorrelationCoefficientsResult, int8_t *autocorrelationCoefficientsScaleResults) { | |||
| int i,j; | |||
| word64_t autoCorrelationSumBuffer[NB_LSP_COEFF+1]; /* used to temporary store sum on 64 bits */ | |||
| word64_t max=0; | |||
| word32_t rescaledAutocorrelationCoefficients[7][NB_LSP_COEFF+1]; /* used to temporary stored rescaled elements */ | |||
| int8_t rightShiftToNormalise = 0; | |||
| /* get lowest scale */ | |||
| int8_t minScale = autocorrelationCoefficientsScale[0]; | |||
| for (i=1; i<nbElements; i++) { | |||
| if (autocorrelationCoefficientsScale[i]<minScale) { | |||
| minScale=autocorrelationCoefficientsScale[i]; | |||
| } | |||
| } | |||
| /* rescale the coefficients */ | |||
| for (j=0; j<nbElements; j++) { | |||
| int8_t rescaling = autocorrelationCoefficientsScale[j] - minScale; | |||
| for (i=0; i<NB_LSP_COEFF+1; i++) { | |||
| rescaledAutocorrelationCoefficients[j][i] = SHR32(autoCorrelationCoefficients[j][i], rescaling); | |||
| } | |||
| } | |||
| /* sum them on 64 bits and get the maximum value reached */ | |||
| for (i=0; i<NB_LSP_COEFF+1; i++) { | |||
| autoCorrelationSumBuffer[i] = rescaledAutocorrelationCoefficients[0][i]; | |||
| for (j=1; j<nbElements; j++) { | |||
| autoCorrelationSumBuffer[i] = ADD64(autoCorrelationSumBuffer[i], rescaledAutocorrelationCoefficients[j][i]); | |||
| } | |||
| if (ABS(autoCorrelationSumBuffer[i])>max) max = ABS(autoCorrelationSumBuffer[i]); | |||
| } | |||
| /* normalise result on 32 bits */ | |||
| if (max>MAXINT32) { | |||
| do { | |||
| max = SHR(max,1); | |||
| rightShiftToNormalise++; | |||
| } while (max>MAXINT32); | |||
| for (i=0; i<NB_LSP_COEFF+1; i++) { | |||
| autoCorrelationCoefficientsResult[i] = (word32_t)SHR64(autoCorrelationSumBuffer[i], rightShiftToNormalise); | |||
| } | |||
| } else { | |||
| for (i=0; i<NB_LSP_COEFF+1; i++) { | |||
| autoCorrelationCoefficientsResult[i] = (word32_t)(autoCorrelationSumBuffer[i]); | |||
| } | |||
| } | |||
| /* adjust output scale according to possible right shift */ | |||
| *autocorrelationCoefficientsScaleResults = minScale - rightShiftToNormalise; | |||
| return; | |||
| } | |||
| /*****************************************************************************/ | |||
| /* residual Energy quantization according to spec B4.2.1 */ | |||
| /* parameters: */ | |||
| /* -(i) residualEnergy : to be quantized in variable scale */ | |||
| /* -(i) residualEnergyScale : scale of previous parameter */ | |||
| /* -(o) decodedLogEnergy : decode the quantized energy into a 10Log(E) */ | |||
| /* returns the quantized residual energy parameterR(on 5 bits) */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| static uint8_t residualEnergyQuantization(word32_t residualEnergy, int8_t residualEnergyScale, int8_t *decodedLogEnergy) { | |||
| word32_t acc; | |||
| //acc = SUB32(g729Log2_Q0Q16(residualEnergy), ADD32(676458, (int32_t)(residualEnergyScale<<16))); /* -676458 is log2(aw/(NCur*80)) acc is log2(E') in Q16 aw=0.125*/ | |||
| //acc = SUB32(g729Log2_Q0Q16(residualEnergy), ADD32(610922, (int32_t)(residualEnergyScale<<16))); /* -676458 is log2(aw/(NCur*80)) acc is log2(E') in Q16 aw=0.25*/ | |||
| acc = SUB32(g729Log2_Q0Q16(residualEnergy), ADD32(479849, (int32_t)(residualEnergyScale<<16))); /* -479849 is log2(aw/(NCur*80)) acc is log2(E') in Q16 aw = 1 */ | |||
| acc = SHR32(acc,1); /* acc = log2(E') in Q15 */ | |||
| acc = MULT16_32_Q15(INV_LOG2_10_Q15, acc); /* acc log10(E') in Q15 */ | |||
| /* quantization acc contains value to be quantized/10 so all constant / 10 respect what is in the spec */ | |||
| if (acc < - 26214) { /* -0.8 in Q15 */ | |||
| *decodedLogEnergy = -12; | |||
| return 0; /* first step by 8 */ | |||
| } else if (acc < 45875 ) { /* 1,4 in Q15 */ | |||
| uint8_t steps; | |||
| acc = (acc+19661); /* up to 14, step by 0.4*/ | |||
| if (acc<0) { | |||
| acc= 0; | |||
| } else { | |||
| acc = MULT16_32_Q13(20480, acc); /* step by 0.4, 20480 is 1/0.4 in Q13 -> acc still in Q15 */ | |||
| } | |||
| steps = SHR(acc,15); | |||
| *decodedLogEnergy = -2 +4*steps; | |||
| return 1+steps; | |||
| } else if (acc<216268) { /* check that log(E') is not > 66dB (216268 is 6.6 in Q15) */ | |||
| uint8_t steps; | |||
| acc = (acc-49152); /* -1.5 in Q15 */ | |||
| if (acc<0) { | |||
| acc = 0; | |||
| } else { | |||
| acc = MULT16_32_Q12(20480, acc); /* step by 0.2, 20480 is 1/0.2 in Q12 -> acc still in Q15 */ | |||
| } | |||
| steps = SHR(acc,15); | |||
| *decodedLogEnergy = 16 +2*steps; | |||
| return 6+steps; | |||
| } else { /* quantized energy support up to 66dB */ | |||
| *decodedLogEnergy = 66; | |||
| return 32; | |||
| } | |||
| } | |||
| /*****************************************************************************/ | |||
| /* compute LP Coefficients auto correlation as in eq B.13 */ | |||
| /* parameters: */ | |||
| /* -(i) LPCoefficients : in Q12, 10 values, 1 LP Coeff is always 1 and not*/ | |||
| /* stored in this buffer */ | |||
| /* -(o) LPCautocorrelation : 11 values in Q20 */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| static void computeLPCoefficientAutocorrelation(word16_t *LPCoefficients, word32_t *LPCautocorrelation) { | |||
| int j,k; | |||
| /* first compute Ra0 */ | |||
| LPCautocorrelation[0] = 4096*4096>>4; /* LPCoefficients are in Q12 -> acc in Q24, init: acc = LP(0)*LP(0) -> 1 in Q20 as LP(0) is not stored because always 1 */ | |||
| for (k=0; k<NB_LSP_COEFF; k++) { | |||
| LPCautocorrelation[0] = MAC16_16_Q4(LPCautocorrelation[0], LPCoefficients[k], LPCoefficients[k]); /* LPCoefficients in Q12*Q12 -> Q24 >> Q4: result in Q20 */ | |||
| } | |||
| /* and the rest */ | |||
| for (j=1; j<NB_LSP_COEFF+1; j++) { | |||
| LPCautocorrelation[j] = SHL(LPCoefficients[j-1],9); /* LPCoeff[0] is not stored always 1, so LPCoeff index is -1 respect LPCautocorrelation, SHL(9) to make *2 and get it in Q20 from Q12 */ | |||
| for (k=0; k<10-j; k++) { | |||
| LPCautocorrelation[j] = MAC16_16_Q3(LPCautocorrelation[j], LPCoefficients[k], LPCoefficients[k+j]); /* this k is actually k-1 respect to eq B.13 Q12*Q12 -> Q24 >> 3 : *2 and result in Q20 */ | |||
| } | |||
| } | |||
| } | |||
| /********************************************************************************/ | |||
| /* compare LPC filters: as in spec B.4.1.3 eq B.12 */ | |||
| /* parameters: */ | |||
| /* -(i) LPCoefficientsAutocorrelation: 11 values in Q20, Ra in spec */ | |||
| /* -(i) autocorrelationCoefficients: 11 values in variable scale, Rt in spec */ | |||
| /* -(i) residualEnergy : in the same scale as previous value, Et in spec */ | |||
| /* -(i) threshold : in Q20 */ | |||
| /* return 1 if the filter significantly differs (eq B.12 is true) */ | |||
| /* */ | |||
| /********************************************************************************/ | |||
| static uint8_t compareLPCFilters(word32_t *LPCoefficientsAutocorrelation, word32_t *autocorrelationCoefficients, word32_t residualEnergy, word32_t threshold) { | |||
| /* on the left term of the comparison we have Ra in Q20 an Rt in variable scale */ | |||
| /* on the right term of the comparison we have Threshold in Q20 an Et in variable scale but same as Rt */ | |||
| word64_t acc = 0; | |||
| int i; | |||
| for (i=0; i<NB_LSP_COEFF+1; i++) { | |||
| acc = MAC64(acc, LPCoefficientsAutocorrelation[i], autocorrelationCoefficients[i]); | |||
| } | |||
| if (acc >= MULT32_32(residualEnergy, threshold)) { | |||
| return 1; | |||
| } else { | |||
| return 0; | |||
| } | |||
| } | |||
| /*****************************************************************************/ | |||
| /* initBcg729DTXChannel : create context structure and initialise it */ | |||
| /* return value : */ | |||
| /* - the DTX channel context data */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| bcg729DTXChannelContextStruct *initBcg729DTXChannel() { | |||
| /* create the context structure */ | |||
| bcg729DTXChannelContextStruct *DTXChannelContext = malloc(sizeof(bcg729DTXChannelContextStruct)); | |||
| memset(DTXChannelContext, 0, sizeof(*DTXChannelContext)); /* set autocorrelation buffers to 0 */ | |||
| DTXChannelContext->previousVADflag = 1; /* previous VAD flag must be initialised to VOICE */ | |||
| DTXChannelContext->pseudoRandomSeed = CNG_DTX_RANDOM_SEED_INIT; | |||
| return DTXChannelContext; | |||
| } | |||
| /*******************************************************************************************/ | |||
| /* updateDTXContext : save autocorrelation value in DTX context as requested in B4.1.1 */ | |||
| /* parameters: */ | |||
| /* -(i/o) DTXChannelContext : the DTX context to be updated */ | |||
| /* -(i) autocorrelationsCoefficients : 11 values of variable scale, values are copied */ | |||
| /* in DTX context */ | |||
| /* -(i) autocorrelationCoefficientsScale : the scale of previous buffer(can be <0) */ | |||
| /* */ | |||
| /*******************************************************************************************/ | |||
| void updateDTXContext(bcg729DTXChannelContextStruct *DTXChannelContext, word32_t *autocorrelationCoefficients, int8_t autocorrelationCoefficientsScale) { | |||
| /* move previous autocorrelation coefficients and store the new one */ | |||
| /* TODO: optimise it buy using rolling index */ | |||
| memcpy(DTXChannelContext->autocorrelationCoefficients[6], DTXChannelContext->autocorrelationCoefficients[5], (NB_LSP_COEFF+1)*sizeof(word32_t)); | |||
| DTXChannelContext->autocorrelationCoefficientsScale[6] = DTXChannelContext->autocorrelationCoefficientsScale[5]; | |||
| memcpy(DTXChannelContext->autocorrelationCoefficients[5], DTXChannelContext->autocorrelationCoefficients[4], (NB_LSP_COEFF+1)*sizeof(word32_t)); | |||
| DTXChannelContext->autocorrelationCoefficientsScale[5] = DTXChannelContext->autocorrelationCoefficientsScale[4]; | |||
| memcpy(DTXChannelContext->autocorrelationCoefficients[4], DTXChannelContext->autocorrelationCoefficients[3], (NB_LSP_COEFF+1)*sizeof(word32_t)); | |||
| DTXChannelContext->autocorrelationCoefficientsScale[4] = DTXChannelContext->autocorrelationCoefficientsScale[3]; | |||
| memcpy(DTXChannelContext->autocorrelationCoefficients[3], DTXChannelContext->autocorrelationCoefficients[2], (NB_LSP_COEFF+1)*sizeof(word32_t)); | |||
| DTXChannelContext->autocorrelationCoefficientsScale[3] = DTXChannelContext->autocorrelationCoefficientsScale[2]; | |||
| memcpy(DTXChannelContext->autocorrelationCoefficients[2], DTXChannelContext->autocorrelationCoefficients[1], (NB_LSP_COEFF+1)*sizeof(word32_t)); | |||
| DTXChannelContext->autocorrelationCoefficientsScale[2] = DTXChannelContext->autocorrelationCoefficientsScale[1]; | |||
| memcpy(DTXChannelContext->autocorrelationCoefficients[1], DTXChannelContext->autocorrelationCoefficients[0], (NB_LSP_COEFF+1)*sizeof(word32_t)); | |||
| DTXChannelContext->autocorrelationCoefficientsScale[1] = DTXChannelContext->autocorrelationCoefficientsScale[0]; | |||
| memcpy(DTXChannelContext->autocorrelationCoefficients[0], autocorrelationCoefficients, (NB_LSP_COEFF+1)*sizeof(word32_t)); | |||
| DTXChannelContext->autocorrelationCoefficientsScale[0] = autocorrelationCoefficientsScale; | |||
| } | |||
| /*******************************************************************************************/ | |||
| /* encodeSIDFrame: called at eache frame even if VADflag is set to active speech */ | |||
| /* Update the previousVADflag and if curent is set to NOISE, compute the SID params */ | |||
| /* parameters: */ | |||
| /* -(i/o) DTXChannelContext: current DTX context, is updated by this function */ | |||
| /* -(o) previousLSPCoefficients : 10 values in Q15, is updated by this function */ | |||
| /* -(i/o) previousqLSPCoefficients : 10 values in Q15, is updated by this function */ | |||
| /* -(i) VADflag : 1 active voice frame, 0 noise frame */ | |||
| /* -(i/o) previousqLSF : set of 4 last frames qLSF in Q2.13, is updated */ | |||
| /* -(i/o) excicationVector : in Q0, accessed in range [-L_PAST_EXCITATION,L_FRAME-1] */ | |||
| /* -(o) qLPCoefficients : 20 values in Q3.12 the quantized LP coefficients */ | |||
| /* -(o) bitStream : SID frame parameters on 2 bytes, may be null if no frame is to be */ | |||
| /* transmitted */ | |||
| /* -(o) bitStreamLength : length of bitStream buffer to be transmitted (2 for SID, 0 for */ | |||
| /* untransmitted frame) */ | |||
| /* */ | |||
| /*******************************************************************************************/ | |||
| void encodeSIDFrame(bcg729DTXChannelContextStruct *DTXChannelContext, word16_t *previousLSPCoefficients, word16_t *previousqLSPCoefficients, uint8_t VADflag, word16_t previousqLSF[MA_MAX_K][NB_LSP_COEFF], word16_t *excitationVector, word16_t *qLPCoefficients, uint8_t *bitStream, uint8_t *bitStreamLength) { | |||
| int i; | |||
| word32_t summedAutocorrelationCoefficients[NB_LSP_COEFF+1]; | |||
| word16_t LPCoefficients[NB_LSP_COEFF]; /* in Q12 */ | |||
| word16_t LSPCoefficients[NB_LSP_COEFF]; /* in Q15 */ | |||
| word32_t reflectionCoefficient; /* not used here, by product of LP coefficients computation */ | |||
| word32_t residualEnergy; /* in variable scale(summedAutocorrelationCoefficientsScale) computed together with LP coefficients */ | |||
| int8_t summedAutocorrelationCoefficientsScale; | |||
| uint8_t frameType; | |||
| word32_t meanEnergy; | |||
| int8_t meanEnergyScale; | |||
| uint8_t quantizedResidualEnergy; | |||
| int8_t decodedLogEnergy; | |||
| uint8_t parameters[3]; /* array of the first 3 output parameters, 4th is in quantizedResidualEnergy */ | |||
| word16_t interpolatedqLSP[NB_LSP_COEFF]; /* the interpolated qLSP used for first subframe in Q15 */ | |||
| if (VADflag == 1) {/* this is a voice frame, just update the VADflag history and return */ | |||
| DTXChannelContext->pseudoRandomSeed = CNG_DTX_RANDOM_SEED_INIT; /* re-init pseudo random seed at each active frame to keep CNG and DTX in sync */ | |||
| DTXChannelContext->previousVADflag = 1; | |||
| return; | |||
| } | |||
| /* NOISE frame */ | |||
| /* compute the autocorrelation coefficients sum on the current and previous frame */ | |||
| sumAutocorrelationCoefficients((DTXChannelContext->autocorrelationCoefficients), DTXChannelContext->autocorrelationCoefficientsScale, 2, | |||
| summedAutocorrelationCoefficients, &summedAutocorrelationCoefficientsScale); | |||
| /* compute LP filter coefficients */ | |||
| autoCorrelation2LP(summedAutocorrelationCoefficients, LPCoefficients, &reflectionCoefficient, &residualEnergy); /* output residualEnergy with the same scale of input summedAutocorrelationCoefficients */ | |||
| /* determine type of frame SID or untrasmitted */ | |||
| if (DTXChannelContext->previousVADflag == 1) { /* if previous frame was active : we must generate a SID frame spec B.10 */ | |||
| frameType = SID_FRAME; | |||
| meanEnergy = residualEnergy; | |||
| meanEnergyScale = summedAutocorrelationCoefficientsScale; | |||
| quantizedResidualEnergy = residualEnergyQuantization(meanEnergy, meanEnergyScale, &decodedLogEnergy); | |||
| } else { /* previous frame was already non active, check if we have to generate a new SID frame according to spec 4.1.2-4.1.4 */ | |||
| int8_t flag_chang = 0; | |||
| /* update meanEnergy using current and previous frame Energy : meanE = current+previous/2 : rescale both of them dividing by 2 and sum */ | |||
| /* eqB14 doesn't divide by KE but it's done in eqB15, do it now */ | |||
| if (summedAutocorrelationCoefficientsScale<DTXChannelContext->previousResidualEnergyScale) { | |||
| meanEnergyScale = summedAutocorrelationCoefficientsScale; | |||
| meanEnergy = ADD32(SHR(residualEnergy,1), VSHR32(DTXChannelContext->previousResidualEnergy, DTXChannelContext->previousResidualEnergyScale - summedAutocorrelationCoefficientsScale + 1)); | |||
| } else { | |||
| meanEnergyScale = DTXChannelContext->previousResidualEnergyScale; | |||
| meanEnergy = ADD32(VSHR32(residualEnergy,summedAutocorrelationCoefficientsScale - DTXChannelContext->previousResidualEnergyScale + 1), SHR(DTXChannelContext->previousResidualEnergy, 1)); | |||
| } | |||
| quantizedResidualEnergy = residualEnergyQuantization(meanEnergy, meanEnergyScale, &decodedLogEnergy); | |||
| /* comparison of LPC filters B4.1.3 : DTXChannelContext->SIDLPCoefficientAutocorrelation contains the last used filter LP coeffecients autocorrelation in Q20 */ | |||
| if (compareLPCFilters(DTXChannelContext->SIDLPCoefficientAutocorrelation, summedAutocorrelationCoefficients, residualEnergy, THRESHOLD1_IN_Q20) != 0) { | |||
| flag_chang = 1; | |||
| } | |||
| /* comparison of the energies B4.1.4 */ | |||
| if (ABS(DTXChannelContext->previousDecodedLogEnergy - decodedLogEnergy)>2) { | |||
| flag_chang = 1; | |||
| } | |||
| /* check if we have to transmit a SID frame eq B.11 */ | |||
| DTXChannelContext->count_fr++; | |||
| if (DTXChannelContext->count_fr<3) { /* min 3 frames between 2 consecutive SID frames */ | |||
| frameType = UNTRANSMITTED_FRAME; | |||
| } else { | |||
| if (flag_chang == 1) { | |||
| frameType = SID_FRAME; | |||
| } else { | |||
| frameType = UNTRANSMITTED_FRAME; | |||
| } | |||
| DTXChannelContext->count_fr = 3; /* counter on 8 bits, keep value low, we just need to know if it is > 3 */ | |||
| } | |||
| } | |||
| /* generate the SID frame */ | |||
| if (frameType == SID_FRAME) { | |||
| word32_t SIDLPCAutocorrelationCoefficients[NB_LSP_COEFF+1]; | |||
| int8_t SIDLPCAutocorrelationCoefficientsScale; | |||
| word16_t pastAverageLPCoefficients[NB_LSP_COEFF]; /* in Q12 */ | |||
| word32_t pastAverageReflectionCoefficient; /* not used here, by product of LP coefficients computation */ | |||
| word32_t pastAverageResidualEnergy; /* not used here, by-product of LP coefficients computation */ | |||
| /* reset frame count */ | |||
| DTXChannelContext->count_fr = 0; | |||
| /*** compute the past average filter on the last 6 past frames ***/ | |||
| sumAutocorrelationCoefficients(&(DTXChannelContext->autocorrelationCoefficients[1]), &(DTXChannelContext->autocorrelationCoefficientsScale[1]), 6, | |||
| SIDLPCAutocorrelationCoefficients, &SIDLPCAutocorrelationCoefficientsScale); | |||
| /* compute past average LP filter coefficients Ap in B4.2.2 */ | |||
| autoCorrelation2LP(SIDLPCAutocorrelationCoefficients, pastAverageLPCoefficients, &pastAverageReflectionCoefficient, &pastAverageResidualEnergy); /* output residualEnergy with the same scale of input summedAutocorrelationCoefficients */ | |||
| /* select coefficients according to eq B.17 we have Ap in SIDLPCoefficients and At in LPCoefficients, store result, in Q12 in SIDLPCoefficients */ | |||
| /* check distance beetwen currently used filter and past filter : compute LPCoefficentAutocorrelation for the past average filter */ | |||
| computeLPCoefficientAutocorrelation(pastAverageLPCoefficients, DTXChannelContext->SIDLPCoefficientAutocorrelation); | |||
| if (compareLPCFilters(DTXChannelContext->SIDLPCoefficientAutocorrelation, summedAutocorrelationCoefficients, residualEnergy, THRESHOLD3_IN_Q20) == 0) { /* use the past average filter */ | |||
| /* generate LSP coefficient using the past LP coefficients */ | |||
| if (!LP2LSPConversion(pastAverageLPCoefficients, LSPCoefficients)) { | |||
| /* unable to find the 10 roots repeat previous LSP */ | |||
| memcpy(LSPCoefficients, previousqLSPCoefficients, NB_LSP_COEFF*sizeof(word16_t)); | |||
| } | |||
| /* LPCoefficientAutocorrelation are already in DTXChannelContext */ | |||
| } else { /* use filter computed on current and previous frame */ | |||
| /* compute the LPCoefficientAutocorrelation for this filter and store them in DTXChannel */ | |||
| computeLPCoefficientAutocorrelation(LPCoefficients, DTXChannelContext->SIDLPCoefficientAutocorrelation); | |||
| /* generate LSP coefficient current LP coefficients */ | |||
| if (!LP2LSPConversion(LPCoefficients, LSPCoefficients)) { | |||
| /* unable to find the 10 roots repeat previous LSP */ | |||
| memcpy(LSPCoefficients, previousqLSPCoefficients, NB_LSP_COEFF*sizeof(word16_t)); | |||
| } | |||
| } | |||
| /* update previousLSP coefficient buffer */ | |||
| memcpy(previousLSPCoefficients, LSPCoefficients, NB_LSP_COEFF*sizeof(word16_t)); | |||
| /* LSP quantization */ | |||
| noiseLSPQuantization(previousqLSF, LSPCoefficients, DTXChannelContext->qLSPCoefficients, parameters); | |||
| /* update previousDecodedLogEnergy and SIDGain */ | |||
| DTXChannelContext->previousDecodedLogEnergy = decodedLogEnergy; | |||
| DTXChannelContext->currentSIDGain = SIDGainCodebook[quantizedResidualEnergy]; | |||
| } | |||
| /* save current Frame Energy */ | |||
| DTXChannelContext->previousResidualEnergy = residualEnergy; | |||
| DTXChannelContext->previousResidualEnergyScale = summedAutocorrelationCoefficientsScale; | |||
| /* apply target gain smoothing eq B.19 */ | |||
| if(DTXChannelContext->previousVADflag == 1) { | |||
| DTXChannelContext->smoothedSIDGain = DTXChannelContext->currentSIDGain; | |||
| } else { | |||
| DTXChannelContext->smoothedSIDGain = SUB16(DTXChannelContext->smoothedSIDGain, (DTXChannelContext->smoothedSIDGain>>3)); | |||
| DTXChannelContext->smoothedSIDGain = ADD16(DTXChannelContext->smoothedSIDGain, (DTXChannelContext->currentSIDGain>>3)); | |||
| } | |||
| /* update excitation vector */ | |||
| computeComfortNoiseExcitationVector(DTXChannelContext->smoothedSIDGain, &(DTXChannelContext->pseudoRandomSeed), excitationVector); | |||
| /* Interpolate qLSP and update the previousqLSP buffer */ | |||
| interpolateqLSP(previousqLSPCoefficients, DTXChannelContext->qLSPCoefficients, interpolatedqLSP); /* in case of untransmitted frame, use qLSP generated for the last transmitted one */ | |||
| for (i=0; i<NB_LSP_COEFF; i++) { | |||
| previousqLSPCoefficients[i] = DTXChannelContext->qLSPCoefficients[i]; | |||
| } | |||
| /* first subframe */ | |||
| qLSP2LP(interpolatedqLSP, qLPCoefficients); | |||
| /* second subframe */ | |||
| qLSP2LP(DTXChannelContext->qLSPCoefficients, &(qLPCoefficients[NB_LSP_COEFF])); | |||
| /* set parameters into the bitStream if a frame must be transmitted */ | |||
| if (frameType == SID_FRAME) { | |||
| *bitStreamLength = 2; | |||
| bitStream[0] = (((parameters[0]&0x01)<<7) /* L0 1 bit */ | |||
| | ((parameters[1]&0x1F)<<2) /* L1 5 bits */ | |||
| | ((parameters[2]>>2)%0x03)); /* L2 is 4 bits 2 MSB in this byte */ | |||
| bitStream[1] = (((parameters[2]&0x03)<<6) /* 2 LSB of 4 bits L2 */ | |||
| | ((quantizedResidualEnergy&0x1F)<<1)); /* Gain 5 bits, last bit is left to 0 */ | |||
| } else { | |||
| *bitStreamLength = 0; | |||
| } | |||
| /* update the previousVADflag in context */ | |||
| DTXChannelContext->previousVADflag = 0; | |||
| } | |||
| @ -0,0 +1,61 @@ | |||
| /* | |||
| dtx.h | |||
| Copyright (C) 2015 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef DTX_H | |||
| #define DTX_H | |||
| /*****************************************************************************/ | |||
| /* initBcg729DTXChannel : create context structure and initialise it */ | |||
| /* return value : */ | |||
| /* - the DTX channel context data */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| bcg729DTXChannelContextStruct *initBcg729DTXChannel(); | |||
| /*******************************************************************************************/ | |||
| /* updateDTXContext : save autocorrelation value in DTX context as requested in B4.1.1 */ | |||
| /* parameters: */ | |||
| /* -(i/o) DTXChannelContext : the DTX context to be updated */ | |||
| /* -(i) autocorrelationsCoefficients : 11 values of variable scale, values are copied */ | |||
| /* in DTX context */ | |||
| /* -(i) autocorrelationCoefficientsScale : the scale of previous buffer(can be <0) */ | |||
| /* */ | |||
| /*******************************************************************************************/ | |||
| void updateDTXContext(bcg729DTXChannelContextStruct *DTXChannelContext, word32_t *autocorrelationCoefficients, int8_t autocorrelationCoefficientsScale); | |||
| /*******************************************************************************************/ | |||
| /* encodeSIDFrame: called at eache frame even if VADflag is set to active speech */ | |||
| /* Update the previousVADflag and if curent is set to NOISE, compute the SID params */ | |||
| /* parameters: */ | |||
| /* -(i/o) DTXChannelContext: current DTX context, is updated by this function */ | |||
| /* -(o) previousLSPCoefficients : 10 values in Q15, is updated by this function */ | |||
| /* -(i/o) previousqLSPCoefficients : 10 values in Q15, is updated by this function */ | |||
| /* -(i) VADflag : 1 active voice frame, 0 noise frame */ | |||
| /* -(i/o) previousqLSF : set of 4 last frames qLSF in Q2.13, is updated */ | |||
| /* -(i/o) excicationVector : in Q0, accessed in range [-L_PAST_EXCITATION,L_FRAME-1] */ | |||
| /* -(o) qLPCoefficients : 20 values in Q3.12 the quantized LP coefficients */ | |||
| /* -(o) bitStream : SID frame parameters on 2 bytes, may be null if no frame is to be */ | |||
| /* transmitted */ | |||
| /* -(o) bitStreamLength : length of bitStream buffer to be transmitted (2 for SID, 0 for */ | |||
| /* untransmitted frame) */ | |||
| /* */ | |||
| /*******************************************************************************************/ | |||
| void encodeSIDFrame(bcg729DTXChannelContextStruct *DTXChannelContext, word16_t *previousLSPCoefficients, word16_t *previousqLSPCoefficients, uint8_t VADflag, word16_t previousqLSF[MA_MAX_K][NB_LSP_COEFF], word16_t *excitationVector, word16_t *qLPCoefficients, uint8_t *bitStream, uint8_t *bitStreamLength); | |||
| #endif /* ifndef DTX_H */ | |||
| @ -0,0 +1,422 @@ | |||
| /* | |||
| vad.c | |||
| Voice Activity Detection | |||
| Copyright (C) 2015 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include "typedef.h" | |||
| #include "codecParameters.h" | |||
| #include "basicOperationsMacros.h" | |||
| #include "utils.h" | |||
| #include "g729FixedPointMath.h" | |||
| #include "codebooks.h" | |||
| #include "vad.h" | |||
| #define VOICE 1 | |||
| #define NOISE 0 | |||
| bcg729VADChannelContextStruct *initBcg729VADChannel() { | |||
| int i; | |||
| /* create the context structure */ | |||
| bcg729VADChannelContextStruct *VADChannelContext = malloc(sizeof(bcg729VADChannelContextStruct)); | |||
| memset(VADChannelContext, 0, sizeof(*VADChannelContext)); /* set meanLSF buffer to 0 */ | |||
| VADChannelContext->frameCount = 0; | |||
| VADChannelContext->updateCount = 0; | |||
| for (i=0; i<N0; i++) { | |||
| VADChannelContext->EfBuffer[i] = MAXINT16; | |||
| } | |||
| VADChannelContext->SVDm1 = VOICE; | |||
| VADChannelContext->SVDm2 = VOICE; | |||
| VADChannelContext->Count_inert = 0; | |||
| VADChannelContext->secondStageVADSmoothingFlag = 1; | |||
| VADChannelContext->smoothingCounter = 0; | |||
| VADChannelContext->previousFrameEf = 0; | |||
| VADChannelContext->noiseContinuityCounter = 0; | |||
| return VADChannelContext; | |||
| } | |||
| /*******************************************************************************************/ | |||
| /* multiBoundaryInitialVoiceActivityDecision as described in B3.5 */ | |||
| /* parameters: */ | |||
| /* -(i) deltaS spectral distorsion as decribed in B3.4.1 in Q13 */ | |||
| /* -(i) deltaEf full band energy difference as described in B3.4.2 in Q11 */ | |||
| /* -(i) deltaEl low band energy difference as described in B3.4.3 in Q11 */ | |||
| /* -(i) deltaZC Zero Crossing difference as described in B3.4.4 in Q15 */ | |||
| /* returns : Ivd as specified in B3.5 : VOICE or NOISE */ | |||
| /* */ | |||
| /*******************************************************************************************/ | |||
| static uint8_t multiBoundaryInitialVoiceActivityDecision(word32_t deltaS, word16_t deltaEf, word16_t deltaEl, word16_t deltaZC) { | |||
| //word32_t acc1, acc2; | |||
| /* test the 14 conditions of B3.5 */ | |||
| /* Note : the constant used in itu code are not the one in spec. Code seems erratic with comment irrelevant, use constant found in floating point version, annex C+ */ | |||
| /* static FLOAT a[14] = { 1.750000e-03, -4.545455e-03, -2.500000e+01, | |||
| * ( 2.000000e+01, 0.000000e+00, 8.800000e+03, | |||
| * 0.000000e+0VADChannelContext->0, 2.5e+01, -2.909091e+01, | |||
| * 0.000000e+00, 1.400000e+04, 0.928571, | |||
| * -1.500000e+00, 0.714285}; | |||
| * | |||
| * static FLOAT b[14] = { 0.00085, 0.001159091, -5.0, | |||
| * -6.0, -4.7, -12.2, | |||
| * 0.0009, -7.0, -4.8182, | |||
| * -5.3, -15.5, 1.14285, | |||
| * -9.0, -2.1428571}; | |||
| * These constants are for Ef and El computed correctlVADChannelContext->y, we have Ef/10 and El/10 in buffers, and lsf normalised (/2pi)-> deltaS/4pi^2 | |||
| */ | |||
| /* adjust input parameters */ | |||
| word32_t deltaEf32 = MULT16_16(10, deltaEf); /* in Q11 */ | |||
| word32_t deltaEl32 = MULT16_16(10, deltaEl); /* in Q11 */ | |||
| deltaS = MULT16_32(830, deltaS); /* 830 is 1/(4*pi^2) in Q15, deltaS in Q28 now */ | |||
| /* cond 1 : deltaS > a1*deltaZC + b1 */ | |||
| if (deltaS > ADD32(MULT16_32_Q12(deltaZC, 58720), 228170)) { /* deltaS in Q28 - deltaZC in Q15*58720(1.75e-3 in Q25) >> 12 -> result in Q28 + 228170 (0.00085 in Q28) */ | |||
| return VOICE; | |||
| } | |||
| /* cond 2 : deltaS > a2*deltaZC + b2 */ | |||
| if (deltaS > ADD32(MULT16_32_Q12(deltaZC, -152520), 311141)) { /* deltaS in Q28 deltaZC in Q15*-152520(-4.545455e-03 in Q25) >> 12 -> result in Q28 + 311141 (0.001159091 in Q28) */ | |||
| return VOICE; | |||
| } | |||
| /* cond 3 : deltaEf < a3*deltaZC + b3 */ | |||
| if (deltaEf32 < ADD32(MULT16_32_Q15(deltaZC, -51200), -10240)) { /* deltaEf32 in Q11 - deltaZC in Q15*-51200(-2.50e+01 in Q11) >> 15 -> result in Q11 - 10240 (-5 in Q11) */ | |||
| return VOICE; | |||
| } | |||
| /* cond 4 : deltaEf < a4*deltaZC + b4 */ | |||
| if (deltaEf32 < ADD32(MULT16_32_Q15(deltaZC, 40960), -12288)) { /* deltaEf32 in Q11 - deltaZC in Q15*40960(2.0e+01 in Q11) >> 15 -> result in Q11 - 12288 (-6.0 in Q11) */ | |||
| return VOICE; | |||
| } | |||
| /* cond 5 : deltaEf < b5 */ | |||
| if (deltaEf32 < -9626) { /* deltaEf32 in Q11 - -4.7 in Q11 -> 9626 */ | |||
| return VOICE; | |||
| } | |||
| /* cond 6 : deltaEf < a6*deltaS + b6 */ | |||
| if (deltaEf32 < ADD32(MULT16_32_Q12(275,deltaS), -24986)) { /* deltaEf32 in Q11 - deltaS in Q28*275(8.8e+03 in Q-5) >> 12 -> result in Q11 - -24986 (-12.2 in Q11) */ | |||
| return VOICE; | |||
| } | |||
| /* cond 7 : deltaS > b7 */ | |||
| if (deltaS > 241592) { /* deltaS in Q28 - 0.0009 in Q28 -> 241592 */ | |||
| return VOICE; | |||
| } | |||
| /* cond 8 : deltaEf < a8*deltaZC + b8 */ | |||
| if (deltaEf32 < ADD32(MULT16_32_Q15(deltaZC, 51200), -14336)) { /* deltaEf32 in Q11 - deltaZC in Q15*51200(2.50e+01 in Q11) >> 15 -> result in Q11 - -14336 (-7 in Q11) */ | |||
| return VOICE; | |||
| } | |||
| /* cond 9 : deltaEf < a9*deltaZC + b9 */ | |||
| if (deltaEf32 < ADD32(MULT16_32_Q15(deltaZC, -59578), -9868)) { /* deltaEf32 in Q11 - deltaZC in Q15*59578(-2.909091e+01 in Q11) >> 15 -> result in Q11 - -9868 (-4.8182 in Q11) */ | |||
| return VOICE; | |||
| } | |||
| /* cond 10 : deltaEf < b10 */ | |||
| /* Note: cond 10 is totally useless : already covered by cond 5 before - skip it.*/ | |||
| if (deltaEf32 < -10854) { /* deltaEf32 in Q11 - -5.3 in Q11 -> 10854 */ | |||
| return VOICE; | |||
| } | |||
| /* cond 11 : deltaEl < a11*deltaS + b11 */ | |||
| if (deltaEl32 < ADD32(MULT16_32_Q13(875,deltaS), -31744)) { /* deltaEl32 in Q11 - deltaS in Q28*875(1.4000e+04 in Q-4) >> 12 -> result in Q11 - -31744 (-15.5 in Q11) */ | |||
| return VOICE; | |||
| } | |||
| /* cond 12 : deltaEl > a12*deltaEf + b12 */ | |||
| if (deltaEl32 > ADD32(MULT16_32_Q15(30427, deltaEl32), 2341)) { /* deltaEl32 in Q11 - deltaEl32 in Q11*30427(0.928571 in Q15) >> 15 -> result in Q11 - 2341 (1.14285 in Q11) */ | |||
| return VOICE; | |||
| } | |||
| /* cond 13 : deltaEl < a13*deltaEf + b13 */ | |||
| if (deltaEl32 < ADD32(MULT16_32_Q14(-24576, deltaEl32), -18432)) { /* deltaEl32 in Q11 - deltaEl32 in Q11*-24576(-1.5 in Q14) >> 15 -> result in Q11 - -18432 (-9 in Q11) */ | |||
| return VOICE; | |||
| } | |||
| /* cond 13 : deltaEl < a14*deltaEf + b14 */ | |||
| if (deltaEl32 < ADD32(MULT16_32_Q15(23406, deltaEl32), -4389)) { /* deltaEl32 in Q11 - deltaEl32 in Q11*23406(0.714285 in Q15) >> 15 -> result in Q11 - -4389 (-2.1428571 in Q11) */ | |||
| return VOICE; | |||
| } | |||
| /* no condition was true */ | |||
| return NOISE; | |||
| } | |||
| /*******************************************************************************************/ | |||
| /* bcg729_vad : voice activity detection from AnnexB */ | |||
| /* parameters: */ | |||
| /* -(i/o):VADChannelContext : context containing all informations needed for VAD */ | |||
| /* -(i): reflectionCoefficient : in Q31 */ | |||
| /* -(i): LSFCoefficients :10 Coefficient in Q2.13 in range [0,Pi[ */ | |||
| /* -(i): autoCorrelationCoefficients : 13 coefficients of autoCorrelation */ | |||
| /* -(i): autoCorrelationCoefficientsScale : scaling factor of previous coeffs */ | |||
| /* -(i): signalCurrentFrame: the preprocessed signal in Q0, accessed in [-1, L_FRAME[ */ | |||
| /* */ | |||
| /* returns : 1 for active voice frame, 0 for silence frame */ | |||
| /* */ | |||
| /*******************************************************************************************/ | |||
| uint8_t bcg729_vad(bcg729VADChannelContextStruct *VADChannelContext, word32_t reflectionCoefficient, word16_t *LSFCoefficients, word32_t *autoCorrelationCoefficients, int8_t autoCorrelationCoefficientsScale, const word16_t *signalCurrentFrame) { | |||
| word16_t Ef, deltaEf; /* full band energy/10 in Q11 eq B1 */ | |||
| word16_t Emin; /* minimum Ef value of the last 128 frames */ | |||
| word16_t El, deltaEl; /* low band energy/10 in Q11 eqB2 */ | |||
| word16_t ZC, deltaZC; /* zero crossing rate in Q15 eq B3 */ | |||
| word32_t deltaS; /* spectral distorsion in Q12 */ | |||
| word32_t acc; | |||
| uint8_t Ivd = VOICE; /* Initial Voice activity decision : VOICE or NOISE, flag defined in B3.5 */ | |||
| int i; | |||
| /*** parameters extraction B3.1 ***/ | |||
| /* full band energy Ef (eq B1) = 10*log10(autoCorrelationCoefficient[0]/240), we compute Ef/10 in Q11 */ | |||
| acc = SUB32(g729Log2_Q0Q16(autoCorrelationCoefficients[0]), (((int32_t)autoCorrelationCoefficientsScale)<<16)); /* acc = log2(R(0)) in Q16*/ | |||
| acc = SHR32(SUB32(acc, LOG2_240_Q16),1); /* acc = log2(R(0)/240) in Q15 */ | |||
| acc = MULT16_32_Q15(INV_LOG2_10_Q15, acc); /* acc Ef in Q15 */ | |||
| Ef = (word16_t)(PSHR(acc,4)); | |||
| /* store Ef in the rolling EfBuffer at position countFrame%N0 */ | |||
| VADChannelContext->EfBuffer[(VADChannelContext->frameCount)%N0] = Ef; | |||
| /* low band energy El (eq B2)/10 in Q11 */ | |||
| acc=MULT16_32_Q15(lowBandFilter[0], autoCorrelationCoefficients[0]); | |||
| for (i=1; i<NB_LSP_COEFF+3; i++) { | |||
| acc = MAC16_32_Q14(acc, lowBandFilter[i], autoCorrelationCoefficients[i]); | |||
| } | |||
| acc = SUB32(g729Log2_Q0Q16(acc), (((int32_t)autoCorrelationCoefficientsScale)<<16)); /* acc = log2(htRh) in Q16*/ | |||
| acc = SHR32(SUB32(acc, LOG2_240_Q16),1); /* acc = log2(htRh/240) in Q15 */ | |||
| acc = MULT16_32_Q15(INV_LOG2_10_Q15, acc); /* acc El in Q15 */ | |||
| El = (word16_t)(PSHR(acc,4)); | |||
| /* compute zero crossing rate ZC (eq B3) in Q15 : at each zero crossing, add 410 (1/80 in Q15)*/ | |||
| ZC = 0; | |||
| for (i=0; i<L_FRAME; i++) { | |||
| if (MULT16_16(signalCurrentFrame[i-1], signalCurrentFrame[i])<0) { | |||
| ZC = ADD16(ZC,410); /* add 1/80 in Q15 */ | |||
| } | |||
| } | |||
| /* B3.2 Initialisation of the running averages of background noise characteristics : End of init period (32 first frames) */ | |||
| if (VADChannelContext->frameCount == Ni) { /* complete initialisations */ | |||
| word16_t meanEn = 0; | |||
| if (VADChannelContext->nbValidInitFrame>0) {/* check if we had a valid frame during init */ | |||
| meanEn = (word16_t)DIV32(VADChannelContext->initEfSum, VADChannelContext->nbValidInitFrame); | |||
| VADChannelContext->meanZC = (word16_t)DIV32(VADChannelContext->initZCSum, VADChannelContext->nbValidInitFrame); | |||
| for (i=0; i<NB_LSP_COEFF; i++) { | |||
| VADChannelContext->meanLSF[i] = (word16_t)DIV32(VADChannelContext->initLSFSum[i], VADChannelContext->nbValidInitFrame); | |||
| } | |||
| /* spec B3.2 describe a mechanism for initialisation of meanEl and meanEf which is not implemented in ITU code */ | |||
| /* ITU code does: meanEf = meanEn-1 and meanEl = meanEn - 1.2, just do that */ | |||
| /* it looks like the case in wich meanEn>T2 (5.5dB) is considered always true which is certainly not the case */ | |||
| VADChannelContext->meanEf = SUB16(meanEn, 2048); /* -1 in Q11 (K4 if K4 is in Q27 in the spec)*/ | |||
| VADChannelContext->meanEl = SUB16(meanEn, 2458); /* -1.2 in Q11 (K5 if K5 is in Q27 in the spec)*/ | |||
| } else {/* if we have no valid frames */ | |||
| VADChannelContext->frameCount = 0; /* as done in Appendix II : reset frameCount to restart init period */ | |||
| } | |||
| } | |||
| /* B3.2 Initialisation of the running averages of background noise characteristics : during init period(32 first frames) */ | |||
| if (VADChannelContext->frameCount < Ni) { /* initialisation is still on going for running averages */ | |||
| if (Ef<3072) { /* Ef stores 1/10 of full band energy in Q11, frames are valid only when Ef>15dB, 3072 is 1.5 in Q11 */ | |||
| Ivd = NOISE; | |||
| } else { | |||
| Ivd = VOICE; | |||
| VADChannelContext->nbValidInitFrame++; | |||
| VADChannelContext->initEfSum = ADD32(VADChannelContext->initEfSum, Ef); | |||
| VADChannelContext->initZCSum = ADD32(VADChannelContext->initZCSum, ZC); | |||
| for (i=0; i<NB_LSP_COEFF; i++) { | |||
| VADChannelContext->initLSFSum[i] = ADD32(VADChannelContext->initLSFSum[i], LSFCoefficients[i]); | |||
| } | |||
| } | |||
| /* context updates */ | |||
| VADChannelContext->frameCount++; | |||
| VADChannelContext->previousFrameEf = Ef; | |||
| VADChannelContext -> SVDm2 = VADChannelContext -> SVDm1; | |||
| VADChannelContext -> SVDm1 = Ivd; | |||
| return Ivd; | |||
| } | |||
| /* B3.3 Generating the long term minimum energy (real computation of the minimum over the last 128 values, no trick as suggested in the spec) */ | |||
| Emin = getMinInArray(VADChannelContext->EfBuffer, N0); | |||
| /* B3.4.1 spectral distorsion deltaS */ | |||
| deltaS = 0; | |||
| for (i=0; i<NB_LSP_COEFF; i++) { | |||
| word16_t acc16; | |||
| acc16 = SUB16(LSFCoefficients[i], VADChannelContext->meanLSF[i]); | |||
| deltaS = MAC16_16_Q13(deltaS, acc16, acc16); | |||
| } | |||
| /* B3.4.2 deltaEf */ | |||
| deltaEf = SUB16(VADChannelContext->meanEf, Ef); /* in Q11 */ | |||
| /* B3.4.3 deltaEf */ | |||
| deltaEl = SUB16(VADChannelContext->meanEl, El); /* in Q11 */ | |||
| /* B3.4.2 deltaEf */ | |||
| deltaZC = SUB16(VADChannelContext->meanZC, ZC); /* in Q15 */ | |||
| if (Ef<3072) { /* Ef stores 1/10 of full band energy in Q11, frames are valid only when Ef>15dB, 3072 is 1.5 in Q11 */ | |||
| Ivd = NOISE; | |||
| } else { | |||
| Ivd = multiBoundaryInitialVoiceActivityDecision(deltaS, deltaEf, deltaEl, deltaZC); | |||
| } | |||
| /* B3.6 : Voice Activity Decision Smoothing */ | |||
| /* Appendix II hysteresis II 5.2 as implemented in ITU code, description in appendix has a test on meanEf which is not implemented in example code */ | |||
| if (Ivd == VOICE) { | |||
| VADChannelContext->Count_inert=0; | |||
| } | |||
| if ((Ivd == NOISE) && (VADChannelContext->Count_inert < 6)) { | |||
| VADChannelContext->Count_inert++; | |||
| Ivd = VOICE; | |||
| } | |||
| /* first stage of VAD smoothing : code add a test on Ef>15 (21 in the floating version?!?) not documented */ | |||
| if ((Ivd == NOISE) && (VADChannelContext->SVDm1) && (deltaEf>410) && (Ef>3072)) { /* deltaEf is (meanEf - Ef)/10 in Q11, test is Ef > meanEf + 2, 410 is 0.2 in Q11 - 3072 is 1.5 in Q11 */ | |||
| Ivd = VOICE; | |||
| } | |||
| /* second stage of VAD smoothing */ | |||
| if ((VADChannelContext->secondStageVADSmoothingFlag == 1) | |||
| && (Ivd == NOISE) | |||
| && (VADChannelContext->SVDm1 == VOICE) | |||
| && (VADChannelContext->SVDm2 == VOICE) | |||
| && (ABS(SUB16(Ef, VADChannelContext->previousFrameEf)) <= 614)) { /* |Ef - meanEf|<=3 -> 614 is 0.3 in Q11 */ | |||
| Ivd = VOICE; | |||
| VADChannelContext->smoothingCounter++; | |||
| if (VADChannelContext->smoothingCounter<=4) { | |||
| VADChannelContext->secondStageVADSmoothingFlag = 1; | |||
| } else { | |||
| VADChannelContext->secondStageVADSmoothingFlag = 0; | |||
| VADChannelContext->smoothingCounter = 0; | |||
| } | |||
| } else { | |||
| VADChannelContext->secondStageVADSmoothingFlag = 1; | |||
| } | |||
| /* third stage of VAD smoothing */ | |||
| if (Ivd == NOISE) { | |||
| VADChannelContext->noiseContinuityCounter++; | |||
| } | |||
| if ( (Ivd == VOICE) | |||
| && (VADChannelContext->noiseContinuityCounter > 10) /* Cs > N2 */ | |||
| && (SUB16(Ef, VADChannelContext->previousFrameEf) <= 614)) { /* Ef - prevEf <= T5(3.0) : 614 is 0.3 in Q11 */ | |||
| Ivd = NOISE; | |||
| VADChannelContext->noiseContinuityCounter = 0; | |||
| VADChannelContext->Count_inert = 6; /* added by appendix II*/ | |||
| } | |||
| if (Ivd == VOICE) { | |||
| VADChannelContext->noiseContinuityCounter = 0; | |||
| } | |||
| /* forth stage of VAD smoothing is not implemented in appendix II */ | |||
| /* update the running averages B3.7 : note ITU implementation differs from spec, not only Ef < meanEf + 3dB is tested but also ReflectionCoefficient < 0.75 and deltaS < 0.002532959 */ | |||
| if ( (SUB16(Ef, 614) < VADChannelContext->meanEf) /* 614 is 0.3 in Q11 : Ef and meanEf are 1/10 of real Ef and meanEf, so 0.3 instead of 3, all in Q11 */ | |||
| && (reflectionCoefficient < 1610612736) ) { /* in Q31, 0.75 is 1610612736 */ | |||
| /* Note : G729 Appendix II suggest that the test on deltaS is not relevant and prevent good detection in very noisy environment : remove it */ | |||
| // && (deltaS < 819)) { /* deltaS is computed using non normalised LSF so compare with 4*pi^2*0.002532959 = 819 in Q13 as ITU code normalise LSF dividing them by 2*Pi */ | |||
| /* all beta Coefficient in Q15 */ | |||
| word16_t betaE, betaZC, betaLSF; /* parameter used for first order auto-regressive update scheme, note betaEf and betaEl are the same: betaE */ | |||
| word16_t betaEComplement, betaZCComplement, betaLSFComplement; /* these are the (1 - betaXX) */ | |||
| /* update number of update performed (Cn in spec B3.7) */ | |||
| VADChannelContext->updateCount++; | |||
| /* according to spec, based on Cn (updateCount) value we must use a different set of coefficient, none of them is defined in the spec, all values are picked from ITU code */ | |||
| if (VADChannelContext->updateCount<20) { | |||
| betaE = 24576; /* 0.75 */ | |||
| betaEComplement = 8192; /* 1-0.75 */ | |||
| betaZC = 26214; /* 0.8 */ | |||
| betaZCComplement = 6554; /* 1-0.8 */ | |||
| betaLSF = 19661; /* 0.6 */ | |||
| betaLSFComplement = 13107; /* 1-0.6 */ | |||
| } else if (VADChannelContext->updateCount<30) { | |||
| betaE = 31130; /* 0.95 */ | |||
| betaEComplement = 1638; /* 1-0.95 */ | |||
| betaZC = 30147; /* 0.92 */ | |||
| betaZCComplement = 2621; /* 1-0.92 */ | |||
| betaLSF = 21299; /* 0.65 */ | |||
| betaLSFComplement = 11469; /* 1-0.65 */ | |||
| } else if (VADChannelContext->updateCount<40) { | |||
| betaE = 31785; /* 0.97 */ | |||
| betaEComplement = 983; /* 1-0.97 */ | |||
| betaZC = 30802; /* 0.94 */ | |||
| betaZCComplement = 1966; /* 1-0.94 */ | |||
| betaLSF = 22938; /* 0.7 */ | |||
| betaLSFComplement = 9830 ; /* 1-0.7 */ | |||
| } else if (VADChannelContext->updateCount<50) { | |||
| betaE = 32440; /* 0.99 */ | |||
| betaEComplement = 328; /* 1-0.99 */ | |||
| betaZC = 31457; /* 0.96 */ | |||
| betaZCComplement = 1311; /* 1-0.96 */ | |||
| betaLSF = 24756; /* 0.75 */ | |||
| betaLSFComplement = 8192; /* 1-0.75 */ | |||
| } else if (VADChannelContext->updateCount<60) { | |||
| betaE = 32604; /* 0.995 */ | |||
| betaEComplement = 164; /* 1-0.995 */ | |||
| betaZC = 32440; /* 0.99 */ | |||
| betaZCComplement = 328; /* 1-0.99 */ | |||
| betaLSF = 24576; /* 0.75 */ | |||
| betaLSFComplement = 8192; /* 1-0.75 */ | |||
| } else { | |||
| betaE = 32702; /* 0.998 */ /* note : in ITU code betaE and betaZC seems to be swaped on this last case, this implementation swap them back */ | |||
| betaEComplement = 66; /* 1-0.998 */ | |||
| betaZC = 32604; /* 0.995 */ | |||
| betaZCComplement = 164; /* 1-0.995 */ | |||
| betaLSF = 24576; /* 0.75 */ | |||
| betaLSFComplement = 8192; /* 1-0.75 */ | |||
| } | |||
| VADChannelContext->meanEf = ADD16(MULT16_16_Q15(VADChannelContext->meanEf, betaE), MULT16_16_Q15(Ef, betaEComplement)); | |||
| VADChannelContext->meanEl = ADD16(MULT16_16_Q15(VADChannelContext->meanEl, betaE), MULT16_16_Q15(El, betaEComplement)); | |||
| VADChannelContext->meanZC = ADD16(MULT16_16_Q15(VADChannelContext->meanZC, betaZC), MULT16_16_Q15(ZC, betaZCComplement)); | |||
| for (i=0; i<NB_LSP_COEFF; i++) { | |||
| VADChannelContext->meanLSF[i] = ADD16(MULT16_16_Q15(VADChannelContext->meanLSF[i], betaLSF), MULT16_16_Q15(LSFCoefficients[i], betaLSFComplement)); | |||
| } | |||
| } | |||
| /* further update of meanEf and updatedFrame, implementation differs from spec again, stick to ITU code */ | |||
| if ( (VADChannelContext->frameCount>N0) | |||
| &&( ((VADChannelContext->meanEf<Emin) && (deltaS < 819)) /* deltaS < 0.002532959 -> 819 is 0.002532959*4*pi^2 in Q13 */ | |||
| ||(VADChannelContext->meanEf > ADD16(Emin, 2048)))) { /* meanEf > Emin + 10 : 2048 is 1 in Q11, Ef is 1/10 of real one */ | |||
| VADChannelContext->meanEf = Emin; | |||
| VADChannelContext->updateCount = 0; | |||
| } | |||
| /* context updates */ | |||
| VADChannelContext->frameCount++; | |||
| VADChannelContext->previousFrameEf = Ef; | |||
| VADChannelContext -> SVDm2 = VADChannelContext -> SVDm1; | |||
| VADChannelContext -> SVDm1 = Ivd; | |||
| return Ivd; | |||
| } | |||
| @ -0,0 +1,42 @@ | |||
| /* | |||
| vad.h | |||
| Copyright (C) 2011 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| #ifndef VAD_H | |||
| #define VAD_H | |||
| /*****************************************************************************/ | |||
| /* initBcg729VADChannel : create context structure and initialise it */ | |||
| /* return value : */ | |||
| /* - the VAD channel context data */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| bcg729VADChannelContextStruct *initBcg729VADChannel(); | |||
| /*******************************************************************************************/ | |||
| /* bcg729_vad : voice activity detection from AnnexB */ | |||
| /* parameters: */ | |||
| /* -(i/o):VADChannelContext : context containing all informations needed for VAD */ | |||
| /* */ | |||
| /* returns : 1 for active voice frame, 0 for silence frame */ | |||
| /* */ | |||
| /*******************************************************************************************/ | |||
| uint8_t bcg729_vad(bcg729VADChannelContextStruct *VADChannelContext, word32_t reflectionCoefficient, word16_t *LSFCoefficients, word32_t *autoCorrelationCoefficients, int8_t autoCorrelationCoefficientsScale, const word16_t *signalCurrentFrame); | |||
| #endif /* ifndef VAD_H */ | |||
| @ -0,0 +1,143 @@ | |||
| /* | |||
| decoderTest.c | |||
| Copyright (C) 2011 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| /*****************************************************************************/ | |||
| /* */ | |||
| /* Test Program for decoder */ | |||
| /* Input: 15 parameters and the frame erasure flag on each row of a */ | |||
| /* a text CSV file. */ | |||
| /* Ouput: the reconstructed signal : each frame (80 16 bits PCM values) */ | |||
| /* on a row of a text CSV file */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <math.h> | |||
| #include <time.h> | |||
| #include "typedef.h" | |||
| #include "codecParameters.h" | |||
| #include "utils.h" | |||
| #include "testUtils.h" | |||
| #include "bcg729/decoder.h" | |||
| int main(int argc, char *argv[] ) | |||
| { | |||
| /*** get calling argument ***/ | |||
| char *filePrefix; | |||
| getArgument(argc, argv, &filePrefix); /* check argument and set filePrefix if needed */ | |||
| /*** input and output file pointers ***/ | |||
| FILE *fpInput; | |||
| FILE *fpOutput; | |||
| FILE *fpBinOutput; | |||
| /*** input and output buffers ***/ | |||
| uint16_t inputBuffer[NB_PARAMETERS+2]; /* input buffer: an array containing the 15 parameters and the frame erasure flag and VAD flag */ | |||
| uint8_t bitStream[10]; /* binary input for the decoder */ | |||
| int16_t outputBuffer[L_FRAME]; /* output buffer: the reconstructed signal */ | |||
| /*** inits ***/ | |||
| /* open the input file */ | |||
| if ( (fpInput = fopen(argv[1], "r")) == NULL) { | |||
| printf("%s - Error: can't open file %s\n", argv[0], argv[1]); | |||
| exit(-1); | |||
| } | |||
| /* create the output file(filename is the same than input file with the .out extension) */ | |||
| char *outputFile = malloc((strlen(filePrefix)+5)*sizeof(char)); | |||
| sprintf(outputFile, "%s.out",filePrefix); | |||
| if ( (fpOutput = fopen(outputFile, "w")) == NULL) { | |||
| printf("%s - Error: can't create file %s\n", argv[0], outputFile); | |||
| exit(-1); | |||
| } | |||
| sprintf(outputFile, "%s.pcm",filePrefix); | |||
| if ( (fpBinOutput = fopen(outputFile, "wb")) == NULL) { | |||
| printf("%s - Error: can't create file %s\n", argv[0], outputFile); | |||
| exit(-1); | |||
| } | |||
| /*** init of the tested bloc ***/ | |||
| bcg729DecoderChannelContextStruct *decoderChannelContext = initBcg729DecoderChannel(); | |||
| /*** initialisation complete ***/ | |||
| /* perf measurement */ | |||
| clock_t start, end; | |||
| double cpu_time_used=0.0; | |||
| int framesNbr =0; | |||
| /* increase LOOP_N to increase input length and perform a more accurate profiling or perf measurement */ | |||
| #define LOOP_N 1 | |||
| int j; | |||
| for (j=0; j<LOOP_N; j++) { | |||
| /* perf measurement */ | |||
| /*** loop over input file ***/ | |||
| while(fscanf(fpInput, "%hd,%hd,%hd,%hd,%hd,%hd,%hd,%hd,%hd,%hd,%hd,%hd,%hd,%hd,%hd,%hd,%hd", &(inputBuffer[0]), &(inputBuffer[1]), &(inputBuffer[2]), &(inputBuffer[3]), &(inputBuffer[4]), &(inputBuffer[5]), &(inputBuffer[6]), &(inputBuffer[7]), &(inputBuffer[8]), &(inputBuffer[9]), &(inputBuffer[10]), &(inputBuffer[11]), &(inputBuffer[12]), &(inputBuffer[13]), &(inputBuffer[14]), &(inputBuffer[15]), &(inputBuffer[16]))==17) /* index 4 and 5 are inverted to get P0 in 4 and P1 in 5 in the array */ | |||
| { /* input buffer contains the parameters and in [15] the frame erasure flag */ | |||
| int i; | |||
| framesNbr++; | |||
| if (inputBuffer[16]==1) { /* active frame */ | |||
| parametersArray2BitStream(inputBuffer, bitStream); | |||
| } else { /* SID frame */ | |||
| if (inputBuffer[16]==0) {/* non transmitted frame */ | |||
| inputBuffer[15] = 1; | |||
| } else { /* actual SID frame */ | |||
| CNGparametersArray2BitStream(inputBuffer, bitStream); | |||
| } | |||
| } | |||
| start = clock(); | |||
| bcg729Decoder(decoderChannelContext, inputBuffer[15]==0?bitStream:NULL, inputBuffer[15], inputBuffer[16]!=1?1:0, outputBuffer); | |||
| end = clock(); | |||
| cpu_time_used += ((double) (end - start)); | |||
| /* write the output to the output files (only on first loop of perf measurement)*/ | |||
| if (j==0) { | |||
| fprintf(fpOutput,"%d",outputBuffer[0]); | |||
| for (i=1; i<L_FRAME; i++) { | |||
| fprintf(fpOutput,",%d",outputBuffer[i]); | |||
| } | |||
| fprintf(fpOutput,"\n"); | |||
| /* write the ouput to raw data file */ | |||
| fwrite(outputBuffer, sizeof(int16_t), L_FRAME, fpBinOutput); | |||
| } | |||
| } | |||
| /* perf measurement */ | |||
| rewind(fpInput); | |||
| } | |||
| closeBcg729DecoderChannel(decoderChannelContext); | |||
| /* Perf measurement: uncomment next line to print cpu usage */ | |||
| printf("Decode %d frames in %f seconds : %f us/frame\n", framesNbr, cpu_time_used/CLOCKS_PER_SEC, cpu_time_used*1000000/((double)framesNbr*CLOCKS_PER_SEC)); | |||
| /* perf measurement */ | |||
| exit (0); | |||
| } | |||
| @ -0,0 +1,105 @@ | |||
| /* | |||
| computeNoiseExcitationTest.c | |||
| Copyright (C) 2011 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| /*****************************************************************************/ | |||
| /* */ | |||
| /* Test Program for computeLP Bloc */ | |||
| /* Input: a CSV text with 236 values of 16 bits : */ | |||
| /* - target gain in Q3 */ | |||
| /* - pseudo random generator seed */ | |||
| /* - 234 values of excitation vector buffer(154 past, 80 current) */ | |||
| /* Ouput: - updated pseudo random generator seed */ | |||
| /* - 234 16 bits values of updated excitation vector */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "typedef.h" | |||
| #include "codecParameters.h" | |||
| #include "testUtils.h" | |||
| #include "cng.h" | |||
| int main(int argc, char *argv[] ) | |||
| { | |||
| /*** get calling argument ***/ | |||
| char *filePrefix; | |||
| getArgument(argc, argv, &filePrefix); /* check argument and set filePrefix if needed */ | |||
| /*** input and output file pointers ***/ | |||
| FILE *fpInput; | |||
| FILE *fpOutput; | |||
| /*** input and output buffers ***/ | |||
| word16_t inputBuffer[L_PAST_EXCITATION+L_FRAME]; /* used for input and output, in Q0 */ | |||
| word16_t targetGain; /* input in Q3 */ | |||
| uint16_t randomGeneratorSeed; | |||
| /*** inits ***/ | |||
| /* open the input file */ | |||
| if ( (fpInput = fopen(argv[1], "r")) == NULL) { | |||
| printf("%s - Error: can't open file %s\n", argv[0], argv[1]); | |||
| exit(-1); | |||
| } | |||
| /* create the output file(filename is the same than input file with the .out extension) */ | |||
| char *outputFile = malloc((strlen(filePrefix)+5)*sizeof(char)); | |||
| sprintf(outputFile, "%s.out",filePrefix); | |||
| if ( (fpOutput = fopen(outputFile, "w")) == NULL) { | |||
| printf("%s - Error: can't create file %s\n", argv[0], outputFile); | |||
| exit(-1); | |||
| } | |||
| /*** init of the tested bloc ***/ | |||
| /*** initialisation complete ***/ | |||
| /*** loop over input file ***/ | |||
| while(1) /* infinite loop, escape condition is in the reading of data */ | |||
| { | |||
| int i; | |||
| /* read the input data until we have some */ | |||
| if (fscanf(fpInput,"%hd",&targetGain) != 1) break; | |||
| if (fscanf(fpInput,",%hd",&randomGeneratorSeed) != 1) break; | |||
| for (i=0; i<L_PAST_EXCITATION+L_FRAME; i++) { | |||
| if (fscanf(fpInput,",%hd",&(inputBuffer[i])) != 1) break; | |||
| } | |||
| /* call the tested function: output will replace the input in the buffer */ | |||
| computeComfortNoiseExcitationVector(targetGain, &randomGeneratorSeed, &(inputBuffer[L_PAST_EXCITATION])); | |||
| /* write the output to the output file */ | |||
| //fprintf(fpOutput,"%d", (int16_t)randomGeneratorSeed); | |||
| fprintf(fpOutput,"%d", (int16_t)inputBuffer[L_PAST_EXCITATION]); | |||
| for (i=L_PAST_EXCITATION+1; i<L_PAST_EXCITATION+L_FRAME; i++) { | |||
| fprintf(fpOutput,",%d", inputBuffer[i]); | |||
| } | |||
| fprintf(fpOutput,"\n"); | |||
| } | |||
| exit (0); | |||
| } | |||
| @ -0,0 +1,161 @@ | |||
| /* | |||
| encoderTest.c | |||
| Copyright (C) 2011 Belledonne Communications, Grenoble, France | |||
| Author : Johan Pascal | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| */ | |||
| /*****************************************************************************/ | |||
| /* */ | |||
| /* Test Program for encoder */ | |||
| /* Input: the reconstructed signal : each frame (80 16 bits PCM values) */ | |||
| /* on a row of a text CSV file */ | |||
| /* Output: 15 parameters on each row of a text CSV file. */ | |||
| /* */ | |||
| /*****************************************************************************/ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <time.h> | |||
| #include "typedef.h" | |||
| #include "codecParameters.h" | |||
| #include "utils.h" | |||
| #include "testUtils.h" | |||
| #include "bcg729/encoder.h" | |||
| FILE *fpOutput; | |||
| int main(int argc, char *argv[] ) | |||
| { | |||
| /*** get calling argument ***/ | |||
| char *filePrefix; | |||
| getArgument(argc, argv, &filePrefix); /* check argument and set filePrefix if needed */ | |||
| /*** input and output file pointers ***/ | |||
| FILE *fpInput; | |||
| /*** input and output buffers ***/ | |||
| int16_t inputBuffer[L_FRAME]; /* output buffer: the signal */ | |||
| uint8_t bitStream[10]; /* binary output of the encoder */ | |||
| uint16_t outputBuffer[NB_PARAMETERS]; /* output buffer: an array containing the 15 parameters */ | |||
| /*** inits ***/ | |||
| /* open the input file */ | |||
| uint16_t inputIsBinary = 0; | |||
| if (argv[1][strlen(argv[1])-1] == 'n') { /* input filename and by n, it's probably a .in : CSV file */ | |||
| if ( (fpInput = fopen(argv[1], "r")) == NULL) { | |||
| printf("%s - Error: can't open file %s\n", argv[0], argv[1]); | |||
| exit(-1); | |||
| } | |||
| } else { /* it's probably a binary file */ | |||
| inputIsBinary = 1; | |||
| if ( (fpInput = fopen(argv[1], "rb")) == NULL) { | |||
| printf("%s - Error: can't open file %s\n", argv[0], argv[1]); | |||
| exit(-1); | |||
| } | |||
| } | |||
| /* create the output file(filename is the same than input file with the .out extension) */ | |||
| char *outputFile = malloc((strlen(filePrefix)+5)*sizeof(char)); | |||
| sprintf(outputFile, "%s.out",filePrefix); | |||
| if ( (fpOutput = fopen(outputFile, "w")) == NULL) { | |||
| printf("%s - Error: can't create file %s\n", argv[0], outputFile); | |||
| exit(-1); | |||
| } | |||
| /*** init of the tested bloc ***/ | |||
| bcg729EncoderChannelContextStruct *encoderChannelContext = initBcg729EncoderChannel(1); | |||
| /*** initialisation complete ***/ | |||
| /* perf measurement */ | |||
| clock_t start, end; | |||
| double cpu_time_used=0.0f; | |||
| int framesNbr =0; | |||
| /* increase LOOP_N to increase input length and perform a more accurate profiling or perf measurement */ | |||
| #define LOOP_N 1 | |||
| int j; | |||
| for (j=0; j<LOOP_N; j++) { | |||
| /* perf measurement */ | |||
| /*** loop over input file ***/ | |||
| while(1) /* infinite loop, escape condition is in the reading of data */ | |||
| { | |||
| int i; | |||
| uint8_t bitStreamLength; | |||
| /* read the input data until we have some */ | |||
| if (inputIsBinary) { | |||
| if (fread(inputBuffer, sizeof(int16_t), L_FRAME, fpInput) != L_FRAME) break; | |||
| } else { | |||
| if (fscanf(fpInput,"%hd",&(inputBuffer[0])) != 1) break; | |||
| for (i=1; i<L_FRAME; i++) { | |||
| if (fscanf(fpInput,",%hd",&(inputBuffer[i])) != 1) break; | |||
| } | |||
| } | |||
| framesNbr++; | |||
| start = clock(); | |||
| bcg729Encoder(encoderChannelContext, inputBuffer, bitStream, &bitStreamLength); | |||
| end = clock(); | |||
| cpu_time_used += ((double) (end - start)); | |||
| /* convert bitStream output in an array for easier debug */ | |||
| if (bitStreamLength == 10) { | |||
| parametersBitStream2Array(bitStream, outputBuffer); | |||
| if (j==0) { | |||
| /* write the output to the output file */ | |||
| fprintf(fpOutput,"%d",outputBuffer[0]); | |||
| for (i=1; i<NB_PARAMETERS; i++) { | |||
| fprintf(fpOutput,",%d",outputBuffer[i]); | |||
| } | |||
| fprintf(fpOutput,",0,1\n"); | |||
| } | |||
| } else if (bitStreamLength == 2) { | |||
| /* write the output to the output file */ | |||
| fprintf(fpOutput,"%d,%d,%d,%d",(bitStream[0]>>7)&0x01, (bitStream[0]>>2)&0x1F, ((bitStream[0]&0x03)<<2) | ((bitStream[1]>>6)&0x03), ((bitStream[1])>>1)&0x1F); | |||
| for (i=4; i<NB_PARAMETERS; i++) { | |||
| fprintf(fpOutput,",0"); /* just get the correct number of parameters */ | |||
| } | |||
| fprintf(fpOutput,",0,2\n"); | |||
| } else { /* bitstream to 0, un transmitted frame */ | |||
| for (i=0; i<NB_PARAMETERS; i++) { | |||
| fprintf(fpOutput,"0,"); /* just get the correct number of parameters */ | |||
| } | |||
| fprintf(fpOutput,"1,0\n"); | |||
| } | |||
| } | |||
| /* perf measurement */ | |||
| rewind(fpInput); | |||
| } | |||
| closeBcg729EncoderChannel(encoderChannelContext); | |||
| /* Perf measurement: uncomment next line to print cpu usage */ | |||
| printf("Encode %d frames in %f seconds : %f us/frame\n", framesNbr, cpu_time_used/CLOCKS_PER_SEC, cpu_time_used*1000000/((double)framesNbr*CLOCKS_PER_SEC)); | |||
| /* perf measurement */ | |||
| exit (0); | |||
| } | |||