xref: /btstack/src/classic/btstack_sbc_plc.c (revision de854f9a28de198c2ddef1824d28b975373a1dd8)
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 
38ab2c6ae4SMatthias Ringwald #define __BTSTACK_FILE__ "btstack_sbc_plc.c"
39ab2c6ae4SMatthias Ringwald 
404e074f72SMilanka Ringwald /*
41e7a41128SMilanka Ringwald  * btstack_sbc_plc.c
424e074f72SMilanka Ringwald  *
434e074f72SMilanka Ringwald  */
444e074f72SMilanka Ringwald 
454e074f72SMilanka Ringwald #include <stdint.h>
464e074f72SMilanka Ringwald #include <stdio.h>
474e074f72SMilanka Ringwald #include <stdlib.h>
484e074f72SMilanka Ringwald #include <string.h>
494e074f72SMilanka Ringwald 
504e074f72SMilanka Ringwald #include "btstack_sbc_plc.h"
51*de854f9aSMilanka Ringwald #include "btstack_debug.h"
524e074f72SMilanka Ringwald 
534e4f6f26SMatthias Ringwald #define SAMPLE_FORMAT int16_t
544e4f6f26SMatthias Ringwald 
554e074f72SMilanka Ringwald static uint8_t indices0[] = { 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 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, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d,
584e074f72SMilanka Ringwald 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d,
594e074f72SMilanka Ringwald 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c};
604e074f72SMilanka Ringwald 
614e074f72SMilanka Ringwald /* Raised COSine table for OLA */
627e6b1e83SMilanka Ringwald static float rcos[SBC_OLAL] = {
634e074f72SMilanka Ringwald     0.99148655f,0.96623611f,0.92510857f,0.86950446f,
644e074f72SMilanka Ringwald     0.80131732f,0.72286918f,0.63683150f,0.54613418f,
654e074f72SMilanka Ringwald     0.45386582f,0.36316850f,0.27713082f,0.19868268f,
664e074f72SMilanka Ringwald     0.13049554f,0.07489143f,0.03376389f,0.00851345f};
674e074f72SMilanka Ringwald 
6840e1e61cSMilanka Ringwald // taken from http://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi
6940e1e61cSMilanka Ringwald // Algorithm: Babylonian Method + some manipulations on IEEE 32 bit floating point representation
7040e1e61cSMilanka Ringwald static float sqrt3(const float x){
7140e1e61cSMilanka Ringwald     union {
7240e1e61cSMilanka Ringwald         int i;
7340e1e61cSMilanka Ringwald         float x;
7440e1e61cSMilanka Ringwald     } u;
7540e1e61cSMilanka Ringwald     u.x = x;
7640e1e61cSMilanka Ringwald     u.i = (1<<29) + (u.i >> 1) - (1<<22);
7740e1e61cSMilanka Ringwald 
7840e1e61cSMilanka Ringwald     // Two Babylonian Steps (simplified from:)
7940e1e61cSMilanka Ringwald     // u.x = 0.5f * (u.x + x/u.x);
8040e1e61cSMilanka Ringwald     // u.x = 0.5f * (u.x + x/u.x);
8140e1e61cSMilanka Ringwald     u.x =       u.x + x/u.x;
8240e1e61cSMilanka Ringwald     u.x = 0.25f*u.x + x/u.x;
8340e1e61cSMilanka Ringwald 
8440e1e61cSMilanka Ringwald     return u.x;
8540e1e61cSMilanka Ringwald }
8640e1e61cSMilanka Ringwald 
8740e1e61cSMilanka Ringwald static float absolute(float x){
8840e1e61cSMilanka Ringwald      if (x < 0) x = -x;
8940e1e61cSMilanka Ringwald      return x;
9040e1e61cSMilanka Ringwald }
9140e1e61cSMilanka Ringwald 
924e4f6f26SMatthias Ringwald static float CrossCorrelation(SAMPLE_FORMAT *x, SAMPLE_FORMAT *y){
93c5e169ecSMilanka Ringwald     float num = 0;
94c5e169ecSMilanka Ringwald     float den = 0;
95c5e169ecSMilanka Ringwald     float x2 = 0;
96c5e169ecSMilanka Ringwald     float y2 = 0;
97c5e169ecSMilanka Ringwald     int   m;
98c5e169ecSMilanka Ringwald     for (m=0;m<SBC_M;m++){
99c5e169ecSMilanka Ringwald         num+=((float)x[m])*y[m];
100c5e169ecSMilanka Ringwald         x2+=((float)x[m])*x[m];
101c5e169ecSMilanka Ringwald         y2+=((float)y[m])*y[m];
102c5e169ecSMilanka Ringwald     }
10340e1e61cSMilanka Ringwald     den = (float)sqrt3(x2*y2);
104c5e169ecSMilanka Ringwald     return num/den;
105c5e169ecSMilanka Ringwald }
106c5e169ecSMilanka Ringwald 
1074e4f6f26SMatthias Ringwald static int PatternMatch(SAMPLE_FORMAT *y){
1084e4f6f26SMatthias Ringwald     float maxCn = -999999.0;  // large negative number
109c5e169ecSMilanka Ringwald     int   bestmatch = 0;
110c5e169ecSMilanka Ringwald     float Cn;
111c5e169ecSMilanka Ringwald     int   n;
112c5e169ecSMilanka Ringwald     for (n=0;n<SBC_N;n++){
1134e4f6f26SMatthias Ringwald         Cn = CrossCorrelation(&y[SBC_LHIST-SBC_M], &y[n]);
114c5e169ecSMilanka Ringwald         if (Cn>maxCn){
115c5e169ecSMilanka Ringwald             bestmatch=n;
116c5e169ecSMilanka Ringwald             maxCn = Cn;
117c5e169ecSMilanka Ringwald         }
118c5e169ecSMilanka Ringwald     }
119c5e169ecSMilanka Ringwald     return bestmatch;
120c5e169ecSMilanka Ringwald }
121c5e169ecSMilanka Ringwald 
1224e4f6f26SMatthias Ringwald static float AmplitudeMatch(SAMPLE_FORMAT *y, SAMPLE_FORMAT bestmatch) {
123c5e169ecSMilanka Ringwald     int   i;
124c5e169ecSMilanka Ringwald     float sumx = 0;
125c5e169ecSMilanka Ringwald     float sumy = 0.000001f;
126c5e169ecSMilanka Ringwald     float sf;
127c5e169ecSMilanka Ringwald 
128c5e169ecSMilanka Ringwald     for (i=0;i<SBC_FS;i++){
12940e1e61cSMilanka Ringwald         sumx += absolute(y[SBC_LHIST-SBC_FS+i]);
13040e1e61cSMilanka Ringwald         sumy += absolute(y[bestmatch+i]);
131c5e169ecSMilanka Ringwald     }
132c5e169ecSMilanka Ringwald     sf = sumx/sumy;
1334e4f6f26SMatthias Ringwald     // This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts
134c5e169ecSMilanka Ringwald     if (sf<0.75f) sf=0.75f;
135c5e169ecSMilanka Ringwald     if (sf>1.2f) sf=1.2f;
136c5e169ecSMilanka Ringwald     return sf;
137c5e169ecSMilanka Ringwald }
1384e074f72SMilanka Ringwald 
1394e4f6f26SMatthias Ringwald static SAMPLE_FORMAT crop_sample(float val){
140250a1e4aSMilanka Ringwald     float croped_val = val;
141250a1e4aSMilanka Ringwald     if (croped_val > 32767.0)  croped_val= 32767.0;
142250a1e4aSMilanka Ringwald     if (croped_val < -32768.0) croped_val=-32768.0;
1434e4f6f26SMatthias Ringwald     return (SAMPLE_FORMAT) croped_val;
1447e6b1e83SMilanka Ringwald }
1457e6b1e83SMilanka Ringwald 
146e7a41128SMilanka Ringwald uint8_t * btstack_sbc_plc_zero_signal_frame(void){
1474e074f72SMilanka Ringwald     return (uint8_t *)&indices0;
1484e074f72SMilanka Ringwald }
1494e074f72SMilanka Ringwald 
150e7a41128SMilanka Ringwald void btstack_sbc_plc_init(btstack_sbc_plc_state_t *plc_state){
1514e074f72SMilanka Ringwald     plc_state->nbf=0;
1524e074f72SMilanka Ringwald     plc_state->bestlag=0;
153b3f76298SMilanka Ringwald     memset(plc_state->hist,0,sizeof(plc_state->hist));
1544e074f72SMilanka Ringwald }
1554e074f72SMilanka Ringwald 
1564e4f6f26SMatthias Ringwald void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, SAMPLE_FORMAT *ZIRbuf, SAMPLE_FORMAT *out){
1574e074f72SMilanka Ringwald     float val;
1587e6b1e83SMilanka Ringwald     int   i = 0;
1597e6b1e83SMilanka Ringwald     float sf = 1;
1607e6b1e83SMilanka Ringwald     plc_state->nbf++;
1617e6b1e83SMilanka Ringwald 
162*de854f9aSMilanka Ringwald     plc_state->bad_frames_nr++;
163*de854f9aSMilanka Ringwald     plc_state->frame_count++;
164*de854f9aSMilanka Ringwald     if (plc_state->max_consecutive_bad_frames_nr < plc_state->nbf){
165*de854f9aSMilanka Ringwald         plc_state->max_consecutive_bad_frames_nr = plc_state->nbf;
166*de854f9aSMilanka Ringwald     }
167*de854f9aSMilanka Ringwald 
1684e074f72SMilanka Ringwald     if (plc_state->nbf==1){
1694e4f6f26SMatthias Ringwald         // Perform pattern matching to find where to replicate
1704e074f72SMilanka Ringwald         plc_state->bestlag = PatternMatch(plc_state->hist);
1714e4f6f26SMatthias Ringwald         // the replication begins after the template match
1727e6b1e83SMilanka Ringwald         plc_state->bestlag += SBC_M;
1734e074f72SMilanka Ringwald 
1744e4f6f26SMatthias Ringwald         // Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet
1754e074f72SMilanka Ringwald         sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag);
1767e6b1e83SMilanka Ringwald         for (i=0;i<SBC_OLAL;i++){
1777e6b1e83SMilanka Ringwald             float left  = ZIRbuf[i];
1787e6b1e83SMilanka Ringwald             float right = sf*plc_state->hist[plc_state->bestlag+i];
17962f77245SMilanka Ringwald             val = left*rcos[i] + right*rcos[SBC_OLAL-1-i];
1804e4f6f26SMatthias Ringwald             plc_state->hist[SBC_LHIST+i] = crop_sample(val);
1814e074f72SMilanka Ringwald         }
1824e074f72SMilanka Ringwald 
1837e6b1e83SMilanka Ringwald         for (;i<SBC_FS;i++){
1847e6b1e83SMilanka Ringwald             val = sf*plc_state->hist[plc_state->bestlag+i];
1854e4f6f26SMatthias Ringwald             plc_state->hist[SBC_LHIST+i] = crop_sample(val);
1864e074f72SMilanka Ringwald         }
1874e074f72SMilanka Ringwald 
1887e6b1e83SMilanka Ringwald         for (;i<SBC_FS+SBC_OLAL;i++){
1897e6b1e83SMilanka Ringwald             float left  = sf*plc_state->hist[plc_state->bestlag+i];
1907e6b1e83SMilanka Ringwald             float right = plc_state->hist[plc_state->bestlag+i];
1914e4f6f26SMatthias Ringwald             val = left*rcos[i-SBC_FS]+right*rcos[SBC_OLAL-1-i+SBC_FS];
1924e4f6f26SMatthias Ringwald             plc_state->hist[SBC_LHIST+i] = crop_sample(val);
1934e074f72SMilanka Ringwald         }
1944e074f72SMilanka Ringwald 
1957e6b1e83SMilanka Ringwald         for (;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
1967e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
1977e6b1e83SMilanka Ringwald         }
1984e074f72SMilanka Ringwald     } else {
1994e4f6f26SMatthias Ringwald         for (;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
2007e6b1e83SMilanka Ringwald             plc_state->hist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
2014e074f72SMilanka Ringwald         }
202c5e169ecSMilanka Ringwald     }
2037e6b1e83SMilanka Ringwald     for (i=0;i<SBC_FS;i++){
2047e6b1e83SMilanka Ringwald         out[i] = plc_state->hist[SBC_LHIST+i];
2057e6b1e83SMilanka Ringwald     }
2064e074f72SMilanka Ringwald 
2074e4f6f26SMatthias Ringwald    // shift the history buffer
2087e6b1e83SMilanka Ringwald     for (i=0;i<SBC_LHIST+SBC_RT+SBC_OLAL;i++){
2097e6b1e83SMilanka Ringwald         plc_state->hist[i] = plc_state->hist[i+SBC_FS];
2107e6b1e83SMilanka Ringwald     }
2114e074f72SMilanka Ringwald }
2124e074f72SMilanka Ringwald 
2134e4f6f26SMatthias Ringwald void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, SAMPLE_FORMAT *in, SAMPLE_FORMAT *out){
2147e6b1e83SMilanka Ringwald     float val;
2157e6b1e83SMilanka Ringwald     int i = 0;
216*de854f9aSMilanka Ringwald     plc_state->good_frames_nr++;
217*de854f9aSMilanka Ringwald     plc_state->frame_count++;
218*de854f9aSMilanka Ringwald 
2194e074f72SMilanka Ringwald     if (plc_state->nbf>0){
2207e6b1e83SMilanka Ringwald         for (i=0;i<SBC_RT;i++){
2217e6b1e83SMilanka Ringwald             out[i] = plc_state->hist[SBC_LHIST+i];
2224e074f72SMilanka Ringwald         }
2237e6b1e83SMilanka Ringwald 
2244e4f6f26SMatthias Ringwald         for (i = SBC_RT;i<SBC_RT+SBC_OLAL;i++){
2257e6b1e83SMilanka Ringwald             float left  = plc_state->hist[SBC_LHIST+i];
2267e6b1e83SMilanka Ringwald             float right = in[i];
2274e4f6f26SMatthias Ringwald             val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL+SBC_RT-1-i];
2284e4f6f26SMatthias Ringwald             out[i] = (SAMPLE_FORMAT)val;
2297e6b1e83SMilanka Ringwald         }
2307e6b1e83SMilanka Ringwald     }
2314e4f6f26SMatthias Ringwald 
2327e6b1e83SMilanka Ringwald     for (;i<SBC_FS;i++){
2334e074f72SMilanka Ringwald         out[i] = in[i];
2347e6b1e83SMilanka Ringwald     }
2354e4f6f26SMatthias Ringwald     // Copy the output to the history buffer
2367e6b1e83SMilanka Ringwald     for (i=0;i<SBC_FS;i++){
2377e6b1e83SMilanka Ringwald         plc_state->hist[SBC_LHIST+i] = out[i];
2387e6b1e83SMilanka Ringwald     }
2394e4f6f26SMatthias Ringwald     // shift the history buffer
2407e6b1e83SMilanka Ringwald     for (i=0;i<SBC_LHIST;i++){
2417e6b1e83SMilanka Ringwald         plc_state->hist[i] = plc_state->hist[i+SBC_FS];
2427e6b1e83SMilanka Ringwald     }
2437e6b1e83SMilanka Ringwald 
2444e074f72SMilanka Ringwald     plc_state->nbf=0;
2454e074f72SMilanka Ringwald }
246*de854f9aSMilanka Ringwald 
247*de854f9aSMilanka Ringwald void btstack_sbc_dump_statistics(btstack_sbc_plc_state_t * state){
248*de854f9aSMilanka Ringwald     log_info("Good frames: %d\n", state->good_frames_nr);
249*de854f9aSMilanka Ringwald     log_info("Bad frames: %d\n", state->bad_frames_nr);
250*de854f9aSMilanka Ringwald     log_info("Max Consecutive bad frames: %d\n", state->max_consecutive_bad_frames_nr);
251*de854f9aSMilanka Ringwald }
252