xref: /btstack/test/le_audio/lc3_test.c (revision c824d78c0a34df89b57d535abafcc7dacf30bb06)
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 
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 
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 
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 
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 
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 
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 
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 
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[]);
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