xref: /btstack/src/classic/btstack_sbc_plc.c (revision e7a41128192613a900ab63c6f220c3ae809feb0f)
14e074f72SMilanka Ringwald /*
24e074f72SMilanka Ringwald  * Copyright (C) 2016 BlueKitchen GmbH
34e074f72SMilanka Ringwald  *
44e074f72SMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
54e074f72SMilanka Ringwald  * modification, are permitted provided that the following conditions
64e074f72SMilanka Ringwald  * are met:
74e074f72SMilanka Ringwald  *
84e074f72SMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
94e074f72SMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
104e074f72SMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
114e074f72SMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
124e074f72SMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
134e074f72SMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
144e074f72SMilanka Ringwald  *    contributors may be used to endorse or promote products derived
154e074f72SMilanka Ringwald  *    from this software without specific prior written permission.
164e074f72SMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
174e074f72SMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
184e074f72SMilanka Ringwald  *    monetary gain.
194e074f72SMilanka Ringwald  *
204e074f72SMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
214e074f72SMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
224e074f72SMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
234e074f72SMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
244e074f72SMilanka Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
254e074f72SMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
264e074f72SMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
274e074f72SMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
284e074f72SMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
294e074f72SMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
304e074f72SMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
314e074f72SMilanka Ringwald  * SUCH DAMAGE.
324e074f72SMilanka Ringwald  *
334e074f72SMilanka Ringwald  * Please inquire about commercial licensing options at
344e074f72SMilanka Ringwald  * [email protected]
354e074f72SMilanka Ringwald  *
364e074f72SMilanka Ringwald  */
374e074f72SMilanka Ringwald 
384e074f72SMilanka Ringwald /*
39*e7a41128SMilanka Ringwald  * btstack_sbc_plc.c
404e074f72SMilanka Ringwald  *
414e074f72SMilanka Ringwald  */
424e074f72SMilanka Ringwald 
434e074f72SMilanka Ringwald #include <stdint.h>
444e074f72SMilanka Ringwald #include <stdio.h>
454e074f72SMilanka Ringwald #include <stdlib.h>
464e074f72SMilanka Ringwald #include <string.h>
474e074f72SMilanka Ringwald #include <fcntl.h>
484e074f72SMilanka Ringwald #include <unistd.h>
494e074f72SMilanka Ringwald #include <math.h>
504e074f72SMilanka Ringwald 
514e074f72SMilanka Ringwald #include "btstack_sbc_plc.h"
524e074f72SMilanka Ringwald 
534e074f72SMilanka Ringwald static uint8_t indices0[] = { 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d,
544e074f72SMilanka Ringwald 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d,
554e074f72SMilanka Ringwald 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d,
564e074f72SMilanka Ringwald 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d,
574e074f72SMilanka Ringwald 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c};
584e074f72SMilanka Ringwald 
594e074f72SMilanka Ringwald /* Raised COSine table for OLA */
604e074f72SMilanka Ringwald static float rcos[OLAL] = {
614e074f72SMilanka Ringwald     0.99148655f,0.96623611f,0.92510857f,0.86950446f,
624e074f72SMilanka Ringwald     0.80131732f,0.72286918f,0.63683150f,0.54613418f,
634e074f72SMilanka Ringwald     0.45386582f,0.36316850f,0.27713082f,0.19868268f,
644e074f72SMilanka Ringwald     0.13049554f,0.07489143f,0.03376389f,0.00851345f};
654e074f72SMilanka Ringwald 
664e074f72SMilanka Ringwald static float CrossCorrelation(int16_t *x, int16_t *y);
674e074f72SMilanka Ringwald static int PatternMatch(int16_t *y);
684e074f72SMilanka Ringwald static float AmplitudeMatch(int16_t *y, int16_t bestmatch);
694e074f72SMilanka Ringwald 
70*e7a41128SMilanka Ringwald uint8_t * btstack_sbc_plc_zero_signal_frame(void){
714e074f72SMilanka Ringwald     return (uint8_t *)&indices0;
724e074f72SMilanka Ringwald }
734e074f72SMilanka Ringwald 
74*e7a41128SMilanka Ringwald void btstack_sbc_plc_init(btstack_sbc_plc_state_t *plc_state){
754e074f72SMilanka Ringwald     int i;
764e074f72SMilanka Ringwald     plc_state->nbf=0;
774e074f72SMilanka Ringwald     plc_state->bestlag=0;
784e074f72SMilanka Ringwald     for (i=0;i<LHIST+SBCRT;i++){
794e074f72SMilanka Ringwald         plc_state->hist[i] = 0;
804e074f72SMilanka Ringwald     }
814e074f72SMilanka Ringwald 
824e074f72SMilanka Ringwald }
834e074f72SMilanka Ringwald 
84*e7a41128SMilanka Ringwald void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, int16_t *ZIRbuf, int16_t *out){
854e074f72SMilanka Ringwald     int   i;
864e074f72SMilanka Ringwald     float val;
874e074f72SMilanka Ringwald     float sf;
884e074f72SMilanka Ringwald     plc_state->nbf++;
894e074f72SMilanka Ringwald     sf=1.0f;
904e074f72SMilanka Ringwald 
914e074f72SMilanka Ringwald     i=0;
924e074f72SMilanka Ringwald     if (plc_state->nbf==1){
934e074f72SMilanka Ringwald         /* Perform pattern matching to find where to replicate */
944e074f72SMilanka Ringwald         plc_state->bestlag = PatternMatch(plc_state->hist);
954e074f72SMilanka Ringwald         /* the replication begins after the template match */
964e074f72SMilanka Ringwald         plc_state->bestlag += M;
974e074f72SMilanka Ringwald 
984e074f72SMilanka Ringwald         /* Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet */
994e074f72SMilanka Ringwald         sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag);
1004e074f72SMilanka Ringwald         for (i=0;i<OLAL;i++){
1014e074f72SMilanka Ringwald             val = ZIRbuf[i]*rcos[i] + sf*plc_state->hist[plc_state->bestlag+i]*rcos[OLAL-i-1];
1024e074f72SMilanka Ringwald             if (val >  32767.0) val= 32767.0;
1034e074f72SMilanka Ringwald             if (val < -32768.0) val=-32768.0;
1044e074f72SMilanka Ringwald              plc_state->hist[LHIST+i] = (int16_t)val;
1054e074f72SMilanka Ringwald         }
1064e074f72SMilanka Ringwald 
1074e074f72SMilanka Ringwald         for (;i<FS;i++){
1084e074f72SMilanka Ringwald             val = sf*plc_state->hist[plc_state->bestlag+i]; if (val > 32767.0) val= 32767.0;
1094e074f72SMilanka Ringwald             if (val < -32768.0) val=-32768.0; plc_state->hist[LHIST+i] = (int16_t)val;
1104e074f72SMilanka Ringwald         }
1114e074f72SMilanka Ringwald 
1124e074f72SMilanka Ringwald         for (;i<FS+OLAL;i++){
1134e074f72SMilanka Ringwald             val = sf*plc_state->hist[plc_state->bestlag+i]*rcos[i-FS]+plc_state->hist[plc_state->bestlag+i]*rcos[OLAL-1-i+FS];
1144e074f72SMilanka Ringwald             if (val >  32767.0) val= 32767.0;
1154e074f72SMilanka Ringwald             if (val < -32768.0) val=-32768.0;
1164e074f72SMilanka Ringwald             plc_state->hist[LHIST+i] = (int16_t)val;
1174e074f72SMilanka Ringwald         }
1184e074f72SMilanka Ringwald 
1194e074f72SMilanka Ringwald         for (;i<FS+SBCRT+OLAL;i++)
1204e074f72SMilanka Ringwald             plc_state->hist[LHIST+i] = plc_state->hist[plc_state->bestlag+i];
1214e074f72SMilanka Ringwald     } else {
1224e074f72SMilanka Ringwald         for (;i<FS;i++)
1234e074f72SMilanka Ringwald             plc_state->hist[LHIST+i] = plc_state->hist[plc_state->bestlag+i];
1244e074f72SMilanka Ringwald         for (;i<FS+SBCRT+OLAL;i++)
1254e074f72SMilanka Ringwald             plc_state->hist[LHIST+i] = plc_state->hist[plc_state->bestlag+i];
1264e074f72SMilanka Ringwald     }
1274e074f72SMilanka Ringwald     for (i=0;i<FS;i++)
1284e074f72SMilanka Ringwald         out[i] = plc_state->hist[LHIST+i];
1294e074f72SMilanka Ringwald 
1304e074f72SMilanka Ringwald    /* shift the history buffer */
1314e074f72SMilanka Ringwald    for (i=0;i<LHIST+SBCRT+OLAL;i++)
1324e074f72SMilanka Ringwald         plc_state->hist[i] = plc_state->hist[i+FS];
1334e074f72SMilanka Ringwald 
1344e074f72SMilanka Ringwald }
1354e074f72SMilanka Ringwald 
136*e7a41128SMilanka Ringwald void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, int16_t *in, int16_t *out){
1374e074f72SMilanka Ringwald     int i;
1384e074f72SMilanka Ringwald     i=0;
1394e074f72SMilanka Ringwald     if (plc_state->nbf>0){
1404e074f72SMilanka Ringwald         for (i=0;i<SBCRT;i++)
1414e074f72SMilanka Ringwald             out[i] = plc_state->hist[LHIST+i];
1424e074f72SMilanka Ringwald         for (;i<SBCRT+OLAL;i++)
1434e074f72SMilanka Ringwald             out[i] = (int16_t)(plc_state->hist[LHIST+i]*rcos[i-SBCRT] + in[i]*rcos[OLAL-1-i+SBCRT]);
1444e074f72SMilanka Ringwald     }
1454e074f72SMilanka Ringwald     for (;i<FS;i++)
1464e074f72SMilanka Ringwald         out[i] = in[i];
1474e074f72SMilanka Ringwald     /*Copy the output to the history buffer */
1484e074f72SMilanka Ringwald     for (i=0;i<FS;i++)
1494e074f72SMilanka Ringwald         plc_state->hist[LHIST+i] = out[i];
1504e074f72SMilanka Ringwald     /* shift the history buffer */
1514e074f72SMilanka Ringwald     for (i=0;i<LHIST;i++)
1524e074f72SMilanka Ringwald         plc_state->hist[i] = plc_state->hist[i+FS];
1534e074f72SMilanka Ringwald     plc_state->nbf=0;
1544e074f72SMilanka Ringwald }
1554e074f72SMilanka Ringwald 
1564e074f72SMilanka Ringwald 
1574e074f72SMilanka Ringwald float CrossCorrelation(int16_t *x, int16_t *y){
1584e074f72SMilanka Ringwald     int   m;
1594e074f72SMilanka Ringwald     float num;
1604e074f72SMilanka Ringwald     float den;
1614e074f72SMilanka Ringwald     float Cn;
1624e074f72SMilanka Ringwald     float x2, y2;
1634e074f72SMilanka Ringwald     num=0;
1644e074f72SMilanka Ringwald     den=0;
1654e074f72SMilanka Ringwald     x2=0.0;
1664e074f72SMilanka Ringwald     y2=0.0;
1674e074f72SMilanka Ringwald     for (m=0;m<M;m++){
1684e074f72SMilanka Ringwald         num+=((float)x[m])*y[m];
1694e074f72SMilanka Ringwald         x2+=((float)x[m])*x[m];
1704e074f72SMilanka Ringwald         y2+=((float)y[m])*y[m];
1714e074f72SMilanka Ringwald     }
1724e074f72SMilanka Ringwald     den = (float)sqrt(x2*y2);
1734e074f72SMilanka Ringwald     Cn = num/den;
1744e074f72SMilanka Ringwald     return(Cn);
1754e074f72SMilanka Ringwald }
1764e074f72SMilanka Ringwald 
1774e074f72SMilanka Ringwald int PatternMatch(int16_t *y){
1784e074f72SMilanka Ringwald     int   n;
1794e074f72SMilanka Ringwald     float maxCn;
1804e074f72SMilanka Ringwald     float Cn;
1814e074f72SMilanka Ringwald     int   bestmatch;
1824e074f72SMilanka Ringwald     maxCn=-999999.0;  /* large negative number */
1834e074f72SMilanka Ringwald     bestmatch=0;
1844e074f72SMilanka Ringwald     for (n=0;n<N;n++){
1854e074f72SMilanka Ringwald         Cn = CrossCorrelation(&y[LHIST-M] /* x */, &y[n]);
1864e074f72SMilanka Ringwald         if (Cn>maxCn){
1874e074f72SMilanka Ringwald             bestmatch=n;
1884e074f72SMilanka Ringwald             maxCn = Cn;
1894e074f72SMilanka Ringwald         }
1904e074f72SMilanka Ringwald     }
1914e074f72SMilanka Ringwald     return(bestmatch);
1924e074f72SMilanka Ringwald }
1934e074f72SMilanka Ringwald 
1944e074f72SMilanka Ringwald 
1954e074f72SMilanka Ringwald float AmplitudeMatch(int16_t *y, int16_t bestmatch) {
1964e074f72SMilanka Ringwald     int   i;
1974e074f72SMilanka Ringwald     float sumx;
1984e074f72SMilanka Ringwald     float sumy;
1994e074f72SMilanka Ringwald     float sf;
2004e074f72SMilanka Ringwald     sumx = 0.0;
2014e074f72SMilanka Ringwald     sumy = 0.000001f;
2024e074f72SMilanka Ringwald     for (i=0;i<FS;i++){
2034e074f72SMilanka Ringwald         sumx += abs(y[LHIST-FS+i]);
2044e074f72SMilanka Ringwald         sumy += abs(y[bestmatch+i]);
2054e074f72SMilanka Ringwald     }
2064e074f72SMilanka Ringwald     sf = sumx/sumy;
2074e074f72SMilanka Ringwald     /* This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts */
2084e074f72SMilanka Ringwald     if (sf<0.75f) sf=0.75f;
2094e074f72SMilanka Ringwald     if (sf>1.2f) sf=1.2f;
2104e074f72SMilanka Ringwald     return(sf);
2114e074f72SMilanka Ringwald }