changeset 613:7ce83c70ec5f

libgsmhr1: integrate encoder DTX functions, first round
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 05 Mar 2026 07:53:49 +0000
parents a5879e04ad47
children cf74197b9d4f
files libgsmhr1/Makefile libgsmhr1/dtx_enc.c libgsmhr1/dtx_enc.h
diffstat 3 files changed, 689 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/libgsmhr1/Makefile	Thu Mar 05 07:35:43 2026 +0000
+++ b/libgsmhr1/Makefile	Thu Mar 05 07:53:49 2026 +0000
@@ -1,12 +1,12 @@
-OBJS=	dec_func.o dec_state.o dhf_packed.o dhf_params.o dtx_dec.o dtx_rxfe.o \
-	enc_out_order.o enc_state.o err_conc.o mathdp31.o mathhalf.o \
+OBJS=	dec_func.o dec_state.o dhf_packed.o dhf_params.o dtx_dec.o dtx_enc.o \
+	dtx_rxfe.o enc_out_order.o enc_state.o err_conc.o mathdp31.o mathhalf.o\
 	pack_frame.o paramval_cod.o paramval_common.o paramval_dec.o rtp_in.o \
 	rtp_in_direct.o rxfe.o rxfe_create.o sid_cw_params.o sid_detect.o \
 	sid_reset.o sp_dec.o sp_rom.o tfo.o twts002_in.o twts002_out.o \
 	unpack_frame.o
-HDRS=	dec_func.h dec_state.h dtx_const.h dtx_dec.h dtx_rxfe.h enc_out_order.h\
-	enc_state.h err_conc.h mathdp31.h mathhalf.h namespace.h rxfe.h \
-	sp_rom.h tw_gsmhr.h typedefs.h
+HDRS=	dec_func.h dec_state.h dtx_const.h dtx_dec.h dtx_enc.h dtx_rxfe.h \
+	enc_out_order.h enc_state.h err_conc.h mathdp31.h mathhalf.h \
+	namespace.h rxfe.h sp_rom.h tw_gsmhr.h typedefs.h
 LIB=	libgsmhr1.a
 
 include ../config.defs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgsmhr1/dtx_enc.c	Thu Mar 05 07:53:49 2026 +0000
@@ -0,0 +1,656 @@
+/***************************************************************************
+ *
+ *   File Name: dtx_enc.c
+ *
+ *   Derivation: this module is the subset of GSM 06.06 dtx.c
+ *   reduced to those functions that are used only by the speech
+ *   encoder.
+ *
+ **************************************************************************/
+
+/*________________________________________________________________________
+ |                                                                        |
+ |                         Include Files                                  |
+ |________________________________________________________________________|
+*/
+
+#include "typedefs.h"
+#include "namespace.h"
+#include "mathhalf.h"
+#include "mathdp31.h"
+#include "enc_state.h"
+#include "dtx_const.h"
+#include "dtx_enc.h"
+#include "dtx_rxfe.h"
+
+/*________________________________________________________________________
+ |                                                                        |
+ |                            Defines                                     |
+ |________________________________________________________________________|
+*/
+
+#define OH_SHIFT 3                     /* shift corresponding to OVERHANG */
+
+#define NP_AFLAT 4
+#define LPC_VQ_SEG 3
+
+
+/*************************************************************************
+ *
+ *   FUNCTION NAME: swComfortNoise
+ *
+ *   PURPOSE:
+ *
+ *   This routine perform the following tasks:
+ *     - generation of the speech flag (swSP)
+ *     - averaging and encoding of the comfort noise parameters
+ *     - randomization of the codebook indices
+ *
+ *
+ *   INPUTS:
+ *
+ *   swVadFrmCnt (global) - swVadFlag=0 frame counter.
+ *   If swVadFlag=1 then this counter is 0, the first frame with
+ *   swVadFlag=0 will set this counter to 1, with each additional
+ *   swVadFlag=0 frame the counter is incremented.
+ *
+ *   swVadFlag - voise activity flag. swVadFlag=0 frame with
+ *   no voice activity, swVadFlag=0 frame with voice activity
+ *
+ *   L_UnqntzdR0 - unquantized R(0), 32 bit value, output of
+ *   FLAT.
+ *
+ *   pL_UnqntzdCorr[NP+1] - unquantized correlation sequence,
+ *   also an output of FLAT.
+ *
+ *
+ *   OUTPUTS:
+ *
+ *   swCNR0 - global variable, the output quantized R0 index
+ *
+ *   pswCNLpc[3]  - global variable, the output quantized LPC to the
+ *   transmitted in the SID frame
+ *
+ *   pswCNGsp0Code[N_SUB] - global variable, the output quantized GSP0 indices
+ *
+ *   pswCNVSCode1[N_SUB] - global variable, the output quantized codevector 1
+ *   indices.
+ *
+ *   pswCNVSCode2[N_SUB] - global variable, the output quantized codevector 2
+ *   indices.
+ *
+ *
+ *   RETURN VALUE:
+ *
+ *   swSP - speech flag, swSP=1 speech frames are generated, swSP=0
+ *   SID frames are generated.
+ *
+ *************************************************************************/
+
+Shortword swComfortNoise(struct gsmhr_encoder_state *st, Shortword swVadFlag,
+			 Longword L_UnqntzdR0, Longword *pL_UnqntzdCorr)
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+
+  struct cn_state *cn_st = &st->cn_state;
+  Shortword swSP;
+  Shortword pswFinalRc[NP];
+
+  /* unquantized reference parameters */
+  Longword L_RefR0;
+  Longword pL_RefCorr[NP + 1];
+  Longword L_RefGs;
+
+  int    i;
+
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Executable Code                            |
+ |_________________________________________________________________________|
+*/
+
+  swSP = 1;
+
+  /* VadFrmCnt will indicate the number of sequential frames where */
+  /* swVadFlag == 0                                                */
+  /* ------------------------------------------------------------- */
+
+  if (swVadFlag)
+    st->swVadFrmCnt = 0;                       /* Voice acitvity present */
+  else
+    st->swVadFrmCnt = add(st->swVadFrmCnt, 1); /* no voice activity */
+
+
+  /* swNElapsed will indicate the number of frames that have elapsed */
+  /* since the last SID frame with updated comfort noise parameters  */
+  /* was generated                                                   */
+  /* --------------------------------------------------------------- */
+
+  st->swNElapsed = add(st->swNElapsed, 1);
+
+
+  /* If no voice activity was detected.  */
+  /* ----------------------------------- */
+
+  if (st->swVadFrmCnt)
+  {
+
+    /* Short speech burst ? */
+    /* -------------------- */
+
+    if (st->swVadFrmCnt == 1)
+    {
+      if (sub(st->swNElapsed, 24) < 0)
+        cn_st->swShortBurst = 1;       /* short speech burst detected */
+      else
+        cn_st->swShortBurst = 0;       /* long speech burst detected */
+    }
+
+
+    /* Update history, with this frames data */
+    /* ------------------------------------- */
+
+    updateCNHist(st, L_UnqntzdR0, pL_UnqntzdCorr,
+                 cn_st->pL_R0Hist, cn_st->ppL_CorrHist);
+
+
+    /* first SID frame */
+    /* --------------- */
+
+    if (((cn_st->swShortBurst == 0) && (st->swVadFrmCnt == OVERHANG)) ||
+        ((cn_st->swShortBurst == 1) && (st->swVadFrmCnt == 1)))
+    {
+
+      /* init. random generator */
+      /* ---------------------- */
+      cn_st->L_TxPNSeed = PN_INIT_SEED;
+
+
+      /* average GS */
+      /* ---------- */
+      avgGsHistQntz(st->pL_GsHist, &L_RefGs);
+
+
+      /* GS quantization */
+      /* --------------- */
+      cn_st->swRefGsIndex = gsQuant(L_RefGs, 0);
+
+    }
+
+
+    /* No Overhang in case of short speech bursts,                */
+    /* generate SID frames with repeated comfort noise parameters */
+    /* ---------------------------------------------------------- */
+
+    if ((cn_st->swShortBurst == 1) && (st->swVadFrmCnt < OVERHANG))
+    {
+
+      /* generate a SID frame with repeated parameters */
+      /* --------------------------------------------- */
+
+      swSP = 0;
+
+
+      /* repeat data: r0, LPC, GS */
+      /* ------------------------ */
+
+      st->swCNR0 = cn_st->swQntRefR0;
+
+      for (i = 0; i < 3; i++)
+        st->pswCNLpc[i] = cn_st->piRefVqCodewds[i];
+
+      for (i = 0; i < N_SUB; i++)
+        st->pswCNGsp0Code[i] = cn_st->swRefGsIndex;
+
+    }
+
+
+    /* generate SID frames with updated comfort noise parameters */
+    /* --------------------------------------------------------- */
+
+    if (st->swVadFrmCnt >= OVERHANG)
+    {
+
+      /* A SID frame with updated parameters */
+      /* ----------------------------------- */
+
+      swSP = 0;
+      st->swNElapsed = 0;
+
+
+      /* average R0 and correlation values */
+      /* --------------------------------- */
+
+      avgCNHist(cn_st->pL_R0Hist, cn_st->ppL_CorrHist, &L_RefR0,
+                pL_RefCorr);
+
+
+      /* now quantize the averaged R(0) */
+      /* ------------------------------ */
+
+      cn_st->swQntRefR0 = r0Quant(L_RefR0);
+
+
+      /* Quantize the averaged correlation */
+      /* --------------------------------- */
+
+      lpcCorrQntz(pL_RefCorr,
+                  pswFinalRc,
+                  cn_st->piRefVqCodewds);
+
+
+      /* update frame data: r0, LPC */
+      /* -------------------------- */
+
+      st->swCNR0 = cn_st->swQntRefR0;
+      for (i = 0; i < 3; i++)
+        st->pswCNLpc[i] = cn_st->piRefVqCodewds[i];
+
+
+      /* update subframe data (unvoiced mode): GSP0 */
+      /* ------------------------------------------ */
+
+      for (i = 0; i < N_SUB; i++)
+        st->pswCNGsp0Code[i] = cn_st->swRefGsIndex;
+
+    }
+
+
+    /* random codevectors */
+    /* ------------------ */
+
+    if (swSP == 0)
+    {
+      for (i = 0; i < N_SUB; i++)
+      {
+        st->pswCNVSCode1[i] = getPnBits(7, &cn_st->L_TxPNSeed);
+        st->pswCNVSCode2[i] = getPnBits(7, &cn_st->L_TxPNSeed);
+      }
+    }
+
+
+  }
+
+  return (swSP);
+}
+
+
+/*************************************************************************
+ *
+ *   FUNCTION NAME:  updateCNHist
+ *
+ *   PURPOSE:
+ *
+ *     Add current frame's unquantized R(0) and LPC information to the
+ *     comfort noise history, so that it will be available for
+ *     averaging.
+ *
+ *   INPUTS:
+ *
+ *     Unquantized values from the coder:
+ *
+ *
+ *     L_UnqntzdR0 - unquantized frame energy R(0), an output of FLAT
+ *
+ *     pL_UnqntzdCorr[NP+1] - unquantized correlation coefficient
+ *     array.  Also an output of FLAT.
+ *
+ *     siUpdPointer (global) - A modulo counter which counts up from
+ *     0 to OVERHANG-1.
+ *
+ *   OUTPUTS:
+ *
+ *     pL_R0History[OVERHANG] - history of the OVERHANG frames worth of
+ *     R(0).
+ *
+ *     ppL_CorrHistory[OVERHANG][NP+1] - - history of the OVERHANG
+ *     frames worth of pL_UnqntzdCorr[].
+ *
+ *   RETURN VALUE:
+ *
+ *     none
+ *
+ *************************************************************************/
+
+void updateCNHist(struct gsmhr_encoder_state *st, Longword L_UnqntzdR0,
+		  Longword *pL_UnqntzdCorr, Longword pL_R0Hist[],
+		  Longword ppL_CorrHist[OVERHANG][NP + 1])
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+
+  int    i;
+
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Executable Code                            |
+ |_________________________________________________________________________|
+*/
+
+  /* update */
+  pL_R0Hist[st->siUpdPointer] = L_UnqntzdR0;
+
+  for (i = 0; i < NP + 1; i++)
+    ppL_CorrHist[st->siUpdPointer][i] = pL_UnqntzdCorr[i];
+
+  st->siUpdPointer = (st->siUpdPointer + 1) % OVERHANG;
+}
+
+
+/*************************************************************************
+ *
+ *   FUNCTION NAME: avgCNHist
+ *
+ *   PURPOSE:
+ *
+ *     Average the unquantized R0 and LPC data stored at the encoder
+ *     to arrive at an average R0 and LPC frame for use in a SID
+ *     frame.
+ *
+ *   INPUTS:
+ *
+ *   pL_R0History[OVERHANG] - contains unquantized R(0) data from the
+ *   most recent OVERHANG frame (including this one).
+ *
+ *   ppL_CorrHistory[OVERHANG][NP+1] - Unquantized correlation
+ *   coefficients from the most recent OVERHANG frame (including this
+ *   one).  The data stored here is an output of FLAT.
+ *
+ *   OUTPUTS:
+ *
+ *   *pL_AvgdR0 - the average of pL_R0History[]
+ *
+ *   pL_AvgdCorrSeq[NP+1] - the average of ppL_CorrHistory[][].
+ *
+ *
+ *   RETURN VALUE:
+ *
+ *     none
+ *
+ *************************************************************************/
+
+void avgCNHist(Longword pL_R0History[],
+		Longword ppL_CorrHistory[OVERHANG][NP + 1],
+		Longword *pL_AvgdR0, Longword pL_AvgdCorrSeq[])
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+
+  int    i,
+         j;
+  Longword L_avg;
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Executable Code                            |
+ |_________________________________________________________________________|
+*/
+
+  /* R0 Averaging */
+  /* ------------ */
+
+  for (L_avg = 0, i = 0; i < OVERHANG; i++)
+    L_avg = L_add(L_shr(pL_R0History[i], OH_SHIFT), L_avg);
+
+  *pL_AvgdR0 = L_avg;
+
+
+  /* LPC: average the last OVERHANG frames */
+  /* ------------------------------------- */
+
+  for (j = 0; j < NP + 1; j++)
+  {
+    for (L_avg = 0, i = 0; i < OVERHANG; i++)
+    {
+      L_avg = L_add(L_shift_r(ppL_CorrHistory[i][j], -OH_SHIFT), L_avg);
+    }
+
+    pL_AvgdCorrSeq[j] = L_avg;
+  }
+
+}
+
+
+/***************************************************************************
+ *
+ *    FUNCTION NAME: lpcCorrQntz
+ *
+ *    PURPOSE:  Quantize a correlation sequence
+ *
+ *
+ *    INPUT:
+ *
+ *         pL_CorrelSeq[NP+1]
+ *                     Correlation sequence to quantize.
+ *
+ *    OUTPUTS:
+ *
+ *        pswFinalRc[0:NP-1]
+ *                     A quantized set of NP reflection coefficients.
+ *
+ *        piVQCodewds[0:2]
+ *                     An array containing the indices of the 3 reflection
+ *                     coefficient vectors selected from the three segment
+ *                     Rc-VQ.
+ *
+ *    RETURN:
+ *        None.
+ *
+ *    KEYWORDS: AFLAT,aflat,flat,vectorquantization, reflectioncoefficients
+ *
+ *************************************************************************/
+
+#if 0
+void   lpcCorrQntz(Longword pL_CorrelSeq[],
+                          Shortword pswFinalRc[],
+                          int piVQCodewds[])
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+
+  Shortword pswPOldSpace[NP_AFLAT],
+         pswPNewSpace[NP_AFLAT],
+         pswVOldSpace[2 * NP_AFLAT - 1],
+         pswVNewSpace[2 * NP_AFLAT - 1],
+        *ppswPAddrs[2],
+        *ppswVAddrs[2],
+        *pswVBar,
+         pswPBar[NP_AFLAT],
+         pswVBarSpace[2 * NP_AFLAT - 1],
+         pswFlatsRc[NP],               /* Unquantized Rc's computed by FLAT */
+         pswRc[NP + 1];                /* Temp list for the converted RC's */
+  Longword *pL_VBarFull,
+         pL_PBarFull[NP],
+         pL_VBarFullSpace[2 * NP - 1];
+
+  int    i,
+         iVec,
+         iSeg,
+         iCnt;                         /* Loop counter */
+  struct QuantList quantList,          /* A list of vectors */
+         bestPql[4];                   /* The four best vectors from
+                                        * the PreQ */
+  struct QuantList bestQl[LPC_VQ_SEG + 1];      /* Best vectors for each of
+                                                 * the three segments */
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Executable Code                            |
+ |_________________________________________________________________________|
+*/
+
+  /* Setup pointers temporary space */
+  /*--------------------------------*/
+
+  pswVBar = pswVBarSpace + NP_AFLAT - 1;
+  pL_VBarFull = pL_VBarFullSpace + NP - 1;
+  ppswPAddrs[0] = pswPOldSpace;
+  ppswPAddrs[1] = pswPNewSpace;
+  ppswVAddrs[0] = pswVOldSpace + NP_AFLAT - 1;
+  ppswVAddrs[1] = pswVNewSpace + NP_AFLAT - 1;
+
+
+  /* Set up pL_PBarFull and pL_VBarFull initial conditions, using the   */
+  /* autocorrelation sequence derived from the optimal reflection       */
+  /* coefficients computed by FLAT. The initial conditions are shifted  */
+  /* right by RSHIFT bits. These initial conditions, stored as          */
+  /* Longwords, are used to initialize PBar and VBar arrays for the     */
+  /* next VQ segment.                                                   */
+  /*--------------------------------------------------------------------*/
+
+  initPBarFullVBarFullL(pL_CorrelSeq, pL_PBarFull, pL_VBarFull);
+
+  /* Set up initial PBar and VBar initial conditions, using pL_PBarFull */
+  /* and pL_VBarFull arrays initialized above. These are the initial    */
+  /* PBar and VBar conditions to be used by the AFLAT recursion at the  */
+  /* 1-st Rc-VQ segment.                                                */
+  /*--------------------------------------------------------------------*/
+
+  initPBarVBarL(pL_PBarFull, pswPBar, pswVBar);
+
+  for (iSeg = 1; iSeg <= LPC_VQ_SEG; iSeg++)
+  {
+    /* initialize candidate list */
+    /*---------------------------*/
+
+    quantList.iNum = psrPreQSz[iSeg - 1];
+    quantList.iRCIndex = 0;
+
+    /* do aflat for all vectors in the list */
+    /*--------------------------------------*/
+
+    setupPreQ(iSeg, quantList.iRCIndex);        /* set up vector ptrs */
+
+    for (iCnt = 0; iCnt < quantList.iNum; iCnt++)
+    {
+      /* get a vector */
+      /*--------------*/
+
+      getNextVec(pswRc);
+
+      /* clear the limiter flag */
+      /*------------------------*/
+
+      iLimit = 0;
+
+      /* find the error values for each vector */
+      /*---------------------------------------*/
+
+      quantList.pswPredErr[iCnt] =
+              aflatRecursion(&pswRc[psvqIndex[iSeg - 1].l],
+                             pswPBar, pswVBar,
+                             ppswPAddrs, ppswVAddrs,
+                             psvqIndex[iSeg - 1].len);
+
+      /* check the limiter flag */
+      /*------------------------*/
+
+      if (iLimit)
+        quantList.pswPredErr[iCnt] = 0x7fff;    /* set error to bad value */
+
+    }                                  /* done list loop */
+
+    /* find 4 best prequantizer levels */
+    /*---------------------------------*/
+
+    findBestInQuantList(quantList, 4, bestPql);
+
+    for (iVec = 0; iVec < 4; iVec++)
+    {
+
+      /* initialize quantizer list */
+      /*---------------------------*/
+
+      quantList.iNum = psrQuantSz[iSeg - 1];
+      quantList.iRCIndex = bestPql[iVec].iRCIndex * psrQuantSz[iSeg - 1];
+
+      setupQuant(iSeg, quantList.iRCIndex);     /* set up vector ptrs */
+
+      /* do aflat recursion on each element of list */
+      /*--------------------------------------------*/
+
+      for (iCnt = 0; iCnt < quantList.iNum; iCnt++)
+      {
+        /* get a vector */
+        /*--------------*/
+
+        getNextVec(pswRc);
+
+        /* clear the limiter flag */
+        /*------------------------*/
+
+        iLimit = 0;
+
+        /* find the error values for each vector */
+        /*---------------------------------------*/
+
+        quantList.pswPredErr[iCnt] =
+                aflatRecursion(&pswRc[psvqIndex[iSeg - 1].l],
+                               pswPBar, pswVBar,
+                               ppswPAddrs, ppswVAddrs,
+                               psvqIndex[iSeg - 1].len);
+
+        /* check the limiter flag */
+        /*------------------------*/
+
+        if (iLimit)
+          quantList.pswPredErr[iCnt] = 0x7fff;  /* set error to the worst
+                                                 * value */
+
+      }                                /* done list loop */
+
+      /* find best quantizer vector for this segment, and save it */
+      /*----------------------------------------------------------*/
+
+      findBestInQuantList(quantList, 1, bestQl);
+      if (iVec == 0)
+        bestQl[iSeg] = bestQl[0];
+      else if (sub(bestQl[iSeg].pswPredErr[0], bestQl[0].pswPredErr[0]) > 0)
+        bestQl[iSeg] = bestQl[0];
+
+    }
+
+    /* find the quantized reflection coefficients */
+    /*--------------------------------------------*/
+
+    setupQuant(iSeg, bestQl[iSeg].iRCIndex);    /* set up vector ptrs */
+    getNextVec((Shortword *) (pswFinalRc - 1));
+
+
+    /* Update pBarFull and vBarFull for the next Rc-VQ segment, and */
+    /* update the pswPBar and pswVBar for the next Rc-VQ segment    */
+    /*--------------------------------------------------------------*/
+
+    if (iSeg < LPC_VQ_SEG)
+      aflatNewBarRecursionL(&pswFinalRc[psvqIndex[iSeg - 1].l - 1], iSeg,
+                            pL_PBarFull, pL_VBarFull, pswPBar, pswVBar);
+
+  }
+
+  /* find the quantizer index (the values to be output in the symbol file) */
+  /*-----------------------------------------------------------------*/
+
+  for (iSeg = 1; iSeg <= LPC_VQ_SEG; iSeg++)
+    piVQCodewds[iSeg - 1] = bestQl[iSeg].iRCIndex;
+
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgsmhr1/dtx_enc.h	Thu Mar 05 07:53:49 2026 +0000
@@ -0,0 +1,28 @@
+#ifndef __DTX_ENC
+#define __DTX_ENC
+
+#include "typedefs.h"
+#include "tw_gsmhr.h"
+
+/*________________________________________________________________________
+ |                                                                        |
+ |                      Function Prototypes                               |
+ |________________________________________________________________________|
+*/
+
+void avgCNHist(Longword pL_R0History[],
+		Longword ppL_CorrHistory[OVERHANG][NP + 1],
+		Longword *pL_AvgdR0, Longword pL_AvgdCorrSeq[]);
+
+Shortword swComfortNoise(struct gsmhr_encoder_state *st, Shortword swVadFlag,
+			 Longword L_UnqntzdR0, Longword *pL_UnqntzdCorr);
+
+void updateCNHist(struct gsmhr_encoder_state *st, Longword L_UnqntzdR0,
+		  Longword *pL_UnqntzdCorr, Longword pL_R0Hist[],
+		  Longword ppL_CorrHist[OVERHANG][NP + 1]);
+
+void   lpcCorrQntz(Longword pL_CorrelSeq[],
+                          Shortword pswFinalRc[],
+                          int piVQCodewds[]);
+
+#endif