1 /*
2 * Copyright (C) 2022 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__ "lc3_test.c"
39
40 /*
41 * Measure LC3 encoding load
42 */
43
44 #include <stdint.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <btstack_debug.h>
48
49 #include "bluetooth_data_types.h"
50 #include "btstack_stdin.h"
51 #include "btstack_event.h"
52 #include "btstack_run_loop.h"
53 #include "gap.h"
54 #include "hci.h"
55 #include "hci_cmd.h"
56 #include "hci_dump.h"
57 #include "btstack_lc3.h"
58 #include "btstack_lc3_google.h"
59
60 #include "hxcmod.h"
61 #include "mods/mod.h"
62
63
64 #ifdef HAVE_POSIX_FILE_IO
65 #include "wav_util.h"
66 #endif
67
68 #include "btstack_lc3plus_fraunhofer.h"
69
70 // max config
71 #define MAX_SAMPLES_PER_FRAME 480
72 #define MAX_NUM_BIS 2
73
74 // lc3 codec config
75 static uint32_t sampling_frequency_hz;
76 static btstack_lc3_frame_duration_t frame_duration;
77 static uint16_t number_samples_per_frame;
78 static uint16_t octets_per_frame;
79 static uint8_t num_bis = 1;
80
81 // lc3 encoder
82 static const btstack_lc3_encoder_t * lc3_encoder;
83 static btstack_lc3_encoder_google_t encoder_contexts[MAX_NUM_BIS];
84 static int16_t pcm[MAX_NUM_BIS * MAX_SAMPLES_PER_FRAME];
85
86 // lc3 decoder
87 static bool use_lc3plus_decoder = false;
88 static const btstack_lc3_decoder_t * lc3_decoder;
89 static int16_t pcm[MAX_NUM_BIS * MAX_SAMPLES_PER_FRAME];
90
91 static btstack_lc3_decoder_google_t google_decoder_contexts[MAX_NUM_BIS];
92 #ifdef HAVE_LC3PLUS
93 static btstack_lc3plus_fraunhofer_decoder_t fraunhofer_decoder_contexts[MAX_NUM_BIS];
94 #endif
95 static void * decoder_contexts[MAX_NR_BIS];
96
97 // PLC
98 static uint16_t plc_frame_counter;
99 static uint16_t plc_dopped_frame_interval;
100
101 // codec menu
102 static uint8_t menu_sampling_frequency;
103 static uint8_t menu_variant;
104
105 // mod player
106 static int hxcmod_initialized;
107 static modcontext mod_context;
108 static tracker_buffer_state trkbuf;
109
110 // sine generator
111 static uint8_t sine_step;
112 static uint16_t sine_phases[MAX_NUM_BIS];
113
114 // audio producer
115 static enum {
116 AUDIO_SOURCE_SINE,
117 AUDIO_SOURCE_MODPLAYER
118 } audio_source = AUDIO_SOURCE_MODPLAYER;
119
120 // enumerate default codec configs
121 static struct {
122 uint32_t samplingrate_hz;
123 uint8_t samplingrate_index;
124 uint8_t num_variants;
125 struct {
126 const char * name;
127 btstack_lc3_frame_duration_t frame_duration;
128 uint16_t octets_per_frame;
129 } variants[6];
130 } codec_configurations[] = {
131 {
132 8000, 0x01, 2,
133 {
134 { "8_1", BTSTACK_LC3_FRAME_DURATION_7500US, 26},
135 { "8_2", BTSTACK_LC3_FRAME_DURATION_10000US, 30}
136 }
137 },
138 {
139 16000, 0x03, 2,
140 {
141 { "16_1", BTSTACK_LC3_FRAME_DURATION_7500US, 30},
142 { "16_2", BTSTACK_LC3_FRAME_DURATION_10000US, 40}
143 }
144 },
145 {
146 24000, 0x05, 2,
147 {
148 { "24_1", BTSTACK_LC3_FRAME_DURATION_7500US, 45},
149 { "24_2", BTSTACK_LC3_FRAME_DURATION_10000US, 60}
150 }
151 },
152 {
153 32000, 0x06, 2,
154 {
155 { "32_1", BTSTACK_LC3_FRAME_DURATION_7500US, 60},
156 { "32_2", BTSTACK_LC3_FRAME_DURATION_10000US, 80}
157 }
158 },
159 {
160 44100, 0x07, 2,
161 {
162 { "441_1", BTSTACK_LC3_FRAME_DURATION_7500US, 97},
163 { "441_2", BTSTACK_LC3_FRAME_DURATION_10000US, 130}
164 }
165 },
166 {
167 48000, 0x08, 6,
168 {
169 { "48_1", BTSTACK_LC3_FRAME_DURATION_7500US, 75},
170 { "48_2", BTSTACK_LC3_FRAME_DURATION_10000US, 100},
171 { "48_3", BTSTACK_LC3_FRAME_DURATION_7500US, 90},
172 { "48_4", BTSTACK_LC3_FRAME_DURATION_10000US, 120},
173 { "48_5", BTSTACK_LC3_FRAME_DURATION_7500US, 117},
174 { "48_6", BTSTACK_LC3_FRAME_DURATION_10000US, 155}
175 }
176 },
177 };
178
179
180 // input signal: pre-computed int16 sine wave, 96000 Hz at 300 Hz
181 static const int16_t sine_int16[] = {
182 0, 643, 1286, 1929, 2571, 3212, 3851, 4489, 5126, 5760,
183 6393, 7022, 7649, 8273, 8894, 9512, 10126, 10735, 11341, 11943,
184 12539, 13131, 13718, 14300, 14876, 15446, 16011, 16569, 17121, 17666,
185 18204, 18736, 19260, 19777, 20286, 20787, 21280, 21766, 22242, 22710,
186 23170, 23620, 24062, 24494, 24916, 25329, 25732, 26126, 26509, 26882,
187 27245, 27597, 27938, 28269, 28589, 28898, 29196, 29482, 29757, 30021,
188 30273, 30513, 30742, 30958, 31163, 31356, 31537, 31705, 31862, 32006,
189 32137, 32257, 32364, 32458, 32540, 32609, 32666, 32710, 32742, 32761,
190 32767, 32761, 32742, 32710, 32666, 32609, 32540, 32458, 32364, 32257,
191 32137, 32006, 31862, 31705, 31537, 31356, 31163, 30958, 30742, 30513,
192 30273, 30021, 29757, 29482, 29196, 28898, 28589, 28269, 27938, 27597,
193 27245, 26882, 26509, 26126, 25732, 25329, 24916, 24494, 24062, 23620,
194 23170, 22710, 22242, 21766, 21280, 20787, 20286, 19777, 19260, 18736,
195 18204, 17666, 17121, 16569, 16011, 15446, 14876, 14300, 13718, 13131,
196 12539, 11943, 11341, 10735, 10126, 9512, 8894, 8273, 7649, 7022,
197 6393, 5760, 5126, 4489, 3851, 3212, 2571, 1929, 1286, 643,
198 0, -643, -1286, -1929, -2571, -3212, -3851, -4489, -5126, -5760,
199 -6393, -7022, -7649, -8273, -8894, -9512, -10126, -10735, -11341, -11943,
200 -12539, -13131, -13718, -14300, -14876, -15446, -16011, -16569, -17121, -17666,
201 -18204, -18736, -19260, -19777, -20286, -20787, -21280, -21766, -22242, -22710,
202 -23170, -23620, -24062, -24494, -24916, -25329, -25732, -26126, -26509, -26882,
203 -27245, -27597, -27938, -28269, -28589, -28898, -29196, -29482, -29757, -30021,
204 -30273, -30513, -30742, -30958, -31163, -31356, -31537, -31705, -31862, -32006,
205 -32137, -32257, -32364, -32458, -32540, -32609, -32666, -32710, -32742, -32761,
206 -32767, -32761, -32742, -32710, -32666, -32609, -32540, -32458, -32364, -32257,
207 -32137, -32006, -31862, -31705, -31537, -31356, -31163, -30958, -30742, -30513,
208 -30273, -30021, -29757, -29482, -29196, -28898, -28589, -28269, -27938, -27597,
209 -27245, -26882, -26509, -26126, -25732, -25329, -24916, -24494, -24062, -23620,
210 -23170, -22710, -22242, -21766, -21280, -20787, -20286, -19777, -19260, -18736,
211 -18204, -17666, -17121, -16569, -16011, -15446, -14876, -14300, -13718, -13131,
212 -12539, -11943, -11341, -10735, -10126, -9512, -8894, -8273, -7649, -7022,
213 -6393, -5760, -5126, -4489, -3851, -3212, -2571, -1929, -1286, -643,
214 };
215
216 static void show_usage(void);
217
print_config(void)218 static void print_config(void) {
219 printf("Config '%s_%u': %u, %s ms, %u octets - %s, drop frame interval %u, decoder %s\n",
220 codec_configurations[menu_sampling_frequency].variants[menu_variant].name,
221 num_bis,
222 codec_configurations[menu_sampling_frequency].samplingrate_hz,
223 codec_configurations[menu_sampling_frequency].variants[menu_variant].frame_duration == BTSTACK_LC3_FRAME_DURATION_7500US ? "7.5" : "10",
224 codec_configurations[menu_sampling_frequency].variants[menu_variant].octets_per_frame,
225 audio_source == AUDIO_SOURCE_SINE ? "Sine" : "Modplayer",
226 plc_dopped_frame_interval,
227 use_lc3plus_decoder ? "LC3plus" : "LC3");
228 }
229
setup_lc3_encoder(void)230 static void setup_lc3_encoder(void){
231 uint8_t channel;
232 for (channel = 0 ; channel < num_bis ; channel++){
233 btstack_lc3_encoder_google_t * context = &encoder_contexts[channel];
234 lc3_encoder = btstack_lc3_encoder_google_init_instance(context);
235 lc3_encoder->configure(context, sampling_frequency_hz, frame_duration, octets_per_frame);
236 }
237 number_samples_per_frame = btstack_lc3_samples_per_frame(sampling_frequency_hz, frame_duration);
238 btstack_assert(number_samples_per_frame <= MAX_SAMPLES_PER_FRAME);
239 printf("LC3 Encoder config: %u hz, frame duration %s ms, num samples %u, num octets %u\n",
240 sampling_frequency_hz, frame_duration == BTSTACK_LC3_FRAME_DURATION_7500US ? "7.5" : "10",
241 number_samples_per_frame, octets_per_frame);
242 }
243
setup_lc3_decoder(void)244 static void setup_lc3_decoder(void){
245 uint8_t channel;
246 for (channel = 0 ; channel < num_bis ; channel++){
247 // pick decoder
248 void * decoder_context = NULL;
249 #ifdef HAVE_LC3PLUS
250 if (use_lc3plus_decoder){
251 decoder_context = &fraunhofer_decoder_contexts[channel];
252 lc3_decoder = btstack_lc3plus_fraunhofer_decoder_init_instance(decoder_context);
253 }
254 else
255 #endif
256 {
257 decoder_context = &google_decoder_contexts[channel];
258 lc3_decoder = btstack_lc3_decoder_google_init_instance(decoder_context);
259 }
260 decoder_contexts[channel] = decoder_context;
261 lc3_decoder->configure(decoder_context, sampling_frequency_hz, frame_duration, octets_per_frame);
262 }
263 number_samples_per_frame = btstack_lc3_samples_per_frame(sampling_frequency_hz, frame_duration);
264 btstack_assert(number_samples_per_frame <= MAX_SAMPLES_PER_FRAME);
265 }
266
setup_mod_player(void)267 static void setup_mod_player(void){
268 if (!hxcmod_initialized) {
269 hxcmod_initialized = hxcmod_init(&mod_context);
270 btstack_assert(hxcmod_initialized != 0);
271 }
272 hxcmod_unload(&mod_context);
273 hxcmod_setcfg(&mod_context, sampling_frequency_hz, 16, 1, 1, 1);
274 hxcmod_load(&mod_context, (void *) &mod_data, mod_len);
275 }
276
generate_audio(void)277 static void generate_audio(void){
278 uint16_t sample;
279 switch (audio_source) {
280 case AUDIO_SOURCE_SINE:
281 // generate sine wave for all channels
282 for (sample = 0 ; sample < number_samples_per_frame ; sample++){
283 uint8_t channel;
284 for (channel = 0; channel < num_bis; channel++) {
285 int16_t value = sine_int16[sine_phases[channel]] / 4;
286 pcm[sample * num_bis + channel] = value;
287 sine_phases[channel] += sine_step * (1+channel); // second channel, double frequency
288 if (sine_phases[channel] >= (sizeof(sine_int16) / sizeof(int16_t))) {
289 sine_phases[channel] = 0;
290 }
291 }
292 }
293 break;
294 case AUDIO_SOURCE_MODPLAYER:
295 // mod player configured for stereo
296 hxcmod_fillbuffer(&mod_context, (unsigned short *) pcm, number_samples_per_frame, &trkbuf);
297 if (num_bis == 1) {
298 // stereo -> mono
299 uint16_t i;
300 for (i=0;i<number_samples_per_frame;i++){
301 pcm[i] = (pcm[2*i] / 2) + (pcm[2*i+1] / 2);
302 }
303 }
304 break;
305 default:
306 btstack_unreachable();
307 break;
308 }
309 }
310
test_encoder()311 static void test_encoder(){
312 #ifdef HAVE_POSIX_FILE_IO
313 wav_writer_open("lc3_test.wav", 1, sampling_frequency_hz);
314 #endif
315
316 // encode 10 seconds of music
317 uint32_t audio_duration_in_seconds = 10;
318 uint32_t total_samples = sampling_frequency_hz * audio_duration_in_seconds;
319 uint32_t generated_samples = 0;
320 uint32_t player_ms = 0;
321 uint32_t encoder_ms = 0;
322 uint32_t decoder_ms = 0;
323 plc_frame_counter = 0;
324
325 printf("Encoding and decoding %u seconds of audio...\n", audio_duration_in_seconds);
326 while (generated_samples < total_samples){
327
328 // generate audio
329 uint32_t block_start_ms = btstack_run_loop_get_time_ms();
330 generate_audio();
331 uint32_t block_generated_ms = btstack_run_loop_get_time_ms();
332
333 // encode frame
334 uint8_t buffer[200];
335 lc3_encoder->encode_signed_16(&encoder_contexts[0], pcm, 1, buffer);
336 generated_samples += number_samples_per_frame;
337 uint32_t block_encoded_ms = btstack_run_loop_get_time_ms();
338 plc_frame_counter++;
339
340 uint8_t BFI = 0;
341 // simulate dropped packets
342 if ((plc_dopped_frame_interval != 0) && (plc_frame_counter == plc_dopped_frame_interval)){
343 plc_frame_counter = 0;
344 BFI = 1;
345 }
346
347 // decode codec frame
348 uint8_t tmp_BEC_detect;
349 (void) lc3_decoder->decode_signed_16(decoder_contexts[0], buffer, BFI, pcm, 1, &tmp_BEC_detect);
350
351 uint32_t block_decoded_ms = btstack_run_loop_get_time_ms();
352
353 #ifdef HAVE_POSIX_FILE_IO
354 wav_writer_write_int16(number_samples_per_frame, pcm);
355 #endif
356
357 // summary
358 player_ms += block_generated_ms - block_start_ms;
359 encoder_ms += block_encoded_ms - block_generated_ms;
360 decoder_ms += block_decoded_ms - block_encoded_ms;
361 }
362 printf("Player: time %5u ms, duty cycle %3u %%\n", player_ms, player_ms / audio_duration_in_seconds / 10);
363 printf("Encoder: time %5u ms, duty cycle %3u %%\n", encoder_ms, encoder_ms / audio_duration_in_seconds / 10);
364 printf("Decoder: time %5u ms, duty cycle %3u %%\n", decoder_ms, decoder_ms / audio_duration_in_seconds / 10);
365
366 #ifdef HAVE_POSIX_FILE_IO
367 wav_writer_close();
368 #endif
369 }
370
show_usage(void)371 static void show_usage(void){
372 printf("\n--- LC3 Encoder Test Console ---\n");
373 print_config();
374 printf("---\n");
375 printf("f - next sampling frequency\n");
376 printf("v - next codec variant\n");
377 printf("t - toggle sine / modplayer\n");
378 printf("p - simulated dropped frames\n");
379 #ifdef HAVE_LC3PLUS
380 printf("q - use LC3plus\n");
381 #endif
382 printf("s - start test\n");
383 printf("---\n");
384 }
385
stdin_process(char c)386 static void stdin_process(char c){
387 switch (c){
388 case 'p':
389 plc_dopped_frame_interval = 16 - plc_dopped_frame_interval;
390 print_config();
391 break;
392 #ifdef HAVE_LC3PLUS
393 case 'q':
394 use_lc3plus_decoder = true;
395 // enforce 10ms as 7.5ms is not supported
396 if ((menu_variant & 1) == 0){
397 menu_variant++;
398 }
399 print_config();
400 break;
401 #endif
402 case 'f':
403 menu_sampling_frequency++;
404 if (menu_sampling_frequency >= 6){
405 menu_sampling_frequency = 0;
406 }
407 if (menu_variant >= codec_configurations[menu_sampling_frequency].num_variants){
408 menu_variant = 0;
409 }
410 print_config();
411 break;
412 case 'v':
413 menu_variant++;
414 if (menu_variant >= codec_configurations[menu_sampling_frequency].num_variants){
415 menu_variant = 0;
416 }
417 print_config();
418 break;
419 case 's':
420 // use values from table
421 sampling_frequency_hz = codec_configurations[menu_sampling_frequency].samplingrate_hz;
422 octets_per_frame = codec_configurations[menu_sampling_frequency].variants[menu_variant].octets_per_frame;
423 frame_duration = codec_configurations[menu_sampling_frequency].variants[menu_variant].frame_duration;
424
425 // get num samples per frame
426 setup_lc3_encoder();
427 setup_lc3_decoder();
428
429 // setup mod player
430 setup_mod_player();
431
432 // setup sine generator
433 if (sampling_frequency_hz == 44100){
434 sine_step = 2;
435 } else {
436 sine_step = 96000 / sampling_frequency_hz;
437 }
438 test_encoder();
439 break;
440 case 't':
441 audio_source = 1 - audio_source;
442 print_config();
443 break;
444 case '\n':
445 case '\r':
446 break;
447 default:
448 show_usage();
449 break;
450 }
451 }
452
453 int btstack_main(int argc, const char * argv[]);
btstack_main(int argc,const char * argv[])454 int btstack_main(int argc, const char * argv[]){
455 (void) argv;
456 (void) argc;
457 btstack_stdin_setup(stdin_process);
458 // default config
459 menu_sampling_frequency = 5;
460 menu_variant = 4;
461 audio_source = AUDIO_SOURCE_SINE;
462 show_usage();
463 return 0;
464 }
465