From 843c130cf755b5acd649ae44fda66dc249411c19 Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Wed, 11 Feb 2015 00:22:45 +0100 Subject: [PATCH] Implement G729 AnnexB : VAD/DTX/CNG - VAD algo is actually the variant described in G729 Appendix II + move headers to src files --- include/Makefile.am | 30 -- include/bcg729/decoder.h | 3 +- include/bcg729/encoder.h | 11 +- include/computeLP.h | 32 -- msbcg729/bcg729_dec.c | 9 +- msbcg729/bcg729_enc.c | 33 +- src/CMakeLists.txt | 3 + {include => src}/LP2LSPConversion.h | 6 +- {include => src}/LPSynthesisFilter.h | 0 src/LSPQuantization.c | 207 ++++++++- {include => src}/LSPQuantization.h | 12 + src/Makefile.am | 5 +- {include => src}/adaptativeCodebookSearch.h | 0 {include => src}/basicOperationsMacros.h | 0 src/cng.c | 294 ++++++++++++ src/cng.h | 54 +++ src/codebooks.c | 66 ++- {include => src}/codebooks.h | 11 + {include => src}/codecParameters.h | 25 + .../computeAdaptativeCodebookGain.h | 0 src/computeLP.c | 200 ++++---- src/computeLP.h | 49 ++ {include => src}/computeWeightedSpeech.h | 0 src/decodeAdaptativeCodeVector.c | 84 ++-- {include => src}/decodeAdaptativeCodeVector.h | 13 + {include => src}/decodeFixedCodeVector.h | 0 {include => src}/decodeGains.h | 0 src/decodeLSP.c | 111 +++-- {include => src}/decodeLSP.h | 14 + src/decoder.c | 104 ++++- src/dtx.c | 431 ++++++++++++++++++ src/dtx.h | 61 +++ src/encoder.c | 112 ++++- {include => src}/findOpenLoopPitchDelay.h | 0 {include => src}/fixedCodebookSearch.h | 0 {include => src}/fixedPointMacros.h | 18 +- {include => src}/floatingPointMacros.h | 0 {include => src}/g729FixedPointMath.h | 0 {include => src}/gainQuantization.h | 0 {include => src}/interpolateqLSP.h | 0 {include => src}/postFilter.h | 0 {include => src}/postProcessing.h | 0 {include => src}/preProcessing.h | 0 {include => src}/qLSP2LP.h | 0 {include => src}/typedef.h | 60 +++ src/utils.c | 56 +++ {include => src}/utils.h | 35 ++ src/vad.c | 422 +++++++++++++++++ src/vad.h | 42 ++ test/bin/Makefile.am | 7 +- test/src/CNGdecoderTest.c | 143 ++++++ test/src/computeLPTest.c | 9 +- test/src/computeNoiseExcitationTest.c | 105 +++++ test/src/decoderMultiChannelTest.c | 2 +- test/src/decoderTest.c | 4 +- test/src/encoderMultiChannelTest.c | 26 +- test/src/encoderTest.c | 40 +- test/src/encoderVADTest.c | 161 +++++++ test/testCampaign | 4 +- 59 files changed, 2809 insertions(+), 305 deletions(-) delete mode 100644 include/computeLP.h rename {include => src}/LP2LSPConversion.h (99%) rename {include => src}/LPSynthesisFilter.h (100%) rename {include => src}/LSPQuantization.h (65%) rename {include => src}/adaptativeCodebookSearch.h (100%) rename {include => src}/basicOperationsMacros.h (100%) create mode 100644 src/cng.c create mode 100644 src/cng.h rename {include => src}/codebooks.h (78%) rename {include => src}/codecParameters.h (86%) rename {include => src}/computeAdaptativeCodebookGain.h (100%) create mode 100644 src/computeLP.h rename {include => src}/computeWeightedSpeech.h (100%) rename {include => src}/decodeAdaptativeCodeVector.h (72%) rename {include => src}/decodeFixedCodeVector.h (100%) rename {include => src}/decodeGains.h (100%) rename {include => src}/decodeLSP.h (65%) create mode 100644 src/dtx.c create mode 100644 src/dtx.h rename {include => src}/findOpenLoopPitchDelay.h (100%) rename {include => src}/fixedCodebookSearch.h (100%) rename {include => src}/fixedPointMacros.h (87%) rename {include => src}/floatingPointMacros.h (100%) rename {include => src}/g729FixedPointMath.h (100%) rename {include => src}/gainQuantization.h (100%) rename {include => src}/interpolateqLSP.h (100%) rename {include => src}/postFilter.h (100%) rename {include => src}/postProcessing.h (100%) rename {include => src}/preProcessing.h (100%) rename {include => src}/qLSP2LP.h (100%) rename {include => src}/typedef.h (71%) rename {include => src}/utils.h (82%) create mode 100644 src/vad.c create mode 100644 src/vad.h create mode 100644 test/src/CNGdecoderTest.c create mode 100644 test/src/computeNoiseExcitationTest.c create mode 100644 test/src/encoderVADTest.c diff --git a/include/Makefile.am b/include/Makefile.am index 8ace3bd..f404f61 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -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) diff --git a/include/bcg729/decoder.h b/include/bcg729/decoder.h index afe2780..504b652 100644 --- a/include/bcg729/decoder.h +++ b/include/bcg729/decoder.h @@ -51,8 +51,9 @@ BCG729_VISIBILITY void closeBcg729DecoderChannel(bcg729DecoderChannelContextStru /* -(i) decoderChannelContext : the channel context data */ /* -(i) bitStream : 15 parameters on 80 bits */ /* -(i) frameErased: flag: true, frame has been erased */ +/* -(i) SIDFrameFlag: flag: true, frame is a SID one */ /* -(o) signal : a decoded frame 80 samples (16 bits PCM) */ /* */ /*****************************************************************************/ -BCG729_VISIBILITY void bcg729Decoder(bcg729DecoderChannelContextStruct *decoderChannelContext, uint8_t bitStream[], uint8_t frameErasureFlag, int16_t signal[]); +BCG729_VISIBILITY void bcg729Decoder(bcg729DecoderChannelContextStruct *decoderChannelContext, uint8_t bitStream[], uint8_t frameErasureFlag, uint8_t SIDFrameFlag, int16_t signal[]); #endif /* ifndef DECODER_H */ diff --git a/include/bcg729/encoder.h b/include/bcg729/encoder.h index 6735a5d..001f887 100644 --- a/include/bcg729/encoder.h +++ b/include/bcg729/encoder.h @@ -31,11 +31,13 @@ typedef struct bcg729EncoderChannelContextStruct_struct bcg729EncoderChannelCont /*****************************************************************************/ /* initBcg729EncoderChannel : create context structure and initialise it */ +/* parameters: */ +/* -(i) enanbleVAD : flag set to 1: VAD/DTX is enabled */ /* return value : */ /* - the encoder channel context data */ /* */ /*****************************************************************************/ -BCG729_VISIBILITY bcg729EncoderChannelContextStruct *initBcg729EncoderChannel(); +BCG729_VISIBILITY bcg729EncoderChannelContextStruct *initBcg729EncoderChannel(uint8_t enableVAD); /*****************************************************************************/ /* closeBcg729EncoderChannel : free memory of context structure */ @@ -51,8 +53,11 @@ BCG729_VISIBILITY void closeBcg729EncoderChannel(bcg729EncoderChannelContextStru /* -(i) encoderChannelContext : context for this encoder channel */ /* -(i) inputFrame : 80 samples (16 bits PCM) */ /* -(o) bitStream : The 15 parameters for a frame on 80 bits */ -/* on 80 bits (5 16bits words) */ +/* on 80 bits (5 16bits words) for voice frame, 4 on 2 byte for */ +/* noise frame, 0 for untransmitted frames */ +/* -(o) bitStreamLength : actual length of output, may be 0, 2 or 10 */ +/* if VAD/DTX is enabled */ /* */ /*****************************************************************************/ -BCG729_VISIBILITY void bcg729Encoder(bcg729EncoderChannelContextStruct *encoderChannelContext, int16_t inputFrame[], uint8_t bitStream[]); +BCG729_VISIBILITY void bcg729Encoder(bcg729EncoderChannelContextStruct *encoderChannelContext, int16_t inputFrame[], uint8_t bitStream[], uint8_t *bitStreamLength); #endif /* ifndef ENCODER_H */ diff --git a/include/computeLP.h b/include/computeLP.h deleted file mode 100644 index c4a0ab1..0000000 --- a/include/computeLP.h +++ /dev/null @@ -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 */ diff --git a/msbcg729/bcg729_dec.c b/msbcg729/bcg729_dec.c index e441860..a8d3431 100644 --- a/msbcg729/bcg729_dec.c +++ b/msbcg729/bcg729_dec.c @@ -26,6 +26,7 @@ /* signal and bitstream frame size in byte */ #define SIGNAL_FRAME_SIZE 160 #define BITSTREAM_FRAME_SIZE 10 +#define NOISE_BITSTREAM_FRAME_SIZE 2 /* decoder struct: context for decoder channel and concealment */ struct bcg729Decoder_struct { @@ -49,11 +50,13 @@ static void filter_process(MSFilter *f){ while((inputMessage=ms_queue_get(f->inputs[0]))) { while(inputMessage->b_rptrb_wptr) { + /* if remaining data in RTP payload have the size of a SID frame it must be one, see RFC3551 section 4.5.6 : any SID frame must be the last one of the RPT payload */ + uint8_t SIDFrameFlag = ((inputMessage->b_wptr-inputMessage->b_rptr)==NOISE_BITSTREAM_FRAME_SIZE)?1:0; outputMessage = allocb(SIGNAL_FRAME_SIZE,0); mblk_meta_copy(inputMessage, outputMessage); - bcg729Decoder(obj->decoderChannelContext, inputMessage->b_rptr, 0, (int16_t *)(outputMessage->b_wptr)); + bcg729Decoder(obj->decoderChannelContext, inputMessage->b_rptr, 0, SIDFrameFlag, (int16_t *)(outputMessage->b_wptr)); outputMessage->b_wptr+=SIGNAL_FRAME_SIZE; - inputMessage->b_rptr+=BITSTREAM_FRAME_SIZE; + inputMessage->b_rptr += (SIDFrameFlag==1)?NOISE_BITSTREAM_FRAME_SIZE:BITSTREAM_FRAME_SIZE; ms_queue_put(f->outputs[0],outputMessage); ms_concealer_inc_sample_time(obj->concealer,f->ticker->time,10, 1); } @@ -62,7 +65,7 @@ static void filter_process(MSFilter *f){ if (ms_concealer_context_is_concealement_required(obj->concealer, f->ticker->time)) { outputMessage = allocb(SIGNAL_FRAME_SIZE,0); - bcg729Decoder(obj->decoderChannelContext, NULL, 1, (int16_t *)(outputMessage->b_wptr)); + bcg729Decoder(obj->decoderChannelContext, NULL, 1, 0, (int16_t *)(outputMessage->b_wptr)); outputMessage->b_wptr+=SIGNAL_FRAME_SIZE; mblk_set_plc_flag(outputMessage, 1); ms_queue_put(f->outputs[0],outputMessage); diff --git a/msbcg729/bcg729_enc.c b/msbcg729/bcg729_enc.c index e298e8f..2b7203f 100644 --- a/msbcg729/bcg729_enc.c +++ b/msbcg729/bcg729_enc.c @@ -24,11 +24,12 @@ /*filter common method*/ struct bcg729Encoder_struct { - bcg729EncoderChannelContextStruct *encoderChannelContext; + bcg729EncoderChannelContextStruct *encoderChannelContext; MSBufferizer *bufferizer; unsigned char ptime; unsigned char max_ptime; uint32_t ts; + uint8_t enableVAD; }; static void filter_init(MSFilter *f){ @@ -37,12 +38,13 @@ static void filter_init(MSFilter *f){ obj = (struct bcg729Encoder_struct*) f->data; obj->ptime=20; obj->max_ptime=100; + obj->enableVAD=0; } static void filter_preprocess(MSFilter *f){ struct bcg729Encoder_struct* obj= (struct bcg729Encoder_struct*) f->data; - obj->encoderChannelContext = initBcg729EncoderChannel(); /* initialize bcg729 encoder, return context */ + obj->encoderChannelContext = initBcg729EncoderChannel(obj->enableVAD); /* initialize bcg729 encoder, return context */ obj->bufferizer=ms_bufferizer_new(); } @@ -59,17 +61,23 @@ static void filter_process(MSFilter *f){ /* process ptimes ms of data : (ptime in ms)/1000->ptime is seconds * 8000(sample rate) * 2(byte per sample) */ while(ms_bufferizer_get_avail(obj->bufferizer)>=obj->ptime*16){ + uint16_t totalPacketDataLength = 0; + uint8_t bitStreamLength = 0; outputMessage = allocb(obj->ptime,0); /* output bitStream is 80 bits long * number of samples */ /* process buffer in 10 ms frames */ - for (bufferIndex=0; bufferIndexptime; bufferIndex+=10) { + /* RFC3551 section 4.5.6 we must end the RTP payload of G729 frames when transmitting a SID frame : bitStreamLength == 2 */ + for (bufferIndex=0; (bufferIndexptime) && (bitStreamLength!=2); bufferIndex+=10) { ms_bufferizer_read(obj->bufferizer,inputBuffer,160); - bcg729Encoder(obj->encoderChannelContext, (int16_t *)inputBuffer, outputMessage->b_wptr); - outputMessage->b_wptr+=10; - + bcg729Encoder(obj->encoderChannelContext, (int16_t *)inputBuffer, outputMessage->b_wptr, &bitStreamLength); + outputMessage->b_wptr+=bitStreamLength; + totalPacketDataLength+=bitStreamLength; + } + obj->ts+=bufferIndex*8; + /* do not enqueue the message if no data out (DTX untransmitted frames) */ + if (totalPacketDataLength>0) { + mblk_set_timestamp_info(outputMessage,obj->ts); + ms_queue_put(f->outputs[0],outputMessage); } - obj->ts+=obj->ptime*8; - mblk_set_timestamp_info(outputMessage,obj->ts); - ms_queue_put(f->outputs[0],outputMessage); } } @@ -110,7 +118,14 @@ static int filter_add_fmtp(MSFilter *f, void *arg){ } ms_message("MSBCG729Enc: got ptime=%i",obj->ptime); + } else if (fmtp_get_value(fmtp,"annexb",buf,sizeof(buf))){ + if (strncmp(buf, "yes",3)) { + obj->enableVAD = 1; + ms_message("MSBCG729Enc: enable VAD/DTX - AnnexB"); + } + } + /* parse annexB param here */ return 0; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 160ad8b..ddc23a0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,6 +44,9 @@ set(BCG729_SOURCE_FILES preProcessing.c qLSP2LP.c utils.c + cng.c + dtx.c + vad.c ) add_library(bcg729 SHARED ${BCG729_SOURCE_FILES}) diff --git a/include/LP2LSPConversion.h b/src/LP2LSPConversion.h similarity index 99% rename from include/LP2LSPConversion.h rename to src/LP2LSPConversion.h index 761b61b..8115c3b 100644 --- a/include/LP2LSPConversion.h +++ b/src/LP2LSPConversion.h @@ -3,17 +3,17 @@ 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. diff --git a/include/LPSynthesisFilter.h b/src/LPSynthesisFilter.h similarity index 100% rename from include/LPSynthesisFilter.h rename to src/LPSynthesisFilter.h diff --git a/src/LSPQuantization.c b/src/LSPQuantization.c index dfc6a92..70e7f50 100644 --- a/src/LSPQuantization.c +++ b/src/LSPQuantization.c @@ -39,6 +39,211 @@ void initLSPQuantization(bcg729EncoderChannelContextStruct *encoderChannelContex } return; } +/**********************************************************************************/ +/* noiseLSPQuantization : Convert LSP to LSF, Quantize LSF and find L parameters, */ +/* qLSF->qLSP as described in spec A3.2.4 */ +/* parameters: */ +/* -(i/o) previousqLSF : 4 previousqLSF, is updated by this function */ +/* -(i) LSPCoefficients : 10 LSP coefficients in Q15 */ +/* -(o) qLSPCoefficients : 10 qLSP coefficients in Q15 */ +/* -(o) parameters : 3 parameters L0, L1, L2 */ +/* */ +/**********************************************************************************/ +void noiseLSPQuantization(word16_t previousqLSF[MA_MAX_K][NB_LSP_COEFF], word16_t LSPCoefficients[], word16_t qLSPCoefficients[], uint8_t parameters[]) +{ + int i,j; + int L0; + word16_t LSF[NB_LSP_COEFF]; /* LSF coefficients in Q2.13 range [0, Pi[ */ + word16_t weights[NB_LSP_COEFF]; /* weights in Q11 */ + word16_t weightsThreshold[NB_LSP_COEFF]; /* store in Q13 the threshold used to compute the weights */ + word16_t L1index[L0_RANGE]; + word16_t L2index[L0_RANGE]; + word32_t weightedMeanSquareError[L0_RANGE]; + word16_t quantizerOutput[NB_LSP_COEFF]; + word16_t qLSF[NB_LSP_COEFF]; + + /*** compute LSF in Q2.13 : lsf = arcos(lsp) range [0, Pi[ spec 3.2.4 eq18 ***/ + for (i=0; i0) { + weights[i] = ONE_IN_Q11; + } else { + weights[i] = (word16_t)SATURATE(ADD32(PSHR(MULT16_16(MULT16_16_Q13(weightsThreshold[i], weightsThreshold[i]), 10), 2), ONE_IN_Q11), MAXINT16); + } + } + weights[4] = MULT16_16_Q14(weights[4], ONE_POINT_2_IN_Q14); + weights[5] = MULT16_16_Q14(weights[5], ONE_POINT_2_IN_Q14); + + /*** compute the coefficients for the two noise noise MA Predictors ***/ + for (L0=0; L0 acc in Q2.28 */ + } + targetVector[i] = MULT16_16_Q12((word16_t)PSHR(acc, 15), invNoiseMAPredictorSum[L0][i]); /* acc->Q13 and invMAPredictorSum in Q12 -> targetVector in Q13 */ + } + + /* find closest match for predictionError (minimize mean square diff) in L1 subset codebook: 32 entries from L1 codebook */ + for (i=0; i l[i] - l^[i] = (wi - w^[i])/(1-SumMAPred[i]) but ITU code ignores this denominator */ + /* works on the first five coefficients only */ + meanSquareDiff = MAXINT32; + for (i=0; i result in Q13 */ + acc = MAC16_16(acc, difftargetVectorL1L2, MULT16_16_Q11(difftargetVectorL1L2, weights[j])); /* weights in Q11, diff in Q13 */ + } + + for (j=NB_LSP_COEFF/2; j result in Q13 */ + acc = MAC16_16(acc, difftargetVectorL1L3, MULT16_16_Q11(difftargetVectorL1L3, weights[j])); /* weights in Q11, diff in Q13 */ + } + + + if (accSUB16(quantizedVector[i],GAP1)) { + quantizedVector[i-1] = PSHR(SUB16(ADD16(quantizedVector[i], quantizedVector[i-1]), GAP1), 1); + quantizedVector[i] = PSHR(ADD16(ADD16(quantizedVector[i], quantizedVector[i-1]), GAP1), 1); + } + } + for (i=NB_LSP_COEFF/2+1; iSUB16(quantizedVector[i],GAP1)) { + quantizedVector[i-1] = PSHR(SUB16(ADD16(quantizedVector[i], quantizedVector[i-1]), GAP1), 1); + quantizedVector[i] = PSHR(ADD16(ADD16(quantizedVector[i], quantizedVector[i-1]), GAP1), 1); + } + } + + /* rearrange the whole quantizedVector with a distance of 0.0006 */ + for (i=1; iSUB16(quantizedVector[i],GAP2)) { + quantizedVector[i-1] = PSHR(SUB16(ADD16(quantizedVector[i], quantizedVector[i-1]), GAP2), 1); + quantizedVector[i] = PSHR(ADD16(ADD16(quantizedVector[i], quantizedVector[i-1]), GAP2), 1); + } + } + + /* compute the weighted mean square distance using the final quantized vector according to eq21 */ + weightedMeanSquareError[L0]=0; + for (i=0; i result in Q13 */ + weightedMeanSquareError[L0] = MAC16_16(weightedMeanSquareError[L0], difftargetVectorQuantizedVector, MULT16_16_Q11(difftargetVectorQuantizedVector, weights[i])); /* weights in Q11, diff in Q13 */ + } + + + } + + /* now select L0 and copy the selected coefficients to the output buffer */ + if (weightedMeanSquareError[0] Q28 */ + for (j=0; j0; i--) { + memcpy(previousqLSF[i], previousqLSF[i-1], NB_LSP_COEFF*sizeof(word16_t)); + } + memcpy(previousqLSF[0], quantizerOutput, NB_LSP_COEFF*sizeof(word16_t)); + + /*** qLSF stability check ***/ + insertionSort(qLSF, NB_LSP_COEFF); + + /* check for low limit on qLSF[0] */ + if (qLSF[1]qLSF_MAX) { + qLSF[NB_LSP_COEFF-1] = qLSF_MAX; + } + + /* convert qLSF to qLSP: qLSP = cos(qLSF) */ + for (i=0; iqLSP as described in spec A3.2.4 */ +/* parameters: */ +/* -(i/o) previousqLSF : 4 previousqLSF, is updated by this function */ +/* -(i) LSPCoefficients : 10 LSP coefficients in Q15 */ +/* -(o) qLSPCoefficients : 10 qLSP coefficients in Q15 */ +/* -(o) parameters : 3 parameters L0, L1, L2 */ +/* */ +/**********************************************************************************/ +void noiseLSPQuantization(word16_t previousqLSF[MA_MAX_K][NB_LSP_COEFF], word16_t LSPCoefficients[], word16_t qLSPCoefficients[], uint8_t parameters[]); #endif /* LSPQUANTIZATION_H */ diff --git a/src/Makefile.am b/src/Makefile.am index 713db4c..f4459d6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,10 @@ libbcg729_la_SOURCES= LP2LSPConversion.c \ postProcessing.c \ preProcessing.c \ qLSP2LP.c \ - utils.c + utils.c \ + cng.c \ + vad.c \ + dtx.c libbcg729_la_LDFLAGS= -no-undefined diff --git a/include/adaptativeCodebookSearch.h b/src/adaptativeCodebookSearch.h similarity index 100% rename from include/adaptativeCodebookSearch.h rename to src/adaptativeCodebookSearch.h diff --git a/include/basicOperationsMacros.h b/src/basicOperationsMacros.h similarity index 100% rename from include/basicOperationsMacros.h rename to src/basicOperationsMacros.h diff --git a/src/cng.c b/src/cng.c new file mode 100644 index 0000000..4d9c5dc --- /dev/null +++ b/src/cng.c @@ -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 +#include + +#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>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; i0) - 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 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=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)>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; iqLSP[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; iqLSP[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); +} diff --git a/src/cng.h b/src/cng.h new file mode 100644 index 0000000..e360a2b --- /dev/null +++ b/src/cng.h @@ -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 */ diff --git a/src/codebooks.c b/src/codebooks.c index 1398080..9176b68 100644 --- a/src/codebooks.c +++ b/src/codebooks.c @@ -191,6 +191,12 @@ word16_t L2L3[L2_RANGE][NB_LSP_COEFF] = { /* The second stage is a 10-bit VQ spl { -163, 674, -11, -886, 531, -1125, -265, -242, 724, 934} }; +/* index used by CNG to reach a subset of L1, L2 and L3 codebooks */ +uint8_t L1SubsetIndex[32] = {96,52,20,54,86,114,82,68,36,121,48,92,18,120, + 94,124,50,125,4,100,28,76,12,117,81,22,90,116, + 127,21,108,66}; +uint8_t L2SubsetIndex[16] = {31,21,9,3,10,2,19,26,4,3,11,29,15,27,21,12}; +uint8_t L3SubsetIndex[16] = {16,1,0,0,8,25,22,20,19,23,20,31,4,31,20,31}; word16_t MAPredictor[L0_RANGE][MA_MAX_K][NB_LSP_COEFF] = { /* the MA predictor coefficients in Q0.15 but max value < 0.5 so it fits on 15 bits */ { @@ -217,6 +223,35 @@ word16_t invMAPredictorSum[L0_RANGE][NB_LSP_COEFF] = {/* 1/(1 - Sum(MAPredictor) { 9202, 7320, 6788, 7738, 8170, 8154, 8856, 8818, 8366, 8544} }; +/* DTX/CNG use different values for MA predictor */ +word16_t noiseMAPredictor[L0_RANGE][MA_MAX_K][NB_LSP_COEFF] = { /* the MA predictor coefficients in Q0.15 but max value < 0.5 so it fits on 15 bits */ + { + { 8421, 9109, 9175, 8965, 9034, 9057, 8765, 8775, 9106, 8673}, + { 7018, 7189, 7638, 7307, 7444, 7379, 7038, 6956, 6930, 6868}, + { 5472, 4990, 5134, 5177, 5246, 5141, 5206, 5095, 4830, 5147}, + { 4056, 3031, 2614, 3024, 2916, 2713, 3309, 3237, 2857, 3473} + }, + { + { 8145, 8617, 8779, 8648, 8718, 8829, 8713, 8705, 8806, 8231 }, + { 5894, 5525, 5603, 5773, 6016, 5968, 5896, 5835, 5721, 5707 }, + { 4568, 3765, 3605, 3963, 4144, 4038, 4225, 4139, 3914, 4255 }, + { 3643, 2455, 1944, 2466, 2438, 2259, 2798, 2775, 2479, 3124 } + } +}; + +word16_t noiseMAPredictorSum[L0_RANGE][NB_LSP_COEFF] = {/* 1 - Sum(MAPredictor) in Q0.15 */ + { 7798, 8447, 8205, 8293, 8126, 8477, 8447, 8703, 9043, 8604}, + {10514, 12402, 12833, 11914, 11447, 11670, 11132, 11311, 11844, 11447} +}; + +word16_t invNoiseMAPredictorSum[L0_RANGE][NB_LSP_COEFF] = {/* 1/(1 - Sum(MAPredictor)) in Q3.12 */ + {17210, 15888, 16357, 16183, 16516, 15833, 15888, 15421, 14840, 15597}, + {12764, 10821, 10458, 11264, 11724, 11500, 12056, 11865, 11331, 11724} +}; + + + + /* codebook for adaptative code vector */ word16_t b30[31] = { /* in Q0.15 */ 29443, 25207, 14701, 3143, -4402, -5850, -2783, 1211, 3130, 2259, 0, -1652, -1666, -464, 756, 1099, 550, -245, -634, -451, 0, 308, 296, 78, -120, -165, -79, 34, 91, 70, 0}; @@ -291,6 +326,31 @@ word16_t wlp[L_LP_ANALYSIS_WINDOW] = { /* in Q15 */ 23055, 22117, 21145, 20139, 19102, 18036, 16941, 15820, 14674, 13505, 12315, 11106, 9879, 8637, 7381, 6114, 4838, 3554, 2264, 971}; -/* lag window as defined in spec 3.2.1 eq6 */ -word16_t wlag[NB_LSP_COEFF+1] = { /* in Q15 note first coeff is not used */ -32767, 32728, 32619, 32438, 32187, 31867, 31480, 31029, 30517, 29946, 29321}; +/* lag window as defined in spec 3.2.1 eq6 : up to 12 values for VAD */ +/* wlag[0] = 1.00000000 not used + wlag[1] = 0.99879038 + wlag[2] = 0.99546897 + wlag[3] = 0.98995781 + wlag[4] = 0.98229337 + wlag[5] = 0.97252619 + wlag[6] = 0.96072036 + wlag[7] = 0.94695264 + wlag[8] = 0.93131179 + wlag[9] = 0.91389757 + wlag[10]= 0.89481968 + wlag[11]= 0.87419660 + wlag[12]= 0.85215437 +*/ +word16_t wlag[NB_LSP_COEFF+3] = { /* in Q15 note first coeff is not used */ +32767, 32728, 32619, 32438, 32187, 31867, 31480, 31029, 30517, 29946, 29321, 28646, 27923}; + +/* quantised SID gain retrieved from ITU code, in Q3 */ +word16_t SIDGainCodebook[32] = { + 2, 5, 8, 13, 20, 32, 50, 64, + 80, 101, 127, 160, 201, 253, 318, 401, + 505, 635, 800, 1007, 1268, 1596, 2010, 2530, + 3185, 4009, 5048, 6355, 8000,10071,12679,15962 }; + +/* Low Band Filter FIR for VAD in Q15 */ +word16_t lowBandFilter[NB_LSP_COEFF+3] = {7869, 7011, 4838, 2299, 321, -660, -782, -484, -164, 3, 39, 21, 4}; + diff --git a/include/codebooks.h b/src/codebooks.h similarity index 78% rename from include/codebooks.h rename to src/codebooks.h index a2d555f..97c665e 100644 --- a/include/codebooks.h +++ b/src/codebooks.h @@ -31,6 +31,10 @@ extern word16_t L2L3[L2_RANGE][NB_LSP_COEFF]; /* Doc : The second stage is a 10- extern word16_t MAPredictor[L0_RANGE][MA_MAX_K][NB_LSP_COEFF]; /* the MA predictor coefficients in Q0.15 but max value < 0.5 so it fits on 15 bits */ extern word16_t MAPredictorSum[L0_RANGE][NB_LSP_COEFF]; /* 1 - Sum(MAPredictor) in Q0.15 */ extern word16_t invMAPredictorSum[L0_RANGE][NB_LSP_COEFF]; /* 1/(1 - Sum(MAPredictor)) in Q3.12 */ +/* same as above but used for SID frame */ +extern word16_t noiseMAPredictor[L0_RANGE][MA_MAX_K][NB_LSP_COEFF]; /* the MA predictor coefficients in Q0.15 but max value < 0.5 so it fits on 15 bits */ +extern word16_t noiseMAPredictorSum[L0_RANGE][NB_LSP_COEFF]; /* 1 - Sum(MAPredictor) in Q0.15 */ +extern word16_t invNoiseMAPredictorSum[L0_RANGE][NB_LSP_COEFF]; /* 1/(1 - Sum(MAPredictor)) in Q3.12 */ /* codebook for adaptative code vector */ extern word16_t b30[31]; @@ -43,6 +47,13 @@ extern uint16_t indexMappingGB[16]; extern word16_t GACodebook[8][2]; extern word16_t GBCodebook[16][2]; extern word16_t MAPredictionCoefficients[4]; +extern word16_t SIDGainCodebook[32]; +extern uint8_t L1SubsetIndex[32]; +extern uint8_t L2SubsetIndex[16]; +extern uint8_t L3SubsetIndex[16]; + +/* codebook for VAD */ +extern word16_t lowBandFilter[NB_LSP_COEFF+3]; /* codebook for LP Analysis */ extern word16_t wlp[L_LP_ANALYSIS_WINDOW]; diff --git a/include/codecParameters.h b/src/codecParameters.h similarity index 86% rename from include/codecParameters.h rename to src/codecParameters.h index f20b729..a35a932 100644 --- a/include/codecParameters.h +++ b/src/codecParameters.h @@ -37,6 +37,9 @@ #define L1_RANGE (1< 1) */ + /* sum in Q27 (sum can't be > 1 but intermediate accumulation can) */ + /* a in Q4.27 with full range possible */ + /* Note: during iteration, current a[i] is in Q31 (can't be >1) and is */ + /* set to Q27 at the end of current iteration */ + /* */ + /*********************************************************************************/ + word32_t previousIterationLPCoefficients[NB_LSP_COEFF+1]; /* to compute a[]*/ + word32_t LPCoefficients[NB_LSP_COEFF+1]; /* in Q4.27 */ + + word32_t E = 0; /* in Q31 */ + word32_t sum = 0; /* in Q27 */ + int i,j; + + /* init */ + LPCoefficients[0] = ONE_IN_Q27; + LPCoefficients[1] = -DIV32_32_Q27(autoCorrelationCoefficients[1], autoCorrelationCoefficients[0]); /* result in Q27(but<1) */ + /* E = r0(1 - a[1]^2) in Q31 */ + E = MULT32_32_Q31(autoCorrelationCoefficients[0], SUB32(ONE_IN_Q31, MULT32_32_Q23(LPCoefficients[1], LPCoefficients[1]))); /* LPCoefficient[1] is in Q27, using a Q23 operation will result in a Q31 variable */ + + for (i=2; i result in Q27 -> sum in Q27 */ + } + sum = ADD32(SHL(sum, 4), autoCorrelationCoefficients[i]); /* set sum in Q31 and add r[0] */ + + /* a[i] = -sum/E */ + LPCoefficients[i] = -DIV32_32_Q31(sum,E); /* LPCoefficient of current iteration is in Q31 for now, it will be set to Q27 at the end of this iteration */ + if (i==2) { /* parameter k[1] from eq8 in 3.2.2 needed by VAD */ + *reflectionCoefficient = LPCoefficients[i]; + } + + /* iterations j = 1..i-1 */ + /* a[j] += a[i]*a[i-j] */ + for (j=1; jMAXINT32); - autoCorrelationCoefficient[0] = acc64; + autoCorrelationCoefficients[0] = acc64; } else { rightShiftToNormalise = -countLeadingZeros((word32_t)acc64); - autoCorrelationCoefficient[0] = SHL((word32_t)acc64, -rightShiftToNormalise); + autoCorrelationCoefficients[0] = SHL((word32_t)acc64, -rightShiftToNormalise); } - /* compute autoCorrelationCoefficient 1 to 10 */ + /* give current autoCorrelation coefficients scale to the output */ + *autoCorrelationCoefficientsScale = -rightShiftToNormalise; + + /* compute autoCorrelationCoefficients 1 to requested number (10 - no VAD - or 12 if VAD is enabled */ if (rightShiftToNormalise>0) { /* acc64 was not fitting on 32 bits so compute the other sum on 64 bits too */ - for (i=1; i 1) */ - /* sum in Q27 (sum can't be > 1 but intermediate accumulation can) */ - /* a in Q4.27 with full range possible */ - /* Note: during iteration, current a[i] is in Q31 (can't be >1) and is */ - /* set to Q27 at the end of current iteration */ - /* */ - /*********************************************************************************/ - /* init */ - LPCoefficients[0] = ONE_IN_Q27; - LPCoefficients[1] = -DIV32_32_Q27(autoCorrelationCoefficient[1], autoCorrelationCoefficient[0]); /* result in Q27(but<1) */ - /* E = r0(1 - a[1]^2) in Q31 */ - E = MULT32_32_Q31(autoCorrelationCoefficient[0], SUB32(ONE_IN_Q31, MULT32_32_Q23(LPCoefficients[1], LPCoefficients[1]))); /* LPCoefficient[1] is in Q27, using a Q23 operation will result in a Q31 variable */ - - for (i=2; i result in Q27 -> sum in Q27 */ - } - sum = ADD32(SHL(sum, 4), autoCorrelationCoefficient[i]); /* set sum in Q31 and add r[0] */ - - /* a[i] = -sum/E */ - LPCoefficients[i] = -DIV32_32_Q31(sum,E); /* LPCoefficient of current iteration is in Q31 for now, it will be set to Q27 at the end of this iteration */ - - /* iterations j = 1..i-1 */ - /* a[j] += a[i]*a[i-j] */ - for (j=1; j increase by one the integer part and set to 2 the fractional part : -(k+1/3) -> -(k+1)+2/3 */ + fracPitchDelay = 2; + } else { + fracPitchDelay = -fracPitchDelay; /* 0 unchanged, -1 -> +1 */ + excitationVectorMinusK = &(excitationVector[-intPitchDelay]); /* -(k-1/3) -> -k+1/3 or -(k) -> -k*/ + } + + for (n=0; n increase by one the integer part and set to 2 the fractional part : -(k+1/3) -> -(k+1)+2/3 */ - fracPitchDelay = 2; - } else { - fracPitchDelay = -fracPitchDelay; /* 0 unchanged, -1 -> +1 */ - excitationVectorMinusK = &(excitationVector[-(*intPitchDelay)]); /* -(k-1/3) -> -k+1/3 or -(k) -> -k*/ - } - - for (n=0; n acc will end up being in Q2.28*/ + /* Note : previousLCodeWord array containing the last 4 code words is updated during this phase */ + + for (i=0; i=0; j--) { + acc = MAC16_16(acc, currentMAPredictor[L0][j][i], previousLCodeWord[j][i]); + previousLCodeWord[j][i] = (j>0)?previousLCodeWord[j-1][i]:codebookqLSF[i]; /* update the previousqLCodeWord array: row[0] = current code word and row[j]=row[j-1] */ + } + /* acc in Q2.28, shift back the acc to a Q2.13 with rounding */ + codebookqLSF[i] = (word16_t)PSHR(acc, 15); /* codebookqLSF in Q2.13 */ + } + /* Note : codebookqLSF buffer now contains qLSF */ + + /*** doc 3.2.4 qLSF stability ***/ + /* qLSF in Q2.13 as are qLSF_MIN and qLSF_MAX and MIN_qLSF_DISTANCE */ + + /* sort the codebookqLSF array */ + insertionSort(codebookqLSF, NB_LSP_COEFF); + + /* check for low limit on qLSF[0] */ + if (codebookqLSF[1]qLSF_MAX) { + codebookqLSF[NB_LSP_COEFF-1] = qLSF_MAX; + } +} + /*****************************************************************************/ /* decodeLSP : decode LSP coefficients as in spec 4.1.1/3.2.4 */ /* parameters: */ @@ -65,7 +129,6 @@ void decodeLSP(bcg729DecoderChannelContextStruct *decoderChannelContext, uint16_ if (frameErased == 0) { /* frame is ok, proceed according to 3.2.4 section of the doc */ - word32_t acc; /* Accumulator in Q2.28 */ /*** doc 3.2.4 eq(19) ***/ /* get the L codewords from the codebooks L1, L2 and L3 */ @@ -81,51 +144,7 @@ void decodeLSP(bcg729DecoderChannelContextStruct *decoderChannelContext, uint16_ currentqLSF[i] = ADD16(L1[L[1]][i], L2L3[L[3]][i]); /* same as previous, output in Q2.13 */ } - /*** rearrange in order to have a minimum distance between two consecutives coefficients ***/ - rearrangeCoefficients(currentqLSF, GAP1); - rearrangeCoefficients(currentqLSF, GAP2); /* currentqLSF still in Q2.13 */ - - /*** doc 3.2.4 eq(20) ***/ - /* compute the qLSF as a weighted sum(weighted by MA coefficient selected according to L0 value) of previous and current frame L codewords coefficients */ - - /* L[0] is the Switched MA predictor of LSP quantizer(1 bit) */ - /* currentqLSF and previousLCodeWord in Q2.13 */ - /* MAPredictor and MAPredictorSum in Q0.15 with MAPredictorSum[MA switch][i]+Sum[j=0-3](MAPredictor[MA switch][j][i])=1 -> acc will end up being in Q2.28*/ - /* Note : previousLCodeWord array containing the last 4 code words is updated during this phase */ - - for (i=0; i=0; j--) { - acc = MAC16_16(acc, MAPredictor[L[0]][j][i], decoderChannelContext->previousLCodeWord[j][i]); - decoderChannelContext->previousLCodeWord[j][i] = (j>0)?decoderChannelContext->previousLCodeWord[j-1][i]:currentqLSF[i]; /* update the previousqLCodeWord array: row[0] = current code word and row[j]=row[j-1] */ - } - /* acc in Q2.28, shift back the acc to a Q2.13 with rounding */ - currentqLSF[i] = (word16_t)PSHR(acc, 15); /* currentqLSF in Q2.13 */ - } - /* Note : currentqLSF buffer now contains qLSF */ - - /*** doc 3.2.4 qLSF stability ***/ - /* qLSF in Q2.13 as are qLSF_MIN and qLSF_MAX and MIN_qLSF_DISTANCE */ - - /* sort the currentqLSF array */ - insertionSort(currentqLSF, NB_LSP_COEFF); - - /* check for low limit on qLSF[0] */ - if (currentqLSF[1]qLSF_MAX) { - currentqLSF[NB_LSP_COEFF-1] = qLSF_MAX; - } + computeqLSF(currentqLSF, decoderChannelContext->previousLCodeWord, L[0], MAPredictor, MAPredictorSum); /* use regular MAPredictor as this function is not called on SID frame decoding */ /* backup the qLSF and L0 to restore them in case of frame erased */ for (i=0; ipreviousqLSP, previousqLSPInitialValues, NB_LSP_COEFF*sizeof(word16_t)); /* initialise the previousqLSP buffer */ memset(decoderChannelContext->excitationVector, 0, L_PAST_EXCITATION*sizeof(word16_t)); /* initialise the part of the excitationVector containing the past excitation */ decoderChannelContext->boundedAdaptativeCodebookGain = BOUNDED_PITCH_GAIN_MIN; decoderChannelContext->pseudoRandomSeed = 21845; /* initialise pseudo Random seed according to spec 4.4.4 */ + decoderChannelContext->CNGpseudoRandomSeed = CNG_DTX_RANDOM_SEED_INIT; /* initialise CNG pseudo Random seed according to ITU code */ decoderChannelContext->adaptativeCodebookGain = 0; /* gains are initialised at 0 */ decoderChannelContext->fixedCodebookGain = 0; memset(decoderChannelContext->reconstructedSpeech, 0, NB_LSP_COEFF*sizeof(word16_t)); /* initialise to zero all the values used from previous frame to get the current frame reconstructed speech */ + decoderChannelContext->previousFrameIsActiveFlag = 1; + decoderChannelContext->CNGChannelContext = initBcg729CNGChannel(); + /* initialisation of the differents blocs which need to be initialised */ initDecodeLSP(decoderChannelContext); @@ -81,7 +86,12 @@ bcg729DecoderChannelContextStruct *initBcg729DecoderChannel() /*****************************************************************************/ void closeBcg729DecoderChannel(bcg729DecoderChannelContextStruct *decoderChannelContext) { - free(decoderChannelContext); + if (decoderChannelContext) { + if (decoderChannelContext->CNGChannelContext) { + free(decoderChannelContext->CNGChannelContext); + } + free(decoderChannelContext); + } return; } @@ -91,14 +101,14 @@ void closeBcg729DecoderChannel(bcg729DecoderChannelContextStruct *decoderChannel /* -(i) decoderChannelContext : the channel context data */ /* -(i) bitStream : 15 parameters on 80 bits */ /* -(i) frameErased: flag: true, frame has been erased */ +/* -(i) SIDFrameFlag: flag: true, frame is a SID one */ /* -(o) signal : a decoded frame 80 samples (16 bits PCM) */ /* */ /*****************************************************************************/ -void bcg729Decoder(bcg729DecoderChannelContextStruct *decoderChannelContext, uint8_t bitStream[], uint8_t frameErasureFlag, int16_t signal[]) +void bcg729Decoder(bcg729DecoderChannelContextStruct *decoderChannelContext, uint8_t bitStream[], uint8_t frameErasureFlag, uint8_t SIDFrameFlag, int16_t signal[]) { int i; uint16_t parameters[NB_PARAMETERS]; - /* internal buffers which we do not need to keep between calls */ word16_t qLSP[NB_LSP_COEFF]; /* store the qLSP coefficients in Q0.15 */ word16_t interpolatedqLSP[NB_LSP_COEFF]; /* store the interpolated qLSP coefficient in Q0.15 */ @@ -129,14 +139,74 @@ void bcg729Decoder(bcg729DecoderChannelContextStruct *decoderChannelContext, uin /* 12 -> S2 (4 bits) */ /* 13 -> GA2(3 bits) */ /* 14 -> GB2(4 bits) */ + /* in case of SID frame : params are decoded in the decodeSIDframe functions */ + /* 0-> L0 (1 bit) */ + /* 1-> L1 (5 bits) */ + /* 2-> L2 (4 bits) */ + /* 3-> Gain (5 bits) */ if (bitStream!=NULL) { /* bitStream might be null in case of frameErased (which shall be set in the appropriated flag)*/ - parametersBitStream2Array(bitStream, parameters); + if (SIDFrameFlag == 0) { + parametersBitStream2Array(bitStream, parameters); + } } else { /* avoid compiler complaining for non inizialazed use of variable */ for (i=0; ipreviousFrameIsActiveFlag) { + SIDFrameFlag = 0; + } else { + SIDFrameFlag = 1; + } + } + + /* this is a SID frame, process it using the dedicated function */ + if (SIDFrameFlag == 1) { + decodeSIDframe(decoderChannelContext->CNGChannelContext, decoderChannelContext->previousFrameIsActiveFlag, bitStream, &(decoderChannelContext->excitationVector[L_PAST_EXCITATION]), decoderChannelContext->previousqLSP, LP, &(decoderChannelContext->CNGpseudoRandomSeed), decoderChannelContext->previousLCodeWord); + decoderChannelContext->previousFrameIsActiveFlag = 0; + + /* loop over the two subframes */ + for (subframeIndex=0; subframeIndex +NB_LSP_COEFF on the index of this one because the first NB_LSP_COEFF elements store the previous frame filter output */ + LPSynthesisFilter(&(decoderChannelContext->excitationVector[L_PAST_EXCITATION + subframeIndex]), &(LP[LPCoefficientsIndex]), &(decoderChannelContext->reconstructedSpeech[NB_LSP_COEFF+subframeIndex]) ); + + /* NOTE: ITU code check for overflow after LP Synthesis Filter computation and if it happened, divide excitation buffer by 2 and recompute the LP Synthesis Filter */ + /* here, possible overflows are managed directly inside the Filter by saturation at MAXINT16 on each result */ + + /* postFilter */ + postFilter(decoderChannelContext, &(LP[LPCoefficientsIndex]), /* select the LP coefficients for this subframe, use last frame intPitchDelay */ + &(decoderChannelContext->reconstructedSpeech[NB_LSP_COEFF+subframeIndex]), decoderChannelContext->previousIntPitchDelay, subframeIndex, postFilteredSignal); + + /* postProcessing */ + postProcessing(decoderChannelContext, postFilteredSignal); + + /* copy postProcessing Output to the signal output buffer */ + for (i=0; iboundedAdaptativeCodebookGain = BOUNDED_PITCH_GAIN_MIN; + + /* Shift Excitation Vector by L_FRAME left */ + memmove(decoderChannelContext->excitationVector, &(decoderChannelContext->excitationVector[L_FRAME]), L_PAST_EXCITATION*sizeof(word16_t)); + /* Copy the last 10 words of reconstructed Speech to the begining of the array for next frame computation */ + memcpy(decoderChannelContext->reconstructedSpeech, &(decoderChannelContext->reconstructedSpeech[L_FRAME]), NB_LSP_COEFF*sizeof(word16_t)); + + return; + } + + decoderChannelContext->previousFrameIsActiveFlag = 1; + /* re-init the CNG pseudo random seed at each active frame spec B.4 */ + decoderChannelContext->CNGpseudoRandomSeed = CNG_DTX_RANDOM_SEED_INIT; /* re-initialise CNG pseudo Random seed to 11111 according to ITU code */ + /*****************************************************************************************/ /*** on frame basis : decodeLSP, interpolate them with previous ones and convert to LP ***/ @@ -176,8 +246,8 @@ void bcg729Decoder(bcg729DecoderChannelContextStruct *decoderChannelContext, uin /* in case of frame erasure we shall generate pseudoRandom signs and index for fixed code vector decoding according to spec 4.4.4 */ if (frameErasureFlag) { - parameters[parametersIndex] = pseudoRandom(decoderChannelContext)&(uint16_t)0x1fff; /* signs are set to the 13 LSB of the first pseudoRandom number */ - parameters[parametersIndex+1] = pseudoRandom(decoderChannelContext)&(uint16_t)0x000f; /* signs are set to the 4 LSB of the second pseudoRandom number */ + parameters[parametersIndex] = pseudoRandom(&(decoderChannelContext->pseudoRandomSeed))&(uint16_t)0x1fff; /* signs are set to the 13 LSB of the first pseudoRandom number */ + parameters[parametersIndex+1] = pseudoRandom(&(decoderChannelContext->pseudoRandomSeed))&(uint16_t)0x000f; /* signs are set to the 4 LSB of the second pseudoRandom number */ } /* decode the fixed Code Vector */ @@ -241,19 +311,3 @@ void bcg729Decoder(bcg729DecoderChannelContextStruct *decoderChannelContext, uin return; } - - -/*****************************************************************************/ -/* pseudoRandom : generate pseudo random number as in spec 4.4.4 eq96 */ -/* parameters: */ -/* -(i) decoderChannelContext : the channel context data */ -/* return value : */ -/* - a unsigned 16 bits pseudo random number */ -/* */ -/*****************************************************************************/ -uint16_t pseudoRandom(bcg729DecoderChannelContextStruct *decoderChannelContext) -{ - /* pseudoRandomSeed is stored in an uint16_t var, we shall not worry about overflow here */ - /* pseudoRandomSeed*31821 + 13849; */ - return decoderChannelContext->pseudoRandomSeed = MAC16_16(13849, (decoderChannelContext->pseudoRandomSeed), 31821); -} diff --git a/src/dtx.c b/src/dtx.c new file mode 100644 index 0000000..cf598e0 --- /dev/null +++ b/src/dtx.c @@ -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 +#include + +#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; imax) 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 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 Q24 >> Q4: result in Q20 */ + } + + /* and the rest */ + for (j=1; j 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= 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 (summedAutocorrelationCoefficientsScalepreviousResidualEnergyScale) { + 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; iqLSPCoefficients[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; + +} diff --git a/src/dtx.h b/src/dtx.h new file mode 100644 index 0000000..2b5ee91 --- /dev/null +++ b/src/dtx.h @@ -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 */ diff --git a/src/encoder.c b/src/encoder.c index b19c34e..f9db65c 100644 --- a/src/encoder.c +++ b/src/encoder.c @@ -41,6 +41,9 @@ #include "computeAdaptativeCodebookGain.h" #include "fixedCodebookSearch.h" #include "gainQuantization.h" +#include "g729FixedPointMath.h" +#include "vad.h" +#include "dtx.h" /* buffers allocation */ static const word16_t previousLSPInitialValues[NB_LSP_COEFF] = {30000, 26000, 21000, 15000, 8000, 0, -8000,-15000,-21000,-26000}; /* in Q0.15 the initials values for the previous LSP buffer */ @@ -51,10 +54,11 @@ static const word16_t previousLSPInitialValues[NB_LSP_COEFF] = {30000, 26000, 21 /* - the encoder channel context data */ /* */ /*****************************************************************************/ -bcg729EncoderChannelContextStruct *initBcg729EncoderChannel() +bcg729EncoderChannelContextStruct *initBcg729EncoderChannel(uint8_t enableVAD) { /* create the context structure */ bcg729EncoderChannelContextStruct *encoderChannelContext = malloc(sizeof(bcg729EncoderChannelContextStruct)); + memset(encoderChannelContext, 0, sizeof(bcg729EncoderChannelContextStruct)); /* initialise statics buffers and variables */ memset(encoderChannelContext->signalBuffer, 0, (L_LP_ANALYSIS_WINDOW-L_FRAME)*sizeof(word16_t)); /* set to zero all the past signal */ @@ -66,6 +70,13 @@ bcg729EncoderChannelContextStruct *initBcg729EncoderChannel() memset(encoderChannelContext->excitationVector, 0, L_PAST_EXCITATION*sizeof(word16_t)); /* set to zero values of previous excitation vector */ memset(encoderChannelContext->targetSignal, 0, NB_LSP_COEFF*sizeof(word16_t)); /* set to zero values filter memory for the targetSignal computation */ encoderChannelContext->lastQuantizedAdaptativeCodebookGain = O2_IN_Q14; /* quantized gain is initialized at his minimum value: 0.2 */ + if (enableVAD == 1) { + encoderChannelContext->VADChannelContext = initBcg729VADChannel(); + encoderChannelContext->DTXChannelContext = initBcg729DTXChannel(); + } else { + encoderChannelContext->VADChannelContext = NULL; + encoderChannelContext->DTXChannelContext = NULL; + } /* initialisation of the differents blocs which need to be initialised */ initPreProcessing(encoderChannelContext); @@ -83,7 +94,15 @@ bcg729EncoderChannelContextStruct *initBcg729EncoderChannel() /*****************************************************************************/ void closeBcg729EncoderChannel(bcg729EncoderChannelContextStruct *encoderChannelContext) { - free(encoderChannelContext); + if (encoderChannelContext) { + if (encoderChannelContext->VADChannelContext) { + free(encoderChannelContext->VADChannelContext); + } + if (encoderChannelContext->DTXChannelContext) { + free(encoderChannelContext->DTXChannelContext); + } + free(encoderChannelContext); + } } /*****************************************************************************/ @@ -95,13 +114,14 @@ void closeBcg729EncoderChannel(bcg729EncoderChannelContextStruct *encoderChannel /* on 80 bits (10 8bits words) */ /* */ /*****************************************************************************/ -void bcg729Encoder(bcg729EncoderChannelContextStruct *encoderChannelContext, int16_t inputFrame[], uint8_t bitStream[]) +void bcg729Encoder(bcg729EncoderChannelContextStruct *encoderChannelContext, int16_t inputFrame[], uint8_t bitStream[], uint8_t *bitStreamLength) { int i; uint16_t parameters[NB_PARAMETERS]; /* the output parameters in an array */ /* internal buffers which we do not need to keep between calls */ word16_t LPCoefficients[NB_LSP_COEFF]; /* the LP coefficients in Q3.12 */ + word16_t LSFCoefficients[NB_LSP_COEFF]; /* the LSF coefficients in Q3.12 */ word16_t qLPCoefficients[2*NB_LSP_COEFF]; /* the quantized LP coefficients in Q3.12 computed from the qLSP one after interpolation: two sets, one for each subframe */ word16_t weightedqLPCoefficients[2*NB_LSP_COEFF]; /* the qLP coefficients in Q3.12 weighted according to spec A3.3.3 */ word16_t LSPCoefficients[NB_LSP_COEFF]; /* the LSP coefficients in Q15 */ @@ -116,17 +136,101 @@ void bcg729Encoder(bcg729EncoderChannelContextStruct *encoderChannelContext, int int parametersIndex = 4; /* index to insert parameters in the parameters output array */ word16_t impulseResponseInput[L_SUBFRAME]; /* input buffer for the impulse response computation: in Q12, 1 followed by all zeros see spec A3.5*/ + /* used for VAD */ + word32_t reflectionCoefficient = 0; /* in Q31, computed during LP generation */ + word32_t autoCorrelationCoefficients[NB_LSP_COEFF+3]; /* if VAD is enabled we must compute 13 coefficients, 11 otherwise but used only internally by computeLP function in that case */ + word32_t noLagAutoCorrelationCoefficients[NB_LSP_COEFF+3]; /* DTX must have access to autocorrelation Coefficients on which lag windowing as not been applied */ + int8_t autoCorrelationCoefficientsScale; /* autocorrelation coefficients are normalised by computeLP, must get their scaling factor */ + /*****************************************************************************************/ /*** on frame basis : preProcessing, LP Analysis, Open-loop pitch search ***/ preProcessing(encoderChannelContext, inputFrame, encoderChannelContext->signalLastInputFrame); /* output of the function in the signal buffer */ - computeLP(encoderChannelContext->signalBuffer, LPCoefficients); /* use the whole signal Buffer for windowing and autocorrelation */ + /* use the whole signal Buffer for windowing and autocorrelation */ + /* autoCorrelation Coefficients are computed and used internally, in case of VAD we must compute and retrieve 13 coefficients, compute only 11 when VAD is disabled */ + computeLP(encoderChannelContext->signalBuffer, LPCoefficients, &reflectionCoefficient, autoCorrelationCoefficients, noLagAutoCorrelationCoefficients, &autoCorrelationCoefficientsScale, (encoderChannelContext->VADChannelContext != NULL)?NB_LSP_COEFF+3:NB_LSP_COEFF+1); /*** compute LSP: it might fail, get the previous one in this case ***/ if (!LP2LSPConversion(LPCoefficients, LSPCoefficients)) { /* unable to find the 10 roots repeat previous LSP */ memcpy(LSPCoefficients, encoderChannelContext->previousLSPCoefficients, NB_LSP_COEFF*sizeof(word16_t)); } + /*********** VAD *****************/ + if (encoderChannelContext->VADChannelContext != NULL) { /* if VAD is not enable, no context */ + uint8_t VADflag = 1; + /* update DTX context */ + updateDTXContext(encoderChannelContext->DTXChannelContext, noLagAutoCorrelationCoefficients, autoCorrelationCoefficientsScale); + + /*** compute LSF in Q2.13 : lsf = arcos(lsp) range [0, Pi[ spec 3.2.4 eq18 ***/ + /* TODO : remove it from LSPQuantizationFunction and perform it out of enableVAD test */ + for (i=0; iVADChannelContext, reflectionCoefficient, LSFCoefficients, autoCorrelationCoefficients, autoCorrelationCoefficientsScale, encoderChannelContext->signalCurrentFrame); + + /* call encodeSIDFrame even if it is a voice frame as it will update DTXContext with current VADflag */ + encodeSIDFrame(encoderChannelContext->DTXChannelContext, encoderChannelContext->previousLSPCoefficients, encoderChannelContext->previousqLSPCoefficients, VADflag, encoderChannelContext->previousqLSF, &(encoderChannelContext->excitationVector[L_PAST_EXCITATION]), qLPCoefficients, bitStream, bitStreamLength); + + if (VADflag == 0 ) { /* NOISE frame has been encoded */ + word16_t residualSignal[L_FRAME]; + /* update encoder context : generate weighted signal */ + /*** Compute the weighted Quantized LP Coefficients according to spec A3.3.3 ***/ + /* weightedqLPCoefficients[0] = qLPCoefficients[0]*Gamma^(i+1) (i=0..9) with Gamma = 0.75 in Q15 */ + weightedqLPCoefficients[0] = MULT16_16_P15(qLPCoefficients[0], GAMMA_E1); + weightedqLPCoefficients[1] = MULT16_16_P15(qLPCoefficients[1], GAMMA_E2); + weightedqLPCoefficients[2] = MULT16_16_P15(qLPCoefficients[2], GAMMA_E3); + weightedqLPCoefficients[3] = MULT16_16_P15(qLPCoefficients[3], GAMMA_E4); + weightedqLPCoefficients[4] = MULT16_16_P15(qLPCoefficients[4], GAMMA_E5); + weightedqLPCoefficients[5] = MULT16_16_P15(qLPCoefficients[5], GAMMA_E6); + weightedqLPCoefficients[6] = MULT16_16_P15(qLPCoefficients[6], GAMMA_E7); + weightedqLPCoefficients[7] = MULT16_16_P15(qLPCoefficients[7], GAMMA_E8); + weightedqLPCoefficients[8] = MULT16_16_P15(qLPCoefficients[8], GAMMA_E9); + weightedqLPCoefficients[9] = MULT16_16_P15(qLPCoefficients[9], GAMMA_E10); + weightedqLPCoefficients[10] = MULT16_16_P15(qLPCoefficients[10], GAMMA_E1); + weightedqLPCoefficients[11] = MULT16_16_P15(qLPCoefficients[11], GAMMA_E2); + weightedqLPCoefficients[12] = MULT16_16_P15(qLPCoefficients[12], GAMMA_E3); + weightedqLPCoefficients[13] = MULT16_16_P15(qLPCoefficients[13], GAMMA_E4); + weightedqLPCoefficients[14] = MULT16_16_P15(qLPCoefficients[14], GAMMA_E5); + weightedqLPCoefficients[15] = MULT16_16_P15(qLPCoefficients[15], GAMMA_E6); + weightedqLPCoefficients[16] = MULT16_16_P15(qLPCoefficients[16], GAMMA_E7); + weightedqLPCoefficients[17] = MULT16_16_P15(qLPCoefficients[17], GAMMA_E8); + weightedqLPCoefficients[18] = MULT16_16_P15(qLPCoefficients[18], GAMMA_E9); + weightedqLPCoefficients[19] = MULT16_16_P15(qLPCoefficients[19], GAMMA_E10); + + /*** Compute weighted signal according to spec A3.3.3, this function also compute LPResidualSignal(entire frame values) as specified in eq A.3 ***/ + computeWeightedSpeech(encoderChannelContext->signalCurrentFrame, qLPCoefficients, weightedqLPCoefficients, &(encoderChannelContext->weightedInputSignal[MAXIMUM_INT_PITCH_DELAY]), residualSignal); /* weightedInputSignal contains MAXIMUM_INT_PITCH_DELAY values from previous frame, points to current frame */ + + /* update the target Signal : targetSignal = residualSignal - excitationVector */ + for (subframeIndex=0; subframeIndextargetSignal[NB_LSP_COEFF+i] = SUB16(residualSignal[subframeIndex+i], encoderChannelContext->excitationVector[L_PAST_EXCITATION+subframeIndex+i]); + } + synthesisFilter(&(encoderChannelContext->targetSignal[NB_LSP_COEFF]), &(weightedqLPCoefficients[LPCoefficientsIndex]), &(encoderChannelContext->targetSignal[NB_LSP_COEFF])); + LPCoefficientsIndex+= NB_LSP_COEFF; + } + + /*** memory updates ***/ + /* shift left by L_FRAME the signal buffer */ + memmove(encoderChannelContext->signalBuffer, &(encoderChannelContext->signalBuffer[L_FRAME]), (L_LP_ANALYSIS_WINDOW-L_FRAME)*sizeof(word16_t)); + /* shift left by L_FRAME the weightedInputSignal buffer */ + memmove(encoderChannelContext->weightedInputSignal, &(encoderChannelContext->weightedInputSignal[L_FRAME]), MAXIMUM_INT_PITCH_DELAY*sizeof(word16_t)); + /* shift left by L_FRAME the excitationVector */ + memmove(encoderChannelContext->excitationVector, &(encoderChannelContext->excitationVector[L_FRAME]), L_PAST_EXCITATION*sizeof(word16_t)); + + return; + } + } + + /* set generated bitStream length: active voice is compressed into 80 bits */ + *bitStreamLength = 10; + + /*********** VAD *****************/ + + + + + /*** LSPQuantization and compute L0, L1, L2, L3: the first four parameters ***/ LSPQuantization(encoderChannelContext, LSPCoefficients, qLSPCoefficients, parameters); diff --git a/include/findOpenLoopPitchDelay.h b/src/findOpenLoopPitchDelay.h similarity index 100% rename from include/findOpenLoopPitchDelay.h rename to src/findOpenLoopPitchDelay.h diff --git a/include/fixedCodebookSearch.h b/src/fixedCodebookSearch.h similarity index 100% rename from include/fixedCodebookSearch.h rename to src/fixedCodebookSearch.h diff --git a/include/fixedPointMacros.h b/src/fixedPointMacros.h similarity index 87% rename from include/fixedPointMacros.h rename to src/fixedPointMacros.h index aa1493a..c4ff573 100644 --- a/include/fixedPointMacros.h +++ b/src/fixedPointMacros.h @@ -44,6 +44,9 @@ /* SATURATE Macro shall be called with MAXINT(nbits). Ex: SATURATE(x,MAXINT16) with MAXINT16 defined to 2pow(16) - 1 */ #define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a+1) ? -(a+1) : (x))) +/* absolute value */ +#define ABS(a) ((a>0) ? a : -a) + /*** add and sub ***/ #define ADD16(a,b) ((word16_t)((word16_t)(a)+(word16_t)(b))) #define SUB16(a,b) ((word16_t)(a)-(word16_t)(b)) @@ -54,15 +57,23 @@ /* WARNING: MULT16_32_QX use MULT16_16 macro but the first multiplication must actually be a 16bits * 32bits with result on 32 bits and not a 16*16 */ /* MULT16_16 is then implemented here as a 32*32 bits giving result on 32 bits */ #define MULT16_16(a,b) ((word32_t)((word32_t)(a))*((word32_t)(b))) +#define MULT16_32(a,b) ((word32_t)((word16_t)(a))*((word32_t)(b))) #define UMULT16_16(a,b) ((uword32_t)((word32_t)(a))*((word32_t)(b))) #define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b)))) #define MSU16_16(c,a,b) (SUB32((c),MULT16_16((a),(b)))) #define DIV32(a,b) (((word32_t)(a))/((word32_t)(b))) #define UDIV32(a,b) (((uword32_t)(a))/((uword32_t)(b))) -/* Unsigned Q4 operations */ +/* Q3 operations */ +#define MULT16_16_Q3(a,b) (SHR(MULT16_16((a),(b)),3)) +#define MULT16_32_Q3(a,b) ADD32(MULT16_16((a),SHR((b),3)), SHR(MULT16_16((a),((b)&0x00000007)),3)) +#define MAC16_16_Q3(c,a,b) ADD32(c,MULT16_16_Q3(a,b)) + +/* Q4 operations */ +#define MULT16_16_Q4(a,b) (SHR(MULT16_16((a),(b)),4)) #define UMULT16_16_Q4(a,b) (SHR(UMULT16_16((a),(b)),4)) #define UMAC16_16_Q4(c,a,b) ADD32(c,UMULT16_16_Q4(a,b)) +#define MAC16_16_Q4(c,a,b) ADD32(c,MULT16_16_Q4(a,b)) /* Q11 operations */ #define MULT16_16_Q11(a,b) (SHR(MULT16_16((a),(b)),11)) @@ -79,14 +90,17 @@ #define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13)) #define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13)) #define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13)) +#define MAC16_16_Q13(c,a,b) ADD32(c,MULT16_16_Q13(a,b)) #define MAC16_32_Q13(c,a,b) ADD32(c,MULT16_32_Q13(a,b)) /* Q14 operations */ #define MULT16_32_P14(a,b) ADD32(MULT16_16((a),SHR((b),14)), PSHR(MULT16_16((a),((b)&0x00003fff)),14)) +#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14)) #define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14)) #define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14)) #define MAC16_16_Q14(c,a,b) ADD32(c,MULT16_16_Q14(a,b)) #define MSU16_16_Q14(c,a,b) SUB32(c,MULT16_16_Q14(a,b)) +#define MAC16_32_Q14(c,a,b) ADD32(c,MULT16_32_Q14(a,b)) /* Q15 operations */ #define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15)) @@ -96,6 +110,8 @@ #define MAC16_32_P15(c,a,b) ADD32(c,MULT16_32_P15(a,b)) /* 64 bits operations */ +#define ADD64(a,b) ((word64_t)(a)+(word64_t)(b)) +#define SUB64(a,b) ((word64_t)(a)-(word32_t)(b)) #define ADD64_32(a,b) ((word64_t)(a)+(word32_t)(b)) #define MULT32_32(a,b) ((word64_t)((word64_t)(a)*((word64_t)(b)))) #define DIV64(a,b) ((word64_t)(a)/(word64_t)(b)) diff --git a/include/floatingPointMacros.h b/src/floatingPointMacros.h similarity index 100% rename from include/floatingPointMacros.h rename to src/floatingPointMacros.h diff --git a/include/g729FixedPointMath.h b/src/g729FixedPointMath.h similarity index 100% rename from include/g729FixedPointMath.h rename to src/g729FixedPointMath.h diff --git a/include/gainQuantization.h b/src/gainQuantization.h similarity index 100% rename from include/gainQuantization.h rename to src/gainQuantization.h diff --git a/include/interpolateqLSP.h b/src/interpolateqLSP.h similarity index 100% rename from include/interpolateqLSP.h rename to src/interpolateqLSP.h diff --git a/include/postFilter.h b/src/postFilter.h similarity index 100% rename from include/postFilter.h rename to src/postFilter.h diff --git a/include/postProcessing.h b/src/postProcessing.h similarity index 100% rename from include/postProcessing.h rename to src/postProcessing.h diff --git a/include/preProcessing.h b/src/preProcessing.h similarity index 100% rename from include/preProcessing.h rename to src/preProcessing.h diff --git a/include/qLSP2LP.h b/src/qLSP2LP.h similarity index 100% rename from include/qLSP2LP.h rename to src/qLSP2LP.h diff --git a/include/typedef.h b/src/typedef.h similarity index 71% rename from include/typedef.h rename to src/typedef.h index 7fa0a3a..510a68b 100644 --- a/include/typedef.h +++ b/src/typedef.h @@ -32,6 +32,58 @@ typedef int32_t word32_t; typedef uint32_t uword32_t; typedef int64_t word64_t; +struct bcg729VADChannelContextStruct_struct { + /* buffer used during the init period - first N0(32) frames */ + word32_t initEfSum; + word32_t initZCSum; + word32_t initLSFSum[NB_LSP_COEFF]; + uint8_t nbValidInitFrame; /* VAD init needs 32 frames to be completed but we must use only frame with Ef > 15dB so we must count them */ + + + word16_t meanZC; /* in Q15 */ + word16_t meanEf; /* in Q11 */ + word16_t meanEl; /* in Q11 */ + word16_t meanLSF[NB_LSP_COEFF]; /* in Q2.13 */ + uint32_t frameCount; /* number of frame processed, used to close the initialisation period */ + uint32_t updateCount; /* number of frame leading to an update of running averages */ + word16_t EfBuffer[N0]; /* store the last N0 Ef values to be able to retrieve the min */ + uint8_t SVDm1; /* smoothed voice activity decision on last frame */ + uint8_t SVDm2; /* smoothed voice activity decision on 2 frames ago */ + uint32_t Count_inert; /* defined in appendix II, used to implement hysteresis on VOICE->NOISE switching */ + uint8_t secondStageVADSmoothingFlag; /* flag defined as FVD-1 in spec B3.6 */ + uint32_t smoothingCounter; /* counter Ce defined in spec B3.6 */ + word16_t previousFrameEf; /* in Q11 */ + word32_t noiseContinuityCounter; /* counter Cs defined in spec B3.6 */ +}; +typedef struct bcg729VADChannelContextStruct_struct bcg729VADChannelContextStruct; + +struct bcg729DTXChannelContextStruct_struct { + /* store past autocorrelations coefficients for 6 past frames and current one(at index 0) spec B4.1.1 */ + word32_t autocorrelationCoefficients[7][NB_LSP_COEFF+1]; + int8_t autocorrelationCoefficientsScale[7]; + + uint8_t previousVADflag; /* previous frame VAD : 1 VOICE 0 NOISE */ + word32_t previousResidualEnergy; + uint8_t previousResidualEnergyScale; + int8_t previousDecodedLogEnergy; /* store the last decodedLog energy sent in a SID frame */ + uint8_t count_fr; /* count the number of noise frame since last SID frame was sent */ + word32_t SIDLPCoefficientAutocorrelation[NB_LSP_COEFF+1]; /* the autocorrelation of LP coefficients as described in eq B.13 in Q20 */ + word16_t currentSIDGain; /* gain in Q3 */ + word16_t smoothedSIDGain; /* gain in Q3 */ + uint16_t pseudoRandomSeed; /* seed used in the pseudo random number generator for excitation generation */ + word16_t qLSPCoefficients[NB_LSP_COEFF]; /* current Quantized LSP coefficient in Q15, saved to be re-used in case of untransmitted frame */ +}; +typedef struct bcg729DTXChannelContextStruct_struct bcg729DTXChannelContextStruct; + +struct bcg729CNGChannelContextStruct_struct { + word16_t receivedSIDGain; /* gain in Q3 */ + word16_t smoothedSIDGain; /* gain in Q3 */ + word16_t qLSP[NB_LSP_COEFF]; /* qLSP in Q0.15 */ + word64_t lastFrameEnergy; /* in Q0 */ +}; + +typedef struct bcg729CNGChannelContextStruct_struct bcg729CNGChannelContextStruct; + /* define the context structure to store all static data for a decoder channel */ struct bcg729DecoderChannelContextStruct_struct { /*** buffers used in decoder bloc ***/ @@ -44,6 +96,7 @@ struct bcg729DecoderChannelContextStruct_struct { word16_t fixedCodebookGain; /* in Q14.1 */ word16_t reconstructedSpeech[NB_LSP_COEFF+L_FRAME]; /* in Q0, output of the LP synthesis filter, the first 10 words store the previous frame output */ uint16_t pseudoRandomSeed; /* seed used in the pseudo random number generator */ + uint16_t CNGpseudoRandomSeed; /* seed used in the pseudo random number generator for CNG */ /*** buffers used in decodeLSP bloc ***/ word16_t lastqLSF[NB_LSP_COEFF]; /* this buffer stores the last qLSF to be used in case of frame lost in Q2.13 */ @@ -73,6 +126,10 @@ struct bcg729DecoderChannelContextStruct_struct { word32_t outputY2; word32_t outputY1; + /* SID frame management */ + bcg729CNGChannelContextStruct *CNGChannelContext; /* store informations specific to CNG */ + uint8_t previousFrameIsActiveFlag; /* store last processed frame type */ + }; /* define the context structure to store all static data for an encoder channel */ @@ -108,6 +165,9 @@ struct bcg729EncoderChannelContextStruct_struct { /*** buffer used in gainQuantization ***/ word16_t previousGainPredictionError[4]; /* the last four gain prediction error U(m) eq69 and eq72, spec3.9.1 in Q10*/ + /*** VAD management ***/ + bcg729VADChannelContextStruct *VADChannelContext; + bcg729DTXChannelContextStruct *DTXChannelContext; }; /* MAXINTXX define the maximum signed integer value on XX bits(2^(XX-1) - 1) */ diff --git a/src/utils.c b/src/utils.c index 3d58864..bdbfb8a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -52,6 +52,23 @@ void insertionSort(word16_t x[], int length) return; } +/*****************************************************************************/ +/* getMinInArray : get the minimum value from an array */ +/* parameters : */ +/* -(i) x: the array to be searched */ +/* -(i) length: the array length */ +/* returns : the minimum value found in the array */ +/* */ +/*****************************************************************************/ +word16_t getMinInArray(word16_t x[], int length) { + word16_t min = MAXINT16; + int i; + for (i=0; i L0 (1 bit) */ +/* 1 -> L1 (5 bits) */ +/* 2 -> L2 (4 bits) */ +/* 3 -> Gain (5 bits) */ +/* parameters: */ +/* -(i) parameters : 4 values parameters array */ +/* -(o) bitStream : the 4 values streamed on 15 bits in a */ +/* 2*8bits values array */ +/* */ +/*****************************************************************************/ + +void CNGparametersArray2BitStream(uint16_t parameters[], uint8_t bitStream[]) { + bitStream[0] = ((parameters[0]&((uint16_t) 0x1))<<7) | + ((parameters[1]&((uint16_t) 0x1f))<<2) | + ((parameters[2]>>2)&((uint16_t) 0x3)); + + bitStream[1] = ((parameters[2]&((uint16_t) 0x03))<<6) | + ((parameters[3]&((uint16_t) 0x1f))<<1); +} + /*****************************************************************************/ /* parametersArray2BitStream : convert bitStream to an array of parameters */ /* reverse operation of previous funtion */ @@ -327,3 +367,19 @@ void parametersBitStream2Array(uint8_t bitStream[], uint16_t parameters[]) return; } + +/*****************************************************************************/ +/* pseudoRandom : generate pseudo random number as in spec 4.4.4 eq96 */ +/* parameters: */ +/* -(i/o) randomGeneratorSeed(updated by this function) */ +/* return value : */ +/* - a unsigned 16 bits pseudo random number */ +/* */ +/*****************************************************************************/ +uint16_t pseudoRandom(uint16_t *randomGeneratorSeed) +{ + /* pseudoRandomSeed is stored in an uint16_t var, we shall not worry about overflow here */ + /* pseudoRandomSeed*31821 + 13849; */ + *randomGeneratorSeed = MAC16_16(13849, (*randomGeneratorSeed), 31821); + return *randomGeneratorSeed; +} diff --git a/include/utils.h b/src/utils.h similarity index 82% rename from include/utils.h rename to src/utils.h index 8f07c60..c6028f7 100644 --- a/include/utils.h +++ b/src/utils.h @@ -37,6 +37,16 @@ /*****************************************************************************/ void insertionSort(word16_t x[], int length); +/*****************************************************************************/ +/* getMinInArray : get the minimum value from an array */ +/* parameters : */ +/* -(i) x: the array to be searched */ +/* -(i) length: the array length */ +/* returns : the minimum value found in the array */ +/* */ +/*****************************************************************************/ +word16_t getMinInArray(word16_t x[], int length); + /*****************************************************************************/ /* computeParity : compute parity for pitch delay adaptative codebook index */ /* XOR of the 6 MSB (pitchDelay on 8 bits) */ @@ -178,6 +188,21 @@ void computeGainPredictionError(word16_t fixedCodebookGainCorrectionFactor, word /*****************************************************************************/ void parametersArray2BitStream(uint16_t parameters[], uint8_t bitStream[]); +/*****************************************************************************/ +/* CNGparametersArray2BitStream : convert array of parameters to bitStream */ +/* according to spec B4.3 - Table B2 and following mapping of values */ +/* 0 -> L0 (1 bit) */ +/* 1 -> L1 (5 bits) */ +/* 2 -> L2 (4 bits) */ +/* 3 -> Gain (5 bits) */ +/* parameters: */ +/* -(i) parameters : 4 values parameters array */ +/* -(o) bitStream : the 4 values streamed on 15 bits in a */ +/* 2*8bits values array */ +/* */ +/*****************************************************************************/ +void CNGparametersArray2BitStream(uint16_t parameters[], uint8_t bitStream[]); + /*****************************************************************************/ /* parametersArray2BitStream : convert bitStream to an array of parameters */ /* reverse operation of previous funtion */ @@ -188,4 +213,14 @@ void parametersArray2BitStream(uint16_t parameters[], uint8_t bitStream[]); /* */ /*****************************************************************************/ void parametersBitStream2Array(uint8_t bitStream[], uint16_t parameters[]); + +/*****************************************************************************/ +/* pseudoRandom : generate pseudo random number as in spec 4.4.4 eq96 */ +/* parameters: */ +/* -(i/o) randomGeneratorSeed(updated by this function) */ +/* return value : */ +/* - a unsigned 16 bits pseudo random number */ +/* */ +/*****************************************************************************/ +uint16_t pseudoRandom(uint16_t *randomGeneratorSeed); #endif /* ifndef UTILS_H */ diff --git a/src/vad.c b/src/vad.c new file mode 100644 index 0000000..d29c478 --- /dev/null +++ b/src/vad.c @@ -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 +#include + +#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; iEfBuffer[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; iframeCount == 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; imeanLSF[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; iinitLSFSum[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; imeanLSF[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; imeanLSF[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 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; +} diff --git a/src/vad.h b/src/vad.h new file mode 100644 index 0000000..c98a428 --- /dev/null +++ b/src/vad.h @@ -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 */ diff --git a/test/bin/Makefile.am b/test/bin/Makefile.am index e343173..dd39130 100644 --- a/test/bin/Makefile.am +++ b/test/bin/Makefile.am @@ -1,6 +1,6 @@ check_PROGRAMS=adaptativeCodebookSearchTest computeAdaptativeCodebookGainTest computeLPTest computeWeightedSpeechTest decodeAdaptativeCodeVectorTest decodeFixedCodeVectorTest decodeGainsTest decodeLSPTest \ decoderTest encoderTest decoderMultiChannelTest encoderMultiChannelTest findOpenLoopPitchDelayTest fixedCodebookSearchTest g729FixedPointMathTest gainQuantizationTest interpolateqLSPAndConvert2LPTest \ - LP2LSPConversionTest LPSynthesisFilterTest LSPQuantizationTest postFilterTest postProcessingTest preProcessingTest + LP2LSPConversionTest LPSynthesisFilterTest LSPQuantizationTest postFilterTest postProcessingTest preProcessingTest computeNoiseExcitationTest CNGdecoderTest encoderVADTest util_src=$(top_srcdir)/test/src/testUtils.c adaptativeCodebookSearchTest_SOURCES=$(top_srcdir)/test/src/adaptativeCodebookSearchTest.c $(util_src) @@ -12,6 +12,7 @@ decodeFixedCodeVectorTest_SOURCES=$(top_srcdir)/test/src/decodeFixedCodeVectorTe decodeGainsTest_SOURCES=$(top_srcdir)/test/src/decodeGainsTest.c $(util_src) decodeLSPTest_SOURCES=$(top_srcdir)/test/src/decodeLSPTest.c $(util_src) decoderTest_SOURCES=$(top_srcdir)/test/src/decoderTest.c $(util_src) +CNGdecoderTest_SOURCES=$(top_srcdir)/test/src/CNGdecoderTest.c $(util_src) decoderMultiChannelTest_SOURCES=$(top_srcdir)/test/src/decoderMultiChannelTest.c $(util_src) encoderTest_SOURCES=$(top_srcdir)/test/src/encoderTest.c $(util_src) encoderMultiChannelTest_SOURCES=$(top_srcdir)/test/src/encoderMultiChannelTest.c $(util_src) @@ -27,7 +28,9 @@ LSPQuantizationTest_SOURCES=$(top_srcdir)/test/src/LSPQuantizationTest.c $(util_ postFilterTest_SOURCES=$(top_srcdir)/test/src/postFilterTest.c $(util_src) postProcessingTest_SOURCES=$(top_srcdir)/test/src/postProcessingTest.c $(util_src) preProcessingTest_SOURCES=$(top_srcdir)/test/src/preProcessingTest.c $(util_src) +computeNoiseExcitationTest_SOURCES=$(top_srcdir)/test/src/computeNoiseExcitationTest.c $(util_src) +encoderVADTest_SOURCES=$(top_srcdir)/test/src/encoderVADTest.c $(util_src) LDADD= $(top_builddir)/src/libbcg729.la -AM_CPPFLAGS=-I$(top_srcdir)/include/ +AM_CPPFLAGS=-I$(top_srcdir)/include/ -I$(top_srcdir)/src/ AM_LDFLAGS=-all-static diff --git a/test/src/CNGdecoderTest.c b/test/src/CNGdecoderTest.c new file mode 100644 index 0000000..22e366d --- /dev/null +++ b/test/src/CNGdecoderTest.c @@ -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 +#include +#include + +#include +#include + + +#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 +#include +#include + + +#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>7)&0x01, (bitStream[0]>>2)&0x1F, ((bitStream[0]&0x03)<<2) | ((bitStream[1]>>6)&0x03), ((bitStream[1])>>1)&0x1F); + for (i=4; i +#include +#include + +#include + + +#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>7)&0x01, (bitStream[0]>>2)&0x1F, ((bitStream[0]&0x03)<<2) | ((bitStream[1]>>6)&0x03), ((bitStream[1])>>1)&0x1F); + for (i=4; i 2)) print "# testCampaign [-s] #\n"; print "# test name in the following list : #\n"; print "# - decoder #\n"; + print "# - CNGdecoder #\n"; print "# - decodeLSP #\n"; print "# - interpolateqLSPAndConvert2LP #\n"; print "# - decodeAdaptativeCodeVector #\n"; @@ -198,7 +199,8 @@ if ($ARGV[0] eq "-s") { "LPSynthesisFilter" => [0,0], "postFilter" => [0,0], "postProcessing" => [0,0], - "decoder" => [0,0] + "decoder" => [0,0], + "CNGdecoder" => [0,0] );