LCOV - code coverage report
Current view: top level - lib_lc3plus - plc_phecu_peak_locator_fx.c (source / functions) Hit Total Coverage
Test: Coverage on main -- dec/rend @ 633e3f2e309758d10805ef21e0436356fe719b7a Lines: 0 141 0.0 %
Date: 2025-08-23 01:22:27 Functions: 0 1 0.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             : *                        ETSI TS 103 634 V1.5.1                               *
       3             : *              Low Complexity Communication Codec Plus (LC3plus)              *
       4             : *                                                                             *
       5             : * Copyright licence is solely granted through ETSI Intellectual Property      *
       6             : * Rights Policy, 3rd April 2019. No patent licence is granted by implication, *
       7             : * estoppel or otherwise.                                                      *
       8             : ******************************************************************************/
       9             : 
      10             : #include "defines.h"
      11             : #include "functions.h"
      12             : 
      13             : #define RES_fx    1  /*  fixed point resolution  */
      14             : 
      15             : /*-----------------------------------------------------------------------------
      16             :  * peak_locator_fx()
      17             :  *----------------------------------------------------------------------------*/
      18           0 : void plc_phEcu_peak_locator_fx(const Word16 *inp, /* i: vector with values >=0   ,Qx      */
      19             :    const Word16  inp_len,              /* i: length of inp                                 */
      20             :    Word16 *      int_plocs,            /* o:  array of filtered integer plocs           Q0 */
      21             :    Word16 *      n_fsc,                /* o:  total_ number of filtered located highs   Q0 */
      22             :    const Word16  sens,                 /* i  sensitivity,   Qx */
      23             :    const Word16  inp_high,             /* i  global high ,  Qx */
      24             :    const Word16  inp_low,              /* i:  global low,  Qx */
      25             :    Word16 maxLprot_Red,                /* i:  optional size for wc memory alloc of scratch buffer  */
      26             :    Word8 *scratchBuffer                /* i: : scratch buffer      2*  3*(1+1+(maxLprot_Red/2)+1) */
      27             : )
      28             : {
      29             :    Counter       j, k, n, idx_high, idx_low;
      30             :    Word16        inp_len_minus1 ;
      31             :    Word16        pairs_start, pairs_end;
      32             :    Word16        *p_tmp;
      33             :    Word16        prev_delta, curr_delta;
      34             :    Word16        delta_predc, delta_fin;
      35             :    Word16        add_dc_flag, add_fin_flag;
      36             :    Word16        low_val_cand_pairs,  val_range;
      37             :    Word16        num_pairs, n_tail_values;
      38             :    Word16        cand_phase_start, cand_idx, prev_low_plus_sens, tmp;
      39             : 
      40             :    Word16        cand_high, prev_low;
      41             :    Word16      *sc_idx; /* 1+ 128/2+1, or 1+ 256/2+1  ...  1+ 640/2+1  or 1+ 768/2+1*/
      42             :    Word16      *cand_pairs_buf ; /*  actually  lowVal + [DC ] + (368/2)pairs + [FS/2]   */
      43             :    Word16      *cand_pairs; /*  actually  [DC ] + pairs + [FS/2]   */
      44             :    Word16      * fsc_idx; /*  list  of high  locations  in   sc__idx  1+368/2+1 */
      45             : 
      46             : 
      47             : 
      48             : 
      49             : #ifdef DYNMEM_COUNT
      50             :    Dyn_Mem_In("peak_locator_fx", sizeof(struct {
      51             :       Counter       j, k, n, idx_high, idx_low;
      52             :       Word16        inp_len_minus1;
      53             :       Word16        pairs_start, pairs_end;
      54             :       Word16        *p_tmp;
      55             :       Word16        prev_delta, curr_delta;
      56             :       Word16        delta_predc, delta_fin;
      57             :       Word16        add_dc_flag, add_fin_flag;
      58             :       Word16        low_val_cand_pairs, val_range;
      59             :       Word16        num_pairs, n_tail_values;
      60             :       Word16        cand_phase_start, cand_idx, prev_low_plus_sens, tmp;
      61             :       Word16        cand_high, prev_low;
      62             :       Word16      *sc_idx;  
      63             :       Word16      *cand_pairs_buf;  
      64             :       Word16      *cand_pairs;      
      65             :       Word16      *fsc_idx;       
      66             :    }));
      67             : #endif
      68             : #ifdef WMOPS
      69             :    push_wmops("PhECU::peak_locator_fx(1st)");
      70             : #endif
      71           0 :    sc_idx          = (Word16 *)scratchAlign(scratchBuffer, 0);                      /* ByteSize = 2 * (1+ inp_len+1) */
      72           0 :    cand_pairs_buf  = (Word16 *)scratchAlign(sc_idx, sizeof(*sc_idx) * (1+inp_len+1)); /* ByteSize = 2 * (1+ 1+ inp_len+1   ) */
      73           0 :    fsc_idx         = (Word16 *)scratchAlign(cand_pairs_buf , sizeof(*cand_pairs_buf) * (1+ 1+ inp_len+1));  /* ByteSize = 2 * ( 1+ inp_len + 1) */
      74           0 :    ASSERT((4 * maxLprot_Red) >= 3 * (1 + 1 + inp_len + 1)); /* basic buffer check */
      75             :    UNUSED(maxLprot_Red);
      76             : 
      77           0 :    inp_len_minus1 = sub(inp_len, 1);  /* size of delta=derivative array ,and last index in inp */
      78             :     
      79           0 :    cand_pairs  = &cand_pairs_buf[1];  /* ptr init , make space for storing a lowest  amplitude value in location  -1    */
      80           0 :    pairs_start = 1;       move16();   /* adjusted to zero or 1 or 2 when/if,  DC is injected  as sc_idx[0], or initial plateau skipped */
      81             :   
      82           0 :    p_tmp = &(sc_idx[pairs_start]); /* ptr init */
      83             :   
      84             : 
      85             :    /*  xor high/low pairs of delta_inp and save sign changes */
      86           0 :    prev_delta = sub(inp[1], inp[0]);  /*  precompute very first delta */
      87             : 
      88           0 :    FOR(n = 1;  n < inp_len_minus1; n++)
      89             :    {   /* sign change analysis */
      90           0 :       curr_delta = sub(inp[n + 1], inp[n]);    /*  n+1 ,n ,   are loop ptrs   */
      91           0 :       if (s_xor(prev_delta, curr_delta) < 0)   /* a "0" delta  treated as a  positive sign */
      92             :       {
      93           0 :          *p_tmp++ = n;   move16();            /* store sign change bin locations , location n in the inp[] signal */
      94             :       }
      95           0 :       prev_delta = curr_delta; move16();
      96             :    }
      97             : 
      98           0 :    L_sub(0, 0); /* account for length calculaton */
      99           0 :    k = (Word16)(p_tmp - &(sc_idx[pairs_start]));  
     100             : 
     101             :     
     102             :    /* copy sign change location values to a pairs array */
     103             :    /* leave one initial sc_idx location open for a potential initial DC value */
     104             : 
     105           0 :    ASSERT(pairs_start >= 0 && ((k - 1) + pairs_start) < (inp_len +2));
     106           0 :    FOR(j = 0; j < k; j++)
     107             :    {
     108           0 :       cand_pairs[j + pairs_start] = inp[sc_idx[j + pairs_start]]; move16();  move16();  /*  the indirect  should be  calculated */
     109             :    }
     110             : 
     111             : 
     112             :    /* filter away a potential  single initial/trailing  plateau
     113             :      to enable correct analysis for adding DC or  fs/2 bins */
     114             : 
     115           0 :    logic16();
     116           0 :    IF((sub(k, 2) >= 0) &&
     117             :       (sub(cand_pairs[pairs_start], cand_pairs[pairs_start + 1]) == 0))
     118             :    {
     119           0 :       pairs_start = add(pairs_start, 1);
     120           0 :       k = sub(k, 1);
     121             :    }
     122             : 
     123             :    /* filter away potential single trailing plateu */
     124           0 :    pairs_end = sub(add(pairs_start,k), 1);  /* point  to last established  sign change element  */
     125           0 :    logic16();
     126           0 :    if ((sub(k, 2) >= 0) &&
     127           0 :       (sub(cand_pairs[sub(pairs_end,1)], cand_pairs[pairs_end]) == 0))
     128             :    {
     129           0 :       k = sub(k, 1);
     130             :    }
     131           0 :    pairs_end = sub(add(pairs_start,k), 1);  /*  recalc  ptr to last element  */
     132             : 
     133             : 
     134             :    /* conditionally add high/lows  on both sides of input (pre_dc or fin) as  candidates  */
     135           0 :    add_dc_flag  = 0; move16();
     136           0 :    add_fin_flag = 0; move16();
     137             : 
     138             : 
     139           0 :    IF(sub(k, 1) == 0) /*  one single sign change found special case */
     140             :    {
     141           0 :       if (sub(inp[0], cand_pairs[pairs_start]) != 0)
     142             :       {
     143           0 :          add_dc_flag = 1;  move16();  /* not plateau    */
     144             :       }
     145             : 
     146           0 :       if (sub(cand_pairs[pairs_end], inp[inp_len_minus1]) != 0)
     147             :       {
     148           0 :          add_fin_flag = 1;   move16();  /* not plateau    */
     149             :       }
     150             :    }
     151             : 
     152           0 :    IF(sub(k, 2) >= 0)
     153             :    {
     154           0 :       delta_predc = sub(cand_pairs[pairs_start + 1], cand_pairs[pairs_start]);
     155           0 :       delta_fin   = sub(cand_pairs[pairs_end], cand_pairs[pairs_end - 1]);
     156             : 
     157             :       /* plateaus are allowed to be detected by xor sign change,
     158             :          but still not allowed at the start nor  at the end */
     159             : 
     160           0 :       add_dc_flag = 1;   move16();
     161           0 :       if (sub(inp[0], cand_pairs[pairs_start]) == 0)
     162             :       {
     163           0 :          add_dc_flag = 0;   move16();   /* plateau down or , plateus up., --> do not add DC  */
     164             :       }
     165             : 
     166           0 :       logic16();
     167           0 :       if ((sub(inp[0], cand_pairs[pairs_start]) < 0) && (delta_predc > 0))
     168             :       {
     169           0 :          add_dc_flag = -1; move16();   /*UP - up    ... replace */
     170             :       }
     171           0 :       logic16();
     172           0 :       if ((sub(inp[0], cand_pairs[pairs_start]) > 0) && (delta_predc < 0))
     173             :       {
     174           0 :          add_dc_flag = -1;   move16();   /* DOWN - down ... % replace */
     175             :       }
     176             : 
     177           0 :       add_fin_flag = 1;   move16();     
     178           0 :       if (sub(cand_pairs[pairs_end], inp[inp_len_minus1]) == 0)
     179             :       {
     180           0 :          add_fin_flag = 0;   move16();   /* up - plateau ... */
     181             :       }
     182           0 :       logic16();
     183           0 :       if ((delta_fin > 0) && (sub(cand_pairs[pairs_end], inp[inp_len_minus1]) < 0))
     184             :       {
     185           0 :          add_fin_flag = -1;   move16();   /* up - UP ...    % replace , hard to hit  */
     186             :       }
     187           0 :       logic16();
     188           0 :       if ((delta_fin < 0) && (sub(cand_pairs[pairs_end], inp[inp_len_minus1]) > 0))
     189             :       {
     190           0 :          add_fin_flag = -1; move16();   /*down - DOWN ... % replace */
     191             :       }
     192             : 
     193             :    }
     194             : 
     195           0 :    IF(add_dc_flag > 0)
     196             :    {  /* add DC */
     197           0 :       pairs_start = sub(pairs_start, 1);
     198           0 :       cand_pairs[pairs_start] = inp[0]; move16();
     199           0 :       sc_idx[pairs_start] = 0; move16();
     200           0 :       ASSERT(pairs_start >= 0 && pairs_start <= 2);
     201           0 :       k = add(k, 1);
     202             :    }
     203           0 :    IF(add_dc_flag < 0)
     204             :    { /*   -1 -->  replace with DC*/
     205           0 :       cand_pairs[pairs_start] = inp[0]; move16();
     206           0 :       sc_idx[pairs_start] = 0; move16();
     207           0 :       ASSERT(pairs_start >=0 && pairs_start <= 2);
     208             :    }
     209             : 
     210           0 :    IF(add_fin_flag > 0)
     211             :    {  /* add FS/2  */
     212           0 :       pairs_end = add(pairs_end, 1);
     213           0 :       cand_pairs[pairs_end] = inp[inp_len_minus1]; move16();
     214           0 :       sc_idx[pairs_end] = inp_len_minus1; move16();
     215           0 :       k = add(k, 1);
     216             :    }
     217           0 :    IF(add_fin_flag < 0)
     218             :    {  /*    -1, replace tail with FS/2*/
     219           0 :       cand_pairs[pairs_end] = inp[inp_len_minus1]; move16();
     220           0 :       sc_idx[pairs_end] = inp_len_minus1; move16();
     221             :    }
     222             :    /* preliminary cand_pairs now only have  highs , lows , no initial/trailing plateaus */
     223             : 
     224             : 
     225             :    /*  we allow the  DC/FsBy2 lows to be used as the candidatelLow  */
     226           0 :    low_val_cand_pairs = inp_low;   move16();
     227           0 :    val_range          = sub( inp_high, low_val_cand_pairs); /* used to determine if search is useful at all */
     228             : 
     229           0 :    logic16();
     230           0 :    if ((sub(val_range, RES_fx) < 0) ||
     231           0 :       (sub( inp_high, sens) < 0))
     232             :    {
     233           0 :       k = 0;   move16();
     234             :    }
     235             : 
     236           0 :    logic16();
     237           0 :    if ((k == 0) && (sub(val_range, sens) >= 0))
     238             :    {
     239           0 :       k = 1;  move16();
     240             :    }
     241             : 
     242             : 
     243           0 :    IF(sub(k, 2) > 0)
     244             :    {
     245             :       /*  low, high, low, ... or 
     246             :           high, low, high, ...*/
     247             : 
     248           0 :       cand_phase_start = pairs_start;  move16();     /*assume first candidate   is a high */
     249           0 :       if (sub(cand_pairs[pairs_start], cand_pairs[pairs_start + 1]) < 0)
     250             :       {
     251           0 :          cand_phase_start = add(pairs_start, 1);     /* first is a low, --> skip to next higher cand  */
     252             :       }
     253             : 
     254             :       /*  high, low, high, ... */
     255           0 :       tmp = k;   move16();
     256           0 :       if (sub(cand_phase_start, pairs_start) != 0)
     257             :       {
     258           0 :          tmp = sub(tmp, 1);
     259             :       }
     260           0 :       num_pairs     = shr(tmp, 1);
     261           0 :       n_tail_values = sub(tmp, shl(num_pairs, 1));
     262             : 
     263             :       /* filter  preliminary  sign changes into sensitivity filtered sign changes */
     264             : 
     265           0 :       *n_fsc    = 0;                       move16(); /*   counter of  filtered fsc_idx */
     266           0 :       cand_high = low_val_cand_pairs;      move16();
     267           0 :       cand_idx  = -1;                      move16(); /*  sentinel location for no high cand found yet. */
     268           0 :       cand_pairs[-1] = low_val_cand_pairs; move16();
     269             : 
     270           0 :       prev_low           = low_val_cand_pairs;    move16();
     271           0 :       prev_low_plus_sens = add(prev_low, sens);
     272             : 
     273             :       /* filter loop for   high - low sign change pairs */
     274             :       /* idx_high, idx_low are raw pointers into the  cand_pairs and sc_idx arrays */
     275             :       
     276           0 :       FOR( idx_high = cand_phase_start;  idx_high < (cand_phase_start + 2 * num_pairs); idx_high += 2)
     277             :       {
     278           0 :          idx_low  = idx_high+1;  /* loop ptr increase */
     279             : 
     280             :          /* new high candidate  larger than previous candidate  and   */
     281             :          /* sensitivity still larger  than the the previous low */
     282           0 :          tmp = s_max(cand_high, prev_low_plus_sens);
     283           0 :          if(sub(cand_pairs[idx_high], tmp) > 0)
     284             :          {
     285           0 :             cand_idx  = idx_high;              move16();   /*   enable or shift candidate position fwd */
     286             :          }
     287           0 :          cand_high = cand_pairs[cand_idx];  move16();  /* NB, cand_pairs[-1] , has the low_val_cand_pairs value  stored */
     288             :            
     289             :          /* now check the fwd  idx_low  of the current  {high,low} pair  */
     290           0 :          prev_low = s_min(cand_pairs[idx_low], prev_low);
     291             : 
     292           0 :          tmp = sub(cand_high, sens);
     293           0 :          IF(sub(tmp, cand_pairs[idx_low]) > 0)
     294             :          {
     295             :             /*  this low  point is now low enough to fix a previous high candidate */
     296             : 
     297           0 :             fsc_idx[*n_fsc]      = cand_idx;  move16();  /*%  add cand high idx  -> output idx list*/
     298           0 :             *n_fsc               = add(*n_fsc, 1);
     299             : 
     300           0 :             prev_low = cand_pairs[idx_low];   move16();  /*  use this value  as new low estimate */
     301           0 :             cand_idx = -1;                    move16(); /*   no  candidate until next pair or tail  bin, and pt to lowVal */
     302           0 :             cand_high = low_val_cand_pairs; move16();   /*  enable next candidate to be selected immediately  */
     303             :          }
     304           0 :          prev_low_plus_sens = add(prev_low, sens);
     305             :       } /* { high, low} FOR loop */
     306             : 
     307           0 :       logic16();
     308           0 :       IF((n_tail_values == 0) && (cand_idx >= 0))
     309             :       {
     310             :          /*  no tail  low or high value  to analyze
     311             :              still may need to lock a non-locked but qualified candidate */
     312           0 :          fsc_idx[*n_fsc]      = cand_idx;  move16();
     313           0 :          *n_fsc               = add(*n_fsc, 1);
     314             :       }
     315             : 
     316             : 
     317             :       /* cand_pairs vector may have a last orphan value */
     318           0 :       IF(n_tail_values > 0)
     319             :       {
     320             :         /*   cand_pairs vector may have a last orphan tail value */
     321             :         /*
     322             :          logic boils down to   if (nTailValues > 0) && (cand_pairs(n_end) > tmp)
     323             :           there is a last  one  trailing high to process
     324             : 
     325             :          a) the last high, may be a new high Peak if we have not yet
     326             :             locked  the current candidate
     327             :          b) if we have locked the last candidate, the last high may also be
     328             :             a highpeak if it is high enough from the(newly set previous) valley floor.
     329             : 
     330             :            tmp=a||b
     331             :         */
     332             : 
     333           0 :          tmp = s_max(cand_high, prev_low_plus_sens);
     334           0 :          tmp = sub(cand_pairs[pairs_end], tmp);
     335           0 :          IF(tmp > 0)
     336             :          {
     337           0 :             fsc_idx[*n_fsc]      = pairs_end;            move16();
     338           0 :             *n_fsc               = add(*n_fsc, 1);
     339             :          }
     340             :          ELSE
     341             :          {
     342           0 :             IF(cand_idx >= 0)
     343             :             { /* we have a previously established high candidate */
     344           0 :                fsc_idx[*n_fsc] = cand_idx;   move16();
     345           0 :                *n_fsc          = add(*n_fsc, 1);
     346             :             }
     347             : 
     348             :          }
     349             :       }
     350             :       /* move high locations info from  fsc_idx , to output  */
     351           0 :       FOR(j = 0; j < *n_fsc; j++)
     352             :       { 
     353           0 :          ASSERT(fsc_idx[j] >= 0 && fsc_idx[j] < (inp_len+2));
     354           0 :          int_plocs[j] = sc_idx[fsc_idx[j]]; move16();  move16();  /*  the indirect moves  are  calculated */
     355             :       }
     356             : 
     357             :    } /* end of  pairs + [tail] section filtering  */
     358             :    ELSE
     359             :    {
     360             :       /* constant/single  rise or constant decay or very low overall values,   cases */
     361           0 :       *n_fsc = 0;  move16();
     362             :    
     363           0 :       logic16();
     364           0 :       tmp = sub(inp_high, sens);
     365           0 :       IF((k != 0) && (sub(tmp, low_val_cand_pairs) > 0))
     366             :       {
     367             :          /*      low,high */
     368             :          /*      high,low */
     369           0 :          tmp          = plc_phEcu_find_ind_fx(inp, inp_len, inp_high);  move16();
     370           0 :          int_plocs[0] = tmp;   move16(); /*  simply  locate the   high peak*/
     371           0 :          *n_fsc       = 1;     move16();
     372           0 :          if (tmp < 0)
     373             :          {  /*safety in case max value index was not found */
     374           0 :             *n_fsc = 0; move16();
     375             :          }
     376             :       }
     377             :    }
     378             : #ifdef DYNMEM_COUNT
     379             :    Dyn_Mem_Out();
     380             : #endif
     381             : #ifdef WMOPS
     382             :    pop_wmops();
     383             : #endif
     384           0 : }
     385             : 

Generated by: LCOV version 1.14