xref: /btstack/src/classic/btstack_sbc_plc.c (revision c5e169ec5e0270cb2b3d9a91a3882ece1db0119f)
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 /*
39e7a41128SMilanka 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 */
607e6b1e83SMilanka Ringwald static float rcos[SBC_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 
66*c5e169ecSMilanka Ringwald static float CrossCorrelation(int16_t *x, int16_t *y){
67*c5e169ecSMilanka Ringwald     float num = 0;
68*c5e169ecSMilanka Ringwald     float den = 0;
69*c5e169ecSMilanka Ringwald     float x2 = 0;
70*c5e169ecSMilanka Ringwald     float y2 = 0;
71*c5e169ecSMilanka Ringwald     int   m;
72*c5e169ecSMilanka Ringwald     for (m=0;m<SBC_M;m++){
73*c5e169ecSMilanka Ringwald         num+=((float)x[m])*y[m];
74*c5e169ecSMilanka Ringwald         x2+=((float)x[m])*x[m];
75*c5e169ecSMilanka Ringwald         y2+=((float)y[m])*y[m];
76*c5e169ecSMilanka Ringwald     }
77*c5e169ecSMilanka Ringwald     den = (float)sqrt(x2*y2);
78*c5e169ecSMilanka Ringwald     return num/den;
79*c5e169ecSMilanka Ringwald }
80*c5e169ecSMilanka Ringwald 
81*c5e169ecSMilanka Ringwald static int PatternMatch(int16_t *y){
82*c5e169ecSMilanka Ringwald     float maxCn = -999999.0;  /* large negative number */
83*c5e169ecSMilanka Ringwald     int   bestmatch = 0;
84*c5e169ecSMilanka Ringwald     float Cn;
85*c5e169ecSMilanka Ringwald     int   n;
86*c5e169ecSMilanka Ringwald     for (n=0;n<SBC_N;n++){
87*c5e169ecSMilanka Ringwald         Cn = CrossCorrelation(&y[SBC_LHIST-SBC_M] /* x */, &y[n]);
88*c5e169ecSMilanka Ringwald         if (Cn>maxCn){
89*c5e169ecSMilanka Ringwald             bestmatch=n;
90*c5e169ecSMilanka Ringwald             maxCn = Cn;
91*c5e169ecSMilanka Ringwald         }
92*c5e169ecSMilanka Ringwald     }
93*c5e169ecSMilanka Ringwald     return bestmatch;
94*c5e169ecSMilanka Ringwald }
95*c5e169ecSMilanka Ringwald 
96*c5e169ecSMilanka Ringwald 
97*c5e169ecSMilanka Ringwald static float AmplitudeMatch(int16_t *y, int16_t bestmatch) {
98*c5e169ecSMilanka Ringwald     int   i;
99*c5e169ecSMilanka Ringwald     float sumx = 0;
100*c5e169ecSMilanka Ringwald     float sumy = 0.000001f;
101*c5e169ecSMilanka Ringwald     float sf;
102*c5e169ecSMilanka Ringwald 
103*c5e169ecSMilanka Ringwald     for (i=0;i<SBC_FS;i++){
104*c5e169ecSMilanka Ringwald         sumx += abs(y[SBC_LHIST-SBC_FS+i]);
105*c5e169ecSMilanka Ringwald         sumy += abs(y[bestmatch+i]);
106*c5e169ecSMilanka Ringwald     }
107*c5e169ecSMilanka Ringwald     sf = sumx/sumy;
108*c5e169ecSMilanka Ringwald     /* This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts */
109*c5e169ecSMilanka Ringwald     if (sf<0.75f) sf=0.75f;
110*c5e169ecSMilanka Ringwald     if (sf>1.2f) sf=1.2f;
111*c5e169ecSMilanka Ringwald     return sf;
112*c5e169ecSMilanka Ringwald }
1134e074f72SMilanka Ringwald 
1147e6b1e83SMilanka Ringwald static int16_t crop_to_int16(float val){
1157e6b1e83SMilanka Ringwald     float croped_val = 0;
1167e6b1e83SMilanka Ringwald     if (val > 32767.0)  croped_val= 32767.0;
1177e6b1e83SMilanka Ringwald     if (val < -32768.0) croped_val=-32768.0;
1187e6b1e83SMilanka Ringwald     return (int16_t) croped_val;
1197e6b1e83SMilanka Ringwald }
1207e6b1e83SMilanka Ringwald 
121e7a41128SMilanka Ringwald uint8_t * btstack_sbc_plc_zero_signal_frame(void){
1224e074f72SMilanka Ringwald     return (uint8_t *)&indices0;
1234e074f72SMilanka Ringwald }
1244e074f72SMilanka Ringwald 
125e7a41128SMilanka Ringwald void btstack_sbc_plc_init(btstack_sbc_plc_state_t *plc_state){
1264e074f72SMilanka Ringwald     plc_state->nbf=0;
1274e074f72SMilanka Ringwald     plc_state->bestlag=0;
128b3f76298SMilanka Ringwald     memset(plc_state->hist,0,sizeof(plc_state->hist));
1294e074f72SMilanka Ringwald }
1304e074f72SMilanka Ringwald 
131e7a41128SMilanka Ringwald void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, int16_t *ZIRbuf, int16_t *out){
1324e074f72SMilanka Ringwald     float val;
1337e6b1e83SMilanka Ringwald     int   i = 0;
1347e6b1e83SMilanka Ringwald     float sf = 1;
1354e074f72SMilanka Ringwald 
1367e6b1e83SMilanka Ringwald     plc_state->nbf++;
1377e6b1e83SMilanka Ringwald 
1384e074f72SMilanka Ringwald     if (plc_state->nbf==1){
1394e074f72SMilanka Ringwald         /* Perform pattern matching to find where to replicate */
1404e074f72SMilanka Ringwald         plc_state->bestlag = PatternMatch(plc_state->hist);
1414e074f72SMilanka Ringwald         /* the replication begins after the template match */
1427e6b1e83SMilanka Ringwald         plc_state->bestlag += SBC_M;
1434e074f72SMilanka Ringwald 
1444e074f72SMilanka Ringwald         /* Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet */
1454e074f72SMilanka Ringwald         sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag);
1467e6b1e83SMilanka Ringwald         for (i=0;i<SBC_OLAL;i++){
1477e6b1e83SMilanka Ringwald             float left  = ZIRbuf[i];
1487e6b1e83SMilanka Ringwald             float right = sf*plc_state->hist[plc_state->bestlag+i];
1497e6b1e83SMilanka Ringwald             val = left*rcos[i] + right*rcos[SBC_OLAL-i-1];
1507e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
1514e074f72SMilanka Ringwald         }
1524e074f72SMilanka Ringwald 
1537e6b1e83SMilanka Ringwald         for (;i<SBC_FS;i++){
1547e6b1e83SMilanka Ringwald             val = sf*plc_state->hist[plc_state->bestlag+i];
1557e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
1564e074f72SMilanka Ringwald         }
1574e074f72SMilanka Ringwald 
1587e6b1e83SMilanka Ringwald         for (;i<SBC_FS+SBC_OLAL;i++){
1597e6b1e83SMilanka Ringwald             float left  = sf*plc_state->hist[plc_state->bestlag+i];
1607e6b1e83SMilanka Ringwald             float right = plc_state->hist[plc_state->bestlag+i];
1617e6b1e83SMilanka Ringwald             val = left*rcos[i-SBC_FS]+right*rcos[SBC_OLAL-1-i+SBC_FS];
1627e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
1634e074f72SMilanka Ringwald         }
1644e074f72SMilanka Ringwald 
1657e6b1e83SMilanka Ringwald         for (;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
1667e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
1677e6b1e83SMilanka Ringwald         }
1687e6b1e83SMilanka Ringwald 
1694e074f72SMilanka Ringwald     } else {
170*c5e169ecSMilanka Ringwald         for (i=0;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
1717e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
1724e074f72SMilanka Ringwald         }
173*c5e169ecSMilanka Ringwald     }
1747e6b1e83SMilanka Ringwald     for (i=0;i<SBC_FS;i++){
1757e6b1e83SMilanka Ringwald         out[i] = plc_state->hist[SBC_LHIST+i];
1767e6b1e83SMilanka Ringwald     }
1774e074f72SMilanka Ringwald 
1784e074f72SMilanka Ringwald     /* shift the history buffer */
1797e6b1e83SMilanka Ringwald     for (i=0;i<SBC_LHIST+SBC_RT+SBC_OLAL;i++){
1807e6b1e83SMilanka Ringwald         plc_state->hist[i] = plc_state->hist[i+SBC_FS];
1817e6b1e83SMilanka Ringwald     }
1824e074f72SMilanka Ringwald 
1834e074f72SMilanka Ringwald }
1844e074f72SMilanka Ringwald 
185e7a41128SMilanka Ringwald void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, int16_t *in, int16_t *out){
1867e6b1e83SMilanka Ringwald     float val;
1877e6b1e83SMilanka Ringwald     int i = 0;
1884e074f72SMilanka Ringwald     if (plc_state->nbf>0){
1897e6b1e83SMilanka Ringwald         for (i=0;i<SBC_RT;i++){
1907e6b1e83SMilanka Ringwald             out[i] = plc_state->hist[SBC_LHIST+i];
1914e074f72SMilanka Ringwald         }
1927e6b1e83SMilanka Ringwald 
1937e6b1e83SMilanka Ringwald         for (;i<SBC_RT+SBC_OLAL;i++){
1947e6b1e83SMilanka Ringwald             float left  = plc_state->hist[SBC_LHIST+i];
1957e6b1e83SMilanka Ringwald             float right = in[i];
1967e6b1e83SMilanka Ringwald             val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL-1-i+SBC_RT];
1977e6b1e83SMilanka Ringwald             out[i] = crop_to_int16(val);
1987e6b1e83SMilanka Ringwald         }
1997e6b1e83SMilanka Ringwald     }
2007e6b1e83SMilanka Ringwald     for (;i<SBC_FS;i++){
2014e074f72SMilanka Ringwald         out[i] = in[i];
2027e6b1e83SMilanka Ringwald     }
2037e6b1e83SMilanka Ringwald 
2044e074f72SMilanka Ringwald     /*Copy the output to the history buffer */
2057e6b1e83SMilanka Ringwald     for (i=0;i<SBC_FS;i++){
2067e6b1e83SMilanka Ringwald         plc_state->hist[SBC_LHIST+i] = out[i];
2077e6b1e83SMilanka Ringwald     }
2084e074f72SMilanka Ringwald     /* shift the history buffer */
2097e6b1e83SMilanka Ringwald     for (i=0;i<SBC_LHIST;i++){
2107e6b1e83SMilanka Ringwald         plc_state->hist[i] = plc_state->hist[i+SBC_FS];
2117e6b1e83SMilanka Ringwald     }
2127e6b1e83SMilanka Ringwald 
2134e074f72SMilanka Ringwald     plc_state->nbf=0;
2144e074f72SMilanka Ringwald }
215