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 BLUEKITCHEN 24 * GMBH 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__ "sco_demo_util.c" 39 40 /* 41 * sco_demo_util.c - send/receive test data via SCO, used by hfp_*_demo and hsp_*_demo 42 */ 43 44 #include <stdio.h> 45 46 #include "sco_demo_util.h" 47 48 #include "btstack_audio.h" 49 #include "btstack_debug.h" 50 #include "btstack_ring_buffer.h" 51 #include "classic/btstack_cvsd_plc.h" 52 #include "classic/btstack_sbc.h" 53 #include "classic/hfp.h" 54 #include "classic/hfp_msbc.h" 55 56 #ifdef _MSC_VER 57 // ignore deprecated warning for fopen 58 #pragma warning(disable : 4996) 59 #endif 60 61 #ifdef HAVE_POSIX_FILE_IO 62 #include "wav_util.h" 63 #endif 64 65 // test modes 66 #define SCO_DEMO_MODE_SINE 0 67 #define SCO_DEMO_MODE_MICROPHONE 1 68 69 // SCO demo configuration 70 #define SCO_DEMO_MODE SCO_DEMO_MODE_MICROPHONE 71 72 // number of sco packets until 'report' on console 73 #define SCO_REPORT_PERIOD 100 74 75 76 #ifdef HAVE_POSIX_FILE_IO 77 // length and name of wav file on disk 78 #define SCO_WAV_DURATION_IN_SECONDS 15 79 #define SCO_WAV_FILENAME "sco_input.wav" 80 #endif 81 82 83 // pre-buffer for CVSD and mSBC - also defines latency 84 #define SCO_CVSD_PA_PREBUFFER_MS 50 85 #define SCO_MSBC_PA_PREBUFFER_MS 50 86 87 // constants 88 #define NUM_CHANNELS 1 89 #define CVSD_SAMPLE_RATE 8000 90 #define MSBC_SAMPLE_RATE 16000 91 #define BYTES_PER_FRAME 2 92 93 #define CVSD_PA_PREBUFFER_BYTES (SCO_CVSD_PA_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * BYTES_PER_FRAME) 94 #define MSBC_PA_PREBUFFER_BYTES (SCO_MSBC_PA_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * BYTES_PER_FRAME) 95 96 // output 97 static int audio_output_paused = 0; 98 static uint8_t audio_output_ring_buffer_storage[2*MSBC_PA_PREBUFFER_BYTES]; 99 static btstack_ring_buffer_t audio_output_ring_buffer; 100 101 // input 102 #if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE 103 #define USE_AUDIO_INPUT 104 #endif 105 static int audio_input_paused = 0; 106 static uint8_t audio_input_ring_buffer_storage[2*8000]; // full second input buffer 107 static btstack_ring_buffer_t audio_input_ring_buffer; 108 109 static int count_sent = 0; 110 static int count_received = 0; 111 static int negotiated_codec = -1; 112 113 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 114 static btstack_sbc_decoder_state_t decoder_state; 115 #endif 116 117 static btstack_cvsd_plc_state_t cvsd_plc_state; 118 119 #define MAX_NUM_MSBC_SAMPLES (16*8) 120 121 int num_samples_to_write; 122 int num_audio_frames; 123 124 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 125 126 unsigned int phase; 127 128 // input signal: pre-computed sine wave, 266 Hz at 16000 kHz 129 static const int16_t sine_int16_at_16000hz[] = { 130 0, 3135, 6237, 9270, 12202, 14999, 17633, 20073, 22294, 24270, 131 25980, 27406, 28531, 29344, 29835, 30000, 29835, 29344, 28531, 27406, 132 25980, 24270, 22294, 20073, 17633, 14999, 12202, 9270, 6237, 3135, 133 0, -3135, -6237, -9270, -12202, -14999, -17633, -20073, -22294, -24270, 134 -25980, -27406, -28531, -29344, -29835, -30000, -29835, -29344, -28531, -27406, 135 -25980, -24270, -22294, -20073, -17633, -14999, -12202, -9270, -6237, -3135, 136 }; 137 138 // 8 kHz samples for CVSD/SCO packets in little endian 139 static void sco_demo_sine_wave_int16_at_8000_hz_host_endian(unsigned int num_samples, int16_t * data){ 140 unsigned int i; 141 for (i=0; i < num_samples; i++){ 142 data[i] = sine_int16_at_16000hz[phase]; 143 // ony use every second sample from 16khz table to get 8khz 144 phase += 2; 145 if (phase >= (sizeof(sine_int16_at_16000hz) / sizeof(int16_t))){ 146 phase = 0; 147 } 148 } 149 } 150 151 // 16 kHz samples for mSBC encoder in host endianess 152 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 153 static void sco_demo_sine_wave_int16_at_16000_hz_host_endian(unsigned int num_samples, int16_t * data){ 154 unsigned int i; 155 for (i=0; i < num_samples; i++){ 156 data[i] = sine_int16_at_16000hz[phase++]; 157 if (phase >= (sizeof(sine_int16_at_16000hz) / sizeof(int16_t))){ 158 phase = 0; 159 } 160 } 161 } 162 #endif 163 #endif 164 165 // Audio Playback / Recording 166 167 static void audio_playback_callback(int16_t * buffer, uint16_t num_samples){ 168 169 uint32_t prebuffer_bytes; 170 switch (negotiated_codec){ 171 case HFP_CODEC_MSBC: 172 prebuffer_bytes = MSBC_PA_PREBUFFER_BYTES; 173 break; 174 case HFP_CODEC_CVSD: 175 default: 176 prebuffer_bytes = CVSD_PA_PREBUFFER_BYTES; 177 break; 178 } 179 180 // fill with silence while paused 181 if (audio_output_paused){ 182 if (btstack_ring_buffer_bytes_available(&audio_output_ring_buffer) < prebuffer_bytes){ 183 memset(buffer, 0, num_samples * BYTES_PER_FRAME); 184 return; 185 } else { 186 // resume playback 187 audio_output_paused = 0; 188 } 189 } 190 191 // get data from ringbuffer 192 uint32_t bytes_read = 0; 193 btstack_ring_buffer_read(&audio_output_ring_buffer, (uint8_t *) buffer, num_samples * BYTES_PER_FRAME, &bytes_read); 194 num_samples -= bytes_read / BYTES_PER_FRAME; 195 buffer += bytes_read / BYTES_PER_FRAME; 196 197 // fill with 0 if not enough 198 if (num_samples){ 199 memset(buffer, 0, num_samples * BYTES_PER_FRAME); 200 audio_output_paused = 1; 201 } 202 } 203 204 #ifdef USE_AUDIO_INPUT 205 static void audio_recording_callback(const int16_t * buffer, uint16_t num_samples){ 206 btstack_ring_buffer_write(&audio_input_ring_buffer, (uint8_t *)buffer, num_samples * 2); 207 } 208 #endif 209 210 // return 1 if ok 211 static int audio_initialize(int sample_rate){ 212 213 // -- output -- // 214 215 // init buffers 216 memset(audio_output_ring_buffer_storage, 0, sizeof(audio_output_ring_buffer_storage)); 217 btstack_ring_buffer_init(&audio_output_ring_buffer, audio_output_ring_buffer_storage, sizeof(audio_output_ring_buffer_storage)); 218 219 // config and setup audio playback 220 const btstack_audio_sink_t * audio_sink = btstack_audio_sink_get_instance(); 221 if (!audio_sink) return 0; 222 223 audio_sink->init(1, sample_rate, &audio_playback_callback); 224 audio_sink->start_stream(); 225 226 audio_output_paused = 1; 227 228 // -- input -- // 229 230 // init buffers 231 memset(audio_input_ring_buffer_storage, 0, sizeof(audio_input_ring_buffer_storage)); 232 btstack_ring_buffer_init(&audio_input_ring_buffer, audio_input_ring_buffer_storage, sizeof(audio_input_ring_buffer_storage)); 233 audio_input_paused = 1; 234 235 #ifdef USE_AUDIO_INPUT 236 // config and setup audio recording 237 const btstack_audio_source_t * audio_source = btstack_audio_source_get_instance(); 238 if (!audio_source) return 0; 239 240 audio_source->init(1, sample_rate, &audio_recording_callback); 241 audio_source->start_stream(); 242 #endif 243 244 return 1; 245 } 246 247 static void audio_terminate(void){ 248 const btstack_audio_sink_t * audio_sink = btstack_audio_sink_get_instance(); 249 if (!audio_sink) return; 250 audio_sink->close(); 251 252 #ifdef USE_AUDIO_INPUT 253 const btstack_audio_source_t * audio_source= btstack_audio_source_get_instance(); 254 if (!audio_source) return; 255 audio_source->close(); 256 #endif 257 } 258 259 260 // CVSD - 8 kHz 261 262 static void sco_demo_init_CVSD(void){ 263 printf("SCO Demo: Init CVSD\n"); 264 265 btstack_cvsd_plc_init(&cvsd_plc_state); 266 267 #ifdef SCO_WAV_FILENAME 268 num_samples_to_write = CVSD_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS; 269 wav_writer_open(SCO_WAV_FILENAME, 1, CVSD_SAMPLE_RATE); 270 #endif 271 272 audio_initialize(CVSD_SAMPLE_RATE); 273 } 274 275 static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){ 276 277 int16_t audio_frame_out[128]; // 278 279 if (size > sizeof(audio_frame_out)){ 280 printf("sco_demo_receive_CVSD: SCO packet larger than local output buffer - dropping data.\n"); 281 return; 282 } 283 284 const int audio_bytes_read = size - 3; 285 const int num_samples = audio_bytes_read / BYTES_PER_FRAME; 286 287 // convert into host endian 288 int16_t audio_frame_in[128]; 289 int i; 290 for (i=0;i<num_samples;i++){ 291 audio_frame_in[i] = little_endian_read_16(packet, 3 + i * 2); 292 } 293 294 // treat packet as bad frame if controller does not report 'all good' 295 bool bad_frame = (packet[1] & 0x30) != 0; 296 297 btstack_cvsd_plc_process_data(&cvsd_plc_state, bad_frame, audio_frame_in, num_samples, audio_frame_out); 298 299 #ifdef SCO_WAV_FILENAME 300 // Samples in CVSD SCO packet are in little endian, ready for wav files (take shortcut) 301 const int samples_to_write = btstack_min(num_samples, num_samples_to_write); 302 wav_writer_write_le_int16(samples_to_write, audio_frame_out); 303 num_samples_to_write -= samples_to_write; 304 if (num_samples_to_write == 0){ 305 wav_writer_close(); 306 } 307 #endif 308 309 btstack_ring_buffer_write(&audio_output_ring_buffer, (uint8_t *)audio_frame_out, audio_bytes_read); 310 } 311 312 void sco_demo_fill_payload_CVSD(uint8_t * payload_buffer, uint16_t sco_payload_length){ 313 314 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 315 #define REFILL_SAMPLES 16 316 // re-fill with sine 317 uint16_t samples_free = btstack_ring_buffer_bytes_free(&audio_input_ring_buffer) / 2; 318 while (samples_free > 0){ 319 int16_t samples_buffer[REFILL_SAMPLES]; 320 uint16_t samples_to_add = btstack_min(samples_free, REFILL_SAMPLES); 321 sco_demo_sine_wave_int16_at_8000_hz_host_endian(samples_to_add, samples_buffer); 322 btstack_ring_buffer_write(&audio_input_ring_buffer, (uint8_t *)samples_buffer, samples_to_add * 2); 323 samples_free -= samples_to_add; 324 } 325 #endif 326 327 // resume if pre-buffer is filled 328 if (audio_input_paused){ 329 if (btstack_ring_buffer_bytes_available(&audio_input_ring_buffer) >= CVSD_PA_PREBUFFER_BYTES){ 330 // resume sending 331 audio_input_paused = 0; 332 } 333 } 334 335 uint16_t bytes_to_copy = sco_payload_length; 336 337 // get data from ringbuffer 338 uint16_t pos = 0; 339 if (!audio_input_paused){ 340 uint16_t samples_to_copy = sco_payload_length / 2; 341 uint32_t bytes_read = 0; 342 btstack_ring_buffer_read(&audio_input_ring_buffer, payload_buffer, bytes_to_copy, &bytes_read); 343 // flip 16 on big endian systems 344 // @note We don't use (uint16_t *) casts since all sample addresses are odd which causes crahses on some systems 345 if (btstack_is_big_endian()){ 346 uint16_t i; 347 for (i=0;i<samples_to_copy/2;i+=2){ 348 uint8_t tmp = payload_buffer[i*2]; 349 payload_buffer[i*2] = payload_buffer[i*2+1]; 350 payload_buffer[i*2+1] = tmp; 351 } 352 } 353 bytes_to_copy -= bytes_read; 354 pos += bytes_read; 355 } 356 357 // fill with 0 if not enough 358 if (bytes_to_copy){ 359 memset(payload_buffer + pos, 0, bytes_to_copy); 360 audio_input_paused = 1; 361 } 362 } 363 364 // mSBC - 16 kHz 365 366 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 367 368 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 369 static void sco_demo_msbc_fill_sine_audio_frame(void){ 370 if (!hfp_msbc_can_encode_audio_frame_now()) return; 371 int num_samples = hfp_msbc_num_audio_samples_per_frame(); 372 if (num_samples > MAX_NUM_MSBC_SAMPLES) return; 373 int16_t sample_buffer[MAX_NUM_MSBC_SAMPLES]; 374 sco_demo_sine_wave_int16_at_16000_hz_host_endian(num_samples, sample_buffer); 375 hfp_msbc_encode_audio_frame(sample_buffer); 376 num_audio_frames++; 377 } 378 #endif 379 380 static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){ 381 UNUSED(context); 382 UNUSED(sample_rate); 383 UNUSED(data); 384 UNUSED(num_samples); 385 UNUSED(num_channels); 386 387 // samples in callback in host endianess, ready for playback 388 btstack_ring_buffer_write(&audio_output_ring_buffer, (uint8_t *)data, num_samples*num_channels*2); 389 390 #ifdef SCO_WAV_FILENAME 391 if (!num_samples_to_write) return; 392 num_samples = btstack_min(num_samples, num_samples_to_write); 393 num_samples_to_write -= num_samples; 394 wav_writer_write_int16(num_samples, data); 395 if (num_samples_to_write == 0){ 396 wav_writer_close(); 397 } 398 #endif /* SCO_WAV_FILENAME */ 399 } 400 401 static void sco_demo_init_mSBC(void){ 402 printf("SCO Demo: Init mSBC\n"); 403 404 btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL); 405 hfp_msbc_init(); 406 407 #ifdef SCO_WAV_FILENAME 408 num_samples_to_write = MSBC_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS; 409 wav_writer_open(SCO_WAV_FILENAME, 1, MSBC_SAMPLE_RATE); 410 #endif 411 412 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 413 sco_demo_msbc_fill_sine_audio_frame(); 414 #endif 415 416 audio_initialize(MSBC_SAMPLE_RATE); 417 } 418 419 static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){ 420 btstack_sbc_decoder_process_data(&decoder_state, (packet[1] >> 4) & 3, packet+3, size-3); 421 } 422 423 void sco_demo_fill_payload_mSBC(uint8_t * payload_buffer, uint16_t sco_payload_length){ 424 425 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 426 if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ 427 log_error("mSBC stream is empty."); 428 } 429 hfp_msbc_read_from_stream(payload_buffer, sco_payload_length); 430 sco_demo_msbc_fill_sine_audio_frame(); 431 #endif 432 433 #if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE 434 if (btstack_audio_source_get_instance()){ 435 // mSBC 436 if (audio_input_paused){ 437 if (btstack_ring_buffer_bytes_available(&audio_input_ring_buffer) >= MSBC_PA_PREBUFFER_BYTES){ 438 // resume sending 439 audio_input_paused = 0; 440 } 441 } 442 if (!audio_input_paused){ 443 int num_samples = hfp_msbc_num_audio_samples_per_frame(); 444 if (num_samples > MAX_NUM_MSBC_SAMPLES) return; // assert 445 if (hfp_msbc_can_encode_audio_frame_now() && btstack_ring_buffer_bytes_available(&audio_input_ring_buffer) >= (unsigned int)(num_samples * BYTES_PER_FRAME)){ 446 int16_t sample_buffer[MAX_NUM_MSBC_SAMPLES]; 447 uint32_t bytes_read; 448 btstack_ring_buffer_read(&audio_input_ring_buffer, (uint8_t*) sample_buffer, num_samples * BYTES_PER_FRAME, &bytes_read); 449 hfp_msbc_encode_audio_frame(sample_buffer); 450 num_audio_frames++; 451 } 452 if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ 453 log_error("mSBC stream should not be empty."); 454 } 455 } 456 } 457 if (audio_input_paused || hfp_msbc_num_bytes_in_stream() < sco_payload_length){ 458 // just send '0's 459 memset(payload_buffer, 0, sco_payload_length); 460 audio_input_paused = 1; 461 } else { 462 hfp_msbc_read_from_stream(payload_buffer, sco_payload_length); 463 } 464 #endif 465 } 466 467 #endif /* ENABLE_HFP_WIDE_BAND_SPEECH */ 468 469 void sco_demo_init(void){ 470 471 #ifdef ENABLE_CLASSIC_LEGACY_CONNECTIONS_FOR_SCO_DEMOS 472 printf("Disable BR/EDR Secure Connctions due to incompatibilities with SCO connections\n"); 473 gap_secure_connections_enable(false); 474 #endif 475 476 // status 477 #if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE 478 printf("SCO Demo: Sending and receiving audio via btstack_audio.\n"); 479 #endif 480 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 481 if (btstack_audio_sink_get_instance()){ 482 printf("SCO Demo: Sending sine wave, audio output via btstack_audio.\n"); 483 } else { 484 printf("SCO Demo: Sending sine wave, hexdump received data.\n"); 485 } 486 #endif 487 488 // Set SCO for CVSD (mSBC or other codecs automatically use 8-bit transparent mode) 489 hci_set_sco_voice_setting(0x60); // linear, unsigned, 16-bit, CVSD 490 } 491 492 void sco_demo_set_codec(uint8_t codec){ 493 if (negotiated_codec == codec) return; 494 negotiated_codec = codec; 495 496 switch (negotiated_codec){ 497 case HFP_CODEC_CVSD: 498 sco_demo_init_CVSD(); 499 break; 500 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 501 case HFP_CODEC_MSBC: 502 sco_demo_init_mSBC(); 503 break; 504 #endif 505 default: 506 btstack_assert(false); 507 break; 508 } 509 } 510 511 void sco_demo_receive(uint8_t * packet, uint16_t size){ 512 static uint32_t packets = 0; 513 static uint32_t crc_errors = 0; 514 static uint32_t data_received = 0; 515 static uint32_t byte_errors = 0; 516 517 count_received++; 518 519 data_received += size - 3; 520 packets++; 521 if (data_received > 100000){ 522 printf("Summary: data %07u, packets %04u, packet with crc errors %0u, byte errors %04u\n", (unsigned int) data_received, (unsigned int) packets, (unsigned int) crc_errors, (unsigned int) byte_errors); 523 crc_errors = 0; 524 byte_errors = 0; 525 data_received = 0; 526 packets = 0; 527 } 528 529 switch (negotiated_codec){ 530 case HFP_CODEC_CVSD: 531 sco_demo_receive_CVSD(packet, size); 532 break; 533 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 534 case HFP_CODEC_MSBC: 535 sco_demo_receive_mSBC(packet, size); 536 break; 537 #endif 538 default: 539 btstack_assert(false); 540 break; 541 } 542 } 543 544 void sco_demo_send(hci_con_handle_t sco_handle){ 545 546 if (sco_handle == HCI_CON_HANDLE_INVALID) return; 547 548 int sco_packet_length = hci_get_sco_packet_length(); 549 int sco_payload_length = sco_packet_length - 3; 550 551 hci_reserve_packet_buffer(); 552 uint8_t * sco_packet = hci_get_outgoing_packet_buffer(); 553 554 switch (negotiated_codec){ 555 case HFP_CODEC_CVSD: 556 sco_demo_fill_payload_CVSD(&sco_packet[3], sco_payload_length); 557 break; 558 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 559 case HFP_CODEC_MSBC: 560 sco_demo_fill_payload_mSBC(&sco_packet[3], sco_payload_length); 561 break; 562 #endif 563 default: 564 btstack_assert(false); 565 break; 566 } 567 568 // set handle + flags 569 little_endian_store_16(sco_packet, 0, sco_handle); 570 // set len 571 sco_packet[2] = sco_payload_length; 572 // finally send packet 573 hci_send_sco_packet_buffer(sco_packet_length); 574 575 // request another send event 576 hci_request_sco_can_send_now_event(); 577 578 count_sent++; 579 if ((count_sent % SCO_REPORT_PERIOD) == 0) { 580 printf("SCO: sent %u, received %u\n", count_sent, count_received); 581 } 582 } 583 584 void sco_demo_close(void){ 585 printf("SCO demo close\n"); 586 587 printf("SCO demo statistics: "); 588 switch (negotiated_codec){ 589 case HFP_CODEC_CVSD: 590 printf("Used CVSD with PLC, number of proccesed frames: \n - %d good frames, \n - %d bad frames.\n", cvsd_plc_state.good_frames_nr, cvsd_plc_state.bad_frames_nr); 591 break; 592 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 593 case HFP_CODEC_MSBC: 594 printf("Used mSBC with PLC, number of processed frames: \n - %d good frames, \n - %d zero frames, \n - %d bad frames.\n", decoder_state.good_frames_nr, decoder_state.zero_frames_nr, decoder_state.bad_frames_nr); 595 break; 596 #endif 597 default: 598 btstack_assert(false); 599 break; 600 } 601 602 negotiated_codec = -1; 603 604 #if defined(SCO_WAV_FILENAME) 605 wav_writer_close(); 606 #endif 607 608 audio_terminate(); 609 } 610