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