xref: /btstack/src/classic/btstack_sbc_plc.c (revision 40e1e61c3d08a44ca507a809db8cca4e716135d7)
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 
504e074f72SMilanka Ringwald #include "btstack_sbc_plc.h"
514e074f72SMilanka Ringwald 
524e074f72SMilanka Ringwald static uint8_t indices0[] = { 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d,
534e074f72SMilanka Ringwald 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 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, 0x6c};
574e074f72SMilanka Ringwald 
584e074f72SMilanka Ringwald /* Raised COSine table for OLA */
597e6b1e83SMilanka Ringwald static float rcos[SBC_OLAL] = {
604e074f72SMilanka Ringwald     0.99148655f,0.96623611f,0.92510857f,0.86950446f,
614e074f72SMilanka Ringwald     0.80131732f,0.72286918f,0.63683150f,0.54613418f,
624e074f72SMilanka Ringwald     0.45386582f,0.36316850f,0.27713082f,0.19868268f,
634e074f72SMilanka Ringwald     0.13049554f,0.07489143f,0.03376389f,0.00851345f};
644e074f72SMilanka Ringwald 
65*40e1e61cSMilanka Ringwald // taken from http://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi
66*40e1e61cSMilanka Ringwald // Algorithm: Babylonian Method + some manipulations on IEEE 32 bit floating point representation
67*40e1e61cSMilanka Ringwald static float sqrt3(const float x){
68*40e1e61cSMilanka Ringwald     union {
69*40e1e61cSMilanka Ringwald         int i;
70*40e1e61cSMilanka Ringwald         float x;
71*40e1e61cSMilanka Ringwald     } u;
72*40e1e61cSMilanka Ringwald     u.x = x;
73*40e1e61cSMilanka Ringwald     u.i = (1<<29) + (u.i >> 1) - (1<<22);
74*40e1e61cSMilanka Ringwald 
75*40e1e61cSMilanka Ringwald     // Two Babylonian Steps (simplified from:)
76*40e1e61cSMilanka Ringwald     // u.x = 0.5f * (u.x + x/u.x);
77*40e1e61cSMilanka Ringwald     // u.x = 0.5f * (u.x + x/u.x);
78*40e1e61cSMilanka Ringwald     u.x =       u.x + x/u.x;
79*40e1e61cSMilanka Ringwald     u.x = 0.25f*u.x + x/u.x;
80*40e1e61cSMilanka Ringwald 
81*40e1e61cSMilanka Ringwald     return u.x;
82*40e1e61cSMilanka Ringwald }
83*40e1e61cSMilanka Ringwald 
84*40e1e61cSMilanka Ringwald static float absolute(float x){
85*40e1e61cSMilanka Ringwald      if (x < 0) x = -x;
86*40e1e61cSMilanka Ringwald      return x;
87*40e1e61cSMilanka Ringwald }
88*40e1e61cSMilanka Ringwald 
89c5e169ecSMilanka Ringwald static float CrossCorrelation(int16_t *x, int16_t *y){
90c5e169ecSMilanka Ringwald     float num = 0;
91c5e169ecSMilanka Ringwald     float den = 0;
92c5e169ecSMilanka Ringwald     float x2 = 0;
93c5e169ecSMilanka Ringwald     float y2 = 0;
94c5e169ecSMilanka Ringwald     int   m;
95c5e169ecSMilanka Ringwald     for (m=0;m<SBC_M;m++){
96c5e169ecSMilanka Ringwald         num+=((float)x[m])*y[m];
97c5e169ecSMilanka Ringwald         x2+=((float)x[m])*x[m];
98c5e169ecSMilanka Ringwald         y2+=((float)y[m])*y[m];
99c5e169ecSMilanka Ringwald     }
100*40e1e61cSMilanka Ringwald     den = (float)sqrt3(x2*y2);
101c5e169ecSMilanka Ringwald     return num/den;
102c5e169ecSMilanka Ringwald }
103c5e169ecSMilanka Ringwald 
104c5e169ecSMilanka Ringwald static int PatternMatch(int16_t *y){
105c5e169ecSMilanka Ringwald     float maxCn = -999999.0;  /* large negative number */
106c5e169ecSMilanka Ringwald     int   bestmatch = 0;
107c5e169ecSMilanka Ringwald     float Cn;
108c5e169ecSMilanka Ringwald     int   n;
109c5e169ecSMilanka Ringwald     for (n=0;n<SBC_N;n++){
110c5e169ecSMilanka Ringwald         Cn = CrossCorrelation(&y[SBC_LHIST-SBC_M] /* x */, &y[n]);
111c5e169ecSMilanka Ringwald         if (Cn>maxCn){
112c5e169ecSMilanka Ringwald             bestmatch=n;
113c5e169ecSMilanka Ringwald             maxCn = Cn;
114c5e169ecSMilanka Ringwald         }
115c5e169ecSMilanka Ringwald     }
116c5e169ecSMilanka Ringwald     return bestmatch;
117c5e169ecSMilanka Ringwald }
118c5e169ecSMilanka Ringwald 
119c5e169ecSMilanka Ringwald 
120c5e169ecSMilanka Ringwald static float AmplitudeMatch(int16_t *y, int16_t bestmatch) {
121c5e169ecSMilanka Ringwald     int   i;
122c5e169ecSMilanka Ringwald     float sumx = 0;
123c5e169ecSMilanka Ringwald     float sumy = 0.000001f;
124c5e169ecSMilanka Ringwald     float sf;
125c5e169ecSMilanka Ringwald 
126c5e169ecSMilanka Ringwald     for (i=0;i<SBC_FS;i++){
127*40e1e61cSMilanka Ringwald         sumx += absolute(y[SBC_LHIST-SBC_FS+i]);
128*40e1e61cSMilanka Ringwald         sumy += absolute(y[bestmatch+i]);
129c5e169ecSMilanka Ringwald     }
130c5e169ecSMilanka Ringwald     sf = sumx/sumy;
131c5e169ecSMilanka Ringwald     /* This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts */
132c5e169ecSMilanka Ringwald     if (sf<0.75f) sf=0.75f;
133c5e169ecSMilanka Ringwald     if (sf>1.2f) sf=1.2f;
134c5e169ecSMilanka Ringwald     return sf;
135c5e169ecSMilanka Ringwald }
1364e074f72SMilanka Ringwald 
1377e6b1e83SMilanka Ringwald static int16_t crop_to_int16(float val){
138250a1e4aSMilanka Ringwald     float croped_val = val;
139250a1e4aSMilanka Ringwald     if (croped_val > 32767.0)  croped_val= 32767.0;
140250a1e4aSMilanka Ringwald     if (croped_val < -32768.0) croped_val=-32768.0;
1417e6b1e83SMilanka Ringwald     return (int16_t) croped_val;
1427e6b1e83SMilanka Ringwald }
1437e6b1e83SMilanka Ringwald 
144e7a41128SMilanka Ringwald uint8_t * btstack_sbc_plc_zero_signal_frame(void){
1454e074f72SMilanka Ringwald     return (uint8_t *)&indices0;
1464e074f72SMilanka Ringwald }
1474e074f72SMilanka Ringwald 
148e7a41128SMilanka Ringwald void btstack_sbc_plc_init(btstack_sbc_plc_state_t *plc_state){
1494e074f72SMilanka Ringwald     plc_state->nbf=0;
1504e074f72SMilanka Ringwald     plc_state->bestlag=0;
151b3f76298SMilanka Ringwald     memset(plc_state->hist,0,sizeof(plc_state->hist));
1524e074f72SMilanka Ringwald }
1534e074f72SMilanka Ringwald 
154e7a41128SMilanka Ringwald void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, int16_t *ZIRbuf, int16_t *out){
1554e074f72SMilanka Ringwald     float val;
1567e6b1e83SMilanka Ringwald     int   i = 0;
1577e6b1e83SMilanka Ringwald     float sf = 1;
1584e074f72SMilanka Ringwald 
1597e6b1e83SMilanka Ringwald     plc_state->nbf++;
1607e6b1e83SMilanka Ringwald 
1614e074f72SMilanka Ringwald     if (plc_state->nbf==1){
1624e074f72SMilanka Ringwald         /* Perform pattern matching to find where to replicate */
1634e074f72SMilanka Ringwald         plc_state->bestlag = PatternMatch(plc_state->hist);
1644e074f72SMilanka Ringwald         /* the replication begins after the template match */
1657e6b1e83SMilanka Ringwald         plc_state->bestlag += SBC_M;
1664e074f72SMilanka Ringwald 
1674e074f72SMilanka Ringwald         /* Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet */
1684e074f72SMilanka Ringwald         sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag);
1697e6b1e83SMilanka Ringwald         for (i=0;i<SBC_OLAL;i++){
1707e6b1e83SMilanka Ringwald             float left  = ZIRbuf[i];
1717e6b1e83SMilanka Ringwald             float right = sf*plc_state->hist[plc_state->bestlag+i];
17262f77245SMilanka Ringwald             val = left*rcos[i] + right*rcos[SBC_OLAL-1-i];
1737e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
1744e074f72SMilanka Ringwald         }
1754e074f72SMilanka Ringwald 
1767e6b1e83SMilanka Ringwald         for (;i<SBC_FS;i++){
1777e6b1e83SMilanka Ringwald             val = sf*plc_state->hist[plc_state->bestlag+i];
1787e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
1794e074f72SMilanka Ringwald         }
1804e074f72SMilanka Ringwald 
1817e6b1e83SMilanka Ringwald         for (;i<SBC_FS+SBC_OLAL;i++){
1827e6b1e83SMilanka Ringwald             float left  = sf*plc_state->hist[plc_state->bestlag+i];
1837e6b1e83SMilanka Ringwald             float right = plc_state->hist[plc_state->bestlag+i];
18462f77245SMilanka Ringwald             val = left*rcos[i-SBC_FS]+right*rcos[SBC_FS+SBC_OLAL-1-i];
1857e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
1864e074f72SMilanka Ringwald         }
1874e074f72SMilanka Ringwald 
1887e6b1e83SMilanka Ringwald         for (;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
1897e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
1907e6b1e83SMilanka Ringwald         }
1917e6b1e83SMilanka Ringwald 
1924e074f72SMilanka Ringwald     } else {
193c5e169ecSMilanka Ringwald         for (i=0;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
1947e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
1954e074f72SMilanka Ringwald         }
196c5e169ecSMilanka Ringwald     }
1977e6b1e83SMilanka Ringwald     for (i=0;i<SBC_FS;i++){
1987e6b1e83SMilanka Ringwald         out[i] = plc_state->hist[SBC_LHIST+i];
1997e6b1e83SMilanka Ringwald     }
2004e074f72SMilanka Ringwald 
2014e074f72SMilanka Ringwald     /* shift the history buffer */
2027e6b1e83SMilanka Ringwald     for (i=0;i<SBC_LHIST+SBC_RT+SBC_OLAL;i++){
2037e6b1e83SMilanka Ringwald         plc_state->hist[i] = plc_state->hist[i+SBC_FS];
2047e6b1e83SMilanka Ringwald     }
2054e074f72SMilanka Ringwald 
2064e074f72SMilanka Ringwald }
2074e074f72SMilanka Ringwald 
208e7a41128SMilanka Ringwald void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, int16_t *in, int16_t *out){
2097e6b1e83SMilanka Ringwald     float val;
2107e6b1e83SMilanka Ringwald     int i = 0;
2114e074f72SMilanka Ringwald     if (plc_state->nbf>0){
2127e6b1e83SMilanka Ringwald         for (i=0;i<SBC_RT;i++){
2137e6b1e83SMilanka Ringwald             out[i] = plc_state->hist[SBC_LHIST+i];
2144e074f72SMilanka Ringwald         }
2157e6b1e83SMilanka Ringwald 
2167e6b1e83SMilanka Ringwald         for (;i<SBC_RT+SBC_OLAL;i++){
2177e6b1e83SMilanka Ringwald             float left  = plc_state->hist[SBC_LHIST+i];
2187e6b1e83SMilanka Ringwald             float right = in[i];
2197e6b1e83SMilanka Ringwald             val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL-1-i+SBC_RT];
220250a1e4aSMilanka Ringwald             out[i] = (int16_t)val;
2217e6b1e83SMilanka Ringwald         }
2227e6b1e83SMilanka Ringwald     }
2237e6b1e83SMilanka Ringwald     for (;i<SBC_FS;i++){
2244e074f72SMilanka Ringwald         out[i] = in[i];
2257e6b1e83SMilanka Ringwald     }
2267e6b1e83SMilanka Ringwald 
2274e074f72SMilanka Ringwald     /*Copy the output to the history buffer */
2287e6b1e83SMilanka Ringwald     for (i=0;i<SBC_FS;i++){
2297e6b1e83SMilanka Ringwald         plc_state->hist[SBC_LHIST+i] = out[i];
2307e6b1e83SMilanka Ringwald     }
2314e074f72SMilanka Ringwald     /* shift the history buffer */
2327e6b1e83SMilanka Ringwald     for (i=0;i<SBC_LHIST;i++){
2337e6b1e83SMilanka Ringwald         plc_state->hist[i] = plc_state->hist[i+SBC_FS];
2347e6b1e83SMilanka Ringwald     }
2357e6b1e83SMilanka Ringwald 
2364e074f72SMilanka Ringwald     plc_state->nbf=0;
2374e074f72SMilanka Ringwald }
238