xref: /btstack/src/classic/btstack_cvsd_plc.c (revision a5eb47ac140cc3b4a16b8b8db6195eeda061dccf)
1 /*
2  * Copyright (C) 2016 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define __BTSTACK_FILE__ "btstack_cvsd_plc.c"
39 
40 /*
41  * btstack_sbc_plc.c
42  *
43  */
44 
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 
50 #include "btstack_cvsd_plc.h"
51 #include "btstack_debug.h"
52 
53 static float rcos[CVSD_OLAL] = {
54     0.99148655f,0.96623611f,0.92510857f,0.86950446f,
55     0.80131732f,0.72286918f,0.63683150f,0.54613418f,
56     0.45386582f,0.36316850f,0.27713082f,0.19868268f,
57     0.13049554f,0.07489143f,0.03376389f,0.00851345f};
58 
59 float btstack_cvsd_plc_rcos(int index){
60     if (index > CVSD_OLAL) return 0;
61     return rcos[index];
62 }
63 // taken from http://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi
64 // Algorithm: Babylonian Method + some manipulations on IEEE 32 bit floating point representation
65 static float sqrt3(const float x){
66     union {
67         int i;
68         float x;
69     } u;
70     u.x = x;
71     u.i = (1<<29) + (u.i >> 1) - (1<<22);
72 
73     // Two Babylonian Steps (simplified from:)
74     // u.x = 0.5f * (u.x + x/u.x);
75     // u.x = 0.5f * (u.x + x/u.x);
76     u.x =       u.x + x/u.x;
77     u.x = 0.25f*u.x + x/u.x;
78 
79     return u.x;
80 }
81 
82 static float btstack_cvsd_plc_absolute(float x){
83      if (x < 0) x = -x;
84      return x;
85 }
86 
87 static float btstack_cvsd_plc_cross_correlation(BTSTACK_CVSD_PLC_SAMPLE_FORMAT *x, BTSTACK_CVSD_PLC_SAMPLE_FORMAT *y){
88     float num = 0;
89     float den = 0;
90     float x2 = 0;
91     float y2 = 0;
92     int   m;
93     for (m=0;m<CVSD_M;m++){
94         num+=((float)x[m])*y[m];
95         x2+=((float)x[m])*x[m];
96         y2+=((float)y[m])*y[m];
97     }
98     den = (float)sqrt3(x2*y2);
99     return num/den;
100 }
101 
102 int btstack_cvsd_plc_pattern_match(BTSTACK_CVSD_PLC_SAMPLE_FORMAT *y){
103     float maxCn = -999999.0;  // large negative number
104     int   bestmatch = 0;
105     float Cn;
106     int   n;
107     for (n=0;n<CVSD_N;n++){
108         Cn = btstack_cvsd_plc_cross_correlation(&y[CVSD_LHIST-CVSD_M], &y[n]);
109         if (Cn>maxCn){
110             bestmatch=n;
111             maxCn = Cn;
112         }
113     }
114     return bestmatch;
115 }
116 
117 float btstack_cvsd_plc_amplitude_match(btstack_cvsd_plc_state_t *plc_state, uint16_t num_samples, BTSTACK_CVSD_PLC_SAMPLE_FORMAT *y, BTSTACK_CVSD_PLC_SAMPLE_FORMAT bestmatch){
118     int   i;
119     float sumx = 0;
120     float sumy = 0.000001f;
121     float sf;
122 
123     for (i=0;i<num_samples;i++){
124         sumx += btstack_cvsd_plc_absolute(y[CVSD_LHIST-num_samples+i]);
125         sumy += btstack_cvsd_plc_absolute(y[bestmatch+i]);
126     }
127     sf = sumx/sumy;
128     // This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts
129     if (sf<0.75f) sf=0.75f;
130     if (sf>1.0) sf=1.0f;
131     return sf;
132 }
133 
134 BTSTACK_CVSD_PLC_SAMPLE_FORMAT btstack_cvsd_plc_crop_sample(float val){
135     float croped_val = val;
136     if (croped_val > 32767.0)  croped_val= 32767.0;
137     if (croped_val < -32768.0) croped_val=-32768.0;
138     return (BTSTACK_CVSD_PLC_SAMPLE_FORMAT) croped_val;
139 }
140 
141 void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state){
142     memset(plc_state, 0, sizeof(btstack_cvsd_plc_state_t));
143 }
144 
145 void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, uint16_t num_samples, BTSTACK_CVSD_PLC_SAMPLE_FORMAT *out){
146     float val;
147     int   i = 0;
148     float sf = 1;
149     plc_state->nbf++;
150     // plc_state->cvsd_fs = CVSD_FS_MAX;
151     if (plc_state->nbf==1){
152         // Perform pattern matching to find where to replicate
153         plc_state->bestlag = btstack_cvsd_plc_pattern_match(plc_state->hist);
154         // the replication begins after the template match
155         plc_state->bestlag += CVSD_M;
156 
157         // Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet
158         sf = btstack_cvsd_plc_amplitude_match(plc_state, num_samples, plc_state->hist, plc_state->bestlag);
159         for (i=0;i<CVSD_OLAL;i++){
160             val = sf*plc_state->hist[plc_state->bestlag+i];
161             plc_state->hist[CVSD_LHIST+i] = btstack_cvsd_plc_crop_sample(val);
162         }
163 
164         for (;i<num_samples;i++){
165             val = sf*plc_state->hist[plc_state->bestlag+i];
166             plc_state->hist[CVSD_LHIST+i] = btstack_cvsd_plc_crop_sample(val);
167         }
168 
169         for (;i<num_samples+CVSD_OLAL;i++){
170             float left  = sf*plc_state->hist[plc_state->bestlag+i];
171             float right = plc_state->hist[plc_state->bestlag+i];
172             val = left*rcos[i-num_samples] + right*rcos[CVSD_OLAL-1-i+num_samples];
173             plc_state->hist[CVSD_LHIST+i] = btstack_cvsd_plc_crop_sample(val);
174         }
175 
176         for (;i<num_samples+CVSD_RT+CVSD_OLAL;i++){
177             plc_state->hist[CVSD_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
178         }
179     } else {
180         for (;i<num_samples+CVSD_RT+CVSD_OLAL;i++){
181             plc_state->hist[CVSD_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
182         }
183     }
184 
185     for (i=0;i<num_samples;i++){
186         out[i] = plc_state->hist[CVSD_LHIST+i];
187     }
188 
189     // shift the history buffer
190     for (i=0;i<CVSD_LHIST+CVSD_RT+CVSD_OLAL;i++){
191         plc_state->hist[i] = plc_state->hist[i+num_samples];
192     }
193 }
194 
195 void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, uint16_t num_samples, BTSTACK_CVSD_PLC_SAMPLE_FORMAT *in, BTSTACK_CVSD_PLC_SAMPLE_FORMAT *out){
196     float val;
197     int i = 0;
198     if (plc_state->nbf>0){
199         for (i=0;i<CVSD_RT;i++){
200             out[i] = plc_state->hist[CVSD_LHIST+i];
201         }
202 
203         for (i=CVSD_RT;i<CVSD_RT+CVSD_OLAL;i++){
204             float left  = plc_state->hist[CVSD_LHIST+i];
205             float right = in[i];
206             val = left * rcos[i-CVSD_RT] + right *rcos[CVSD_OLAL+CVSD_RT-1-i];
207             out[i] = (BTSTACK_CVSD_PLC_SAMPLE_FORMAT)val;
208         }
209     }
210 
211     for (;i<num_samples;i++){
212         out[i] = in[i];
213     }
214     // Copy the output to the history buffer
215     for (i=0;i<num_samples;i++){
216         plc_state->hist[CVSD_LHIST+i] = out[i];
217     }
218     // shift the history buffer
219     for (i=0;i<CVSD_LHIST;i++){
220         plc_state->hist[i] = plc_state->hist[i+num_samples];
221     }
222     plc_state->nbf=0;
223 }
224 
225 static int count_equal_samples(BTSTACK_CVSD_PLC_SAMPLE_FORMAT * packet, uint16_t size){
226     int count = 0;
227     int temp_count = 1;
228     int i;
229     for (i = 0; i < size-1; i++){
230         if (packet[i] == packet[i+1]){
231             temp_count++;
232             continue;
233         }
234         if (count < temp_count){
235             count = temp_count;
236         }
237         temp_count = 1;
238     }
239     if (temp_count > count + 1){
240         count = temp_count;
241     }
242     return count;
243 }
244 
245 // more than half the samples are the same -> bad frame
246 static int bad_frame(btstack_cvsd_plc_state_t *plc_state, BTSTACK_CVSD_PLC_SAMPLE_FORMAT * frame, uint16_t size){
247     return count_equal_samples(frame, size) > size / 2;
248 }
249 
250 void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * plc_state, BTSTACK_CVSD_PLC_SAMPLE_FORMAT * in, uint16_t num_samples, BTSTACK_CVSD_PLC_SAMPLE_FORMAT * out){
251     if (num_samples == 0) return;
252 
253     plc_state->frame_count++;
254 
255     if (bad_frame(plc_state, in, num_samples)){
256         memcpy(out, in, num_samples * 2);
257         if (plc_state->good_samples > CVSD_LHIST){
258             btstack_cvsd_plc_bad_frame(plc_state, num_samples, out);
259             plc_state->bad_frames_nr++;
260         } else {
261             memset(out, 0, num_samples * 2);
262         }
263     } else {
264         btstack_cvsd_plc_good_frame(plc_state, num_samples, in, out);
265         plc_state->good_frames_nr++;
266         if (plc_state->good_frames_nr == 1){
267             log_info("First good frame at index %d\n", plc_state->frame_count-1);
268         }
269         plc_state->good_samples += num_samples;
270     }
271 }
272 
273 void btstack_cvsd_dump_statistics(btstack_cvsd_plc_state_t * state){
274     log_info("Good frames: %d\n", state->good_frames_nr);
275     log_info("Bad frames: %d\n", state->bad_frames_nr);
276 }
277