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 static int audio_input_paused = 0; 105 static uint8_t audio_input_ring_buffer_storage[2*8000]; // full second input buffer 106 static btstack_ring_buffer_t audio_input_ring_buffer; 107 #endif 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_little_endian(unsigned int num_samples, uint8_t * data){ 140 unsigned int i; 141 for (i=0; i < num_samples; i++){ 142 int16_t sample = sine_int16_at_16000hz[phase]; 143 little_endian_store_16(data, i * 2, sample); 144 // ony use every second sample from 16khz table to get 8khz 145 phase += 2; 146 if (phase >= (sizeof(sine_int16_at_16000hz) / sizeof(int16_t))){ 147 phase = 0; 148 } 149 } 150 } 151 152 // 16 kHz samples for mSBC encoder in host endianess 153 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 154 static void sco_demo_sine_wave_int16_at_16000_hz_host_endian(unsigned int num_samples, int16_t * data){ 155 unsigned int i; 156 for (i=0; i < num_samples; i++){ 157 data[i] = sine_int16_at_16000hz[phase++]; 158 if (phase >= (sizeof(sine_int16_at_16000hz) / sizeof(int16_t))){ 159 phase = 0; 160 } 161 } 162 } 163 #endif 164 #endif 165 166 // Audio Playback / Recording 167 168 static void audio_playback_callback(int16_t * buffer, uint16_t num_samples){ 169 170 uint32_t prebuffer_bytes; 171 switch (negotiated_codec){ 172 case HFP_CODEC_MSBC: 173 prebuffer_bytes = MSBC_PA_PREBUFFER_BYTES; 174 break; 175 case HFP_CODEC_CVSD: 176 default: 177 prebuffer_bytes = CVSD_PA_PREBUFFER_BYTES; 178 break; 179 } 180 181 // fill with silence while paused 182 if (audio_output_paused){ 183 if (btstack_ring_buffer_bytes_available(&audio_output_ring_buffer) < prebuffer_bytes){ 184 memset(buffer, 0, num_samples * BYTES_PER_FRAME); 185 return; 186 } else { 187 // resume playback 188 audio_output_paused = 0; 189 } 190 } 191 192 // get data from ringbuffer 193 uint32_t bytes_read = 0; 194 btstack_ring_buffer_read(&audio_output_ring_buffer, (uint8_t *) buffer, num_samples * BYTES_PER_FRAME, &bytes_read); 195 num_samples -= bytes_read / BYTES_PER_FRAME; 196 buffer += bytes_read / BYTES_PER_FRAME; 197 198 // fill with 0 if not enough 199 if (num_samples){ 200 memset(buffer, 0, num_samples * BYTES_PER_FRAME); 201 audio_output_paused = 1; 202 } 203 } 204 205 #ifdef USE_AUDIO_INPUT 206 static void audio_recording_callback(const int16_t * buffer, uint16_t num_samples){ 207 btstack_ring_buffer_write(&audio_input_ring_buffer, (uint8_t *)buffer, num_samples * 2); 208 } 209 #endif 210 211 // return 1 if ok 212 static int audio_initialize(int sample_rate){ 213 214 // -- output -- // 215 216 // init buffers 217 memset(audio_output_ring_buffer_storage, 0, sizeof(audio_output_ring_buffer_storage)); 218 btstack_ring_buffer_init(&audio_output_ring_buffer, audio_output_ring_buffer_storage, sizeof(audio_output_ring_buffer_storage)); 219 220 // config and setup audio playback 221 const btstack_audio_sink_t * audio_sink = btstack_audio_sink_get_instance(); 222 if (!audio_sink) return 0; 223 224 audio_sink->init(1, sample_rate, &audio_playback_callback); 225 audio_sink->start_stream(); 226 227 audio_output_paused = 1; 228 229 // -- input -- // 230 231 #ifdef USE_AUDIO_INPUT 232 // init buffers 233 memset(audio_input_ring_buffer_storage, 0, sizeof(audio_input_ring_buffer_storage)); 234 btstack_ring_buffer_init(&audio_input_ring_buffer, audio_input_ring_buffer_storage, sizeof(audio_input_ring_buffer_storage)); 235 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 243 audio_input_paused = 1; 244 #endif 245 246 return 1; 247 } 248 249 static void audio_terminate(void){ 250 const btstack_audio_sink_t * audio_sink = btstack_audio_sink_get_instance(); 251 if (!audio_sink) return; 252 audio_sink->close(); 253 254 #ifdef USE_AUDIO_INPUT 255 const btstack_audio_source_t * audio_source= btstack_audio_source_get_instance(); 256 if (!audio_source) return; 257 audio_source->close(); 258 #endif 259 } 260 261 262 // CVSD - 8 kHz 263 264 static void sco_demo_init_CVSD(void){ 265 printf("SCO Demo: Init CVSD\n"); 266 267 btstack_cvsd_plc_init(&cvsd_plc_state); 268 269 #ifdef SCO_WAV_FILENAME 270 num_samples_to_write = CVSD_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS; 271 wav_writer_open(SCO_WAV_FILENAME, 1, CVSD_SAMPLE_RATE); 272 #endif 273 274 audio_initialize(CVSD_SAMPLE_RATE); 275 } 276 277 static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){ 278 279 int16_t audio_frame_out[128]; // 280 281 if (size > sizeof(audio_frame_out)){ 282 printf("sco_demo_receive_CVSD: SCO packet larger than local output buffer - dropping data.\n"); 283 return; 284 } 285 286 const int audio_bytes_read = size - 3; 287 const int num_samples = audio_bytes_read / BYTES_PER_FRAME; 288 289 // convert into host endian 290 int16_t audio_frame_in[128]; 291 int i; 292 for (i=0;i<num_samples;i++){ 293 audio_frame_in[i] = little_endian_read_16(packet, 3 + i * 2); 294 } 295 296 // treat packet as bad frame if controller does not report 'all good' 297 bool bad_frame = (packet[1] & 0x30) != 0; 298 299 btstack_cvsd_plc_process_data(&cvsd_plc_state, bad_frame, audio_frame_in, num_samples, audio_frame_out); 300 301 #ifdef SCO_WAV_FILENAME 302 // Samples in CVSD SCO packet are in little endian, ready for wav files (take shortcut) 303 const int samples_to_write = btstack_min(num_samples, num_samples_to_write); 304 wav_writer_write_le_int16(samples_to_write, audio_frame_out); 305 num_samples_to_write -= samples_to_write; 306 if (num_samples_to_write == 0){ 307 wav_writer_close(); 308 } 309 #endif 310 311 btstack_ring_buffer_write(&audio_output_ring_buffer, (uint8_t *)audio_frame_out, audio_bytes_read); 312 } 313 314 void sco_demo_fill_payload_CVSD(uint8_t * payload_buffer, uint16_t sco_payload_length){ 315 316 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 317 const int audio_samples_per_packet = sco_payload_length / BYTES_PER_FRAME; 318 sco_demo_sine_wave_int16_at_8000_hz_little_endian(audio_samples_per_packet, payload_buffer); 319 #endif 320 321 #if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE 322 if (btstack_audio_source_get_instance()){ 323 // CVSD 324 log_debug("send: bytes avail %u, free %u", btstack_ring_buffer_bytes_available(&audio_input_ring_buffer), btstack_ring_buffer_bytes_free(&audio_input_ring_buffer)); 325 // fill with silence while paused 326 int bytes_to_copy = sco_payload_length; 327 if (audio_input_paused){ 328 if (btstack_ring_buffer_bytes_available(&audio_input_ring_buffer) >= CVSD_PA_PREBUFFER_BYTES){ 329 // resume sending 330 audio_input_paused = 0; 331 } 332 } 333 334 // get data from ringbuffer 335 uint16_t pos = 0; 336 uint8_t * sample_data = payload_buffer; 337 if (!audio_input_paused){ 338 uint32_t bytes_read = 0; 339 btstack_ring_buffer_read(&audio_input_ring_buffer, sample_data, bytes_to_copy, &bytes_read); 340 // flip 16 on big endian systems 341 // @note We don't use (uint16_t *) casts since all sample addresses are odd which causes crahses on some systems 342 if (btstack_is_big_endian()){ 343 unsigned int i; 344 for (i=0;i<bytes_read;i+=2){ 345 uint8_t tmp = sample_data[i*2]; 346 sample_data[i*2] = sample_data[i*2+1]; 347 sample_data[i*2+1] = tmp; 348 } 349 } 350 bytes_to_copy -= bytes_read; 351 pos += bytes_read; 352 } 353 354 // fill with 0 if not enough 355 if (bytes_to_copy){ 356 memset(sample_data + pos, 0, bytes_to_copy); 357 audio_input_paused = 1; 358 } 359 } 360 else { 361 // just send '0's 362 memset(payload_buffer, 0, sco_payload_length); 363 } 364 #endif 365 } 366 367 // mSBC - 16 kHz 368 369 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 370 371 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 372 static void sco_demo_msbc_fill_sine_audio_frame(void){ 373 if (!hfp_msbc_can_encode_audio_frame_now()) return; 374 int num_samples = hfp_msbc_num_audio_samples_per_frame(); 375 if (num_samples > MAX_NUM_MSBC_SAMPLES) return; 376 int16_t sample_buffer[MAX_NUM_MSBC_SAMPLES]; 377 sco_demo_sine_wave_int16_at_16000_hz_host_endian(num_samples, sample_buffer); 378 hfp_msbc_encode_audio_frame(sample_buffer); 379 num_audio_frames++; 380 } 381 #endif 382 383 static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){ 384 UNUSED(context); 385 UNUSED(sample_rate); 386 UNUSED(data); 387 UNUSED(num_samples); 388 UNUSED(num_channels); 389 390 // samples in callback in host endianess, ready for playback 391 btstack_ring_buffer_write(&audio_output_ring_buffer, (uint8_t *)data, num_samples*num_channels*2); 392 393 #ifdef SCO_WAV_FILENAME 394 if (!num_samples_to_write) return; 395 num_samples = btstack_min(num_samples, num_samples_to_write); 396 num_samples_to_write -= num_samples; 397 wav_writer_write_int16(num_samples, data); 398 if (num_samples_to_write == 0){ 399 wav_writer_close(); 400 } 401 #endif /* SCO_WAV_FILENAME */ 402 } 403 404 static void sco_demo_init_mSBC(void){ 405 printf("SCO Demo: Init mSBC\n"); 406 407 btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL); 408 hfp_msbc_init(); 409 410 #ifdef SCO_WAV_FILENAME 411 num_samples_to_write = MSBC_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS; 412 wav_writer_open(SCO_WAV_FILENAME, 1, MSBC_SAMPLE_RATE); 413 #endif 414 415 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 416 sco_demo_msbc_fill_sine_audio_frame(); 417 #endif 418 419 audio_initialize(MSBC_SAMPLE_RATE); 420 } 421 422 static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){ 423 btstack_sbc_decoder_process_data(&decoder_state, (packet[1] >> 4) & 3, packet+3, size-3); 424 } 425 426 void sco_demo_fill_payload_mSBC(uint8_t * payload_buffer, uint16_t sco_payload_length){ 427 428 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 429 if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ 430 log_error("mSBC stream is empty."); 431 } 432 hfp_msbc_read_from_stream(payload_buffer, sco_payload_length); 433 sco_demo_msbc_fill_sine_audio_frame(); 434 #endif 435 436 #if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE 437 if (btstack_audio_source_get_instance()){ 438 // mSBC 439 if (audio_input_paused){ 440 if (btstack_ring_buffer_bytes_available(&audio_input_ring_buffer) >= MSBC_PA_PREBUFFER_BYTES){ 441 // resume sending 442 audio_input_paused = 0; 443 } 444 } 445 if (!audio_input_paused){ 446 int num_samples = hfp_msbc_num_audio_samples_per_frame(); 447 if (num_samples > MAX_NUM_MSBC_SAMPLES) return; // assert 448 if (hfp_msbc_can_encode_audio_frame_now() && btstack_ring_buffer_bytes_available(&audio_input_ring_buffer) >= (unsigned int)(num_samples * BYTES_PER_FRAME)){ 449 int16_t sample_buffer[MAX_NUM_MSBC_SAMPLES]; 450 uint32_t bytes_read; 451 btstack_ring_buffer_read(&audio_input_ring_buffer, (uint8_t*) sample_buffer, num_samples * BYTES_PER_FRAME, &bytes_read); 452 hfp_msbc_encode_audio_frame(sample_buffer); 453 num_audio_frames++; 454 } 455 if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ 456 log_error("mSBC stream should not be empty."); 457 } 458 } 459 } 460 if (audio_input_paused || hfp_msbc_num_bytes_in_stream() < sco_payload_length){ 461 // just send '0's 462 memset(payload_buffer, 0, sco_payload_length); 463 audio_input_paused = 1; 464 } else { 465 hfp_msbc_read_from_stream(payload_buffer, sco_payload_length); 466 } 467 #endif 468 } 469 470 #endif /* ENABLE_HFP_WIDE_BAND_SPEECH */ 471 472 void sco_demo_init(void){ 473 474 #ifdef ENABLE_CLASSIC_LEGACY_CONNECTIONS_FOR_SCO_DEMOS 475 printf("Disable BR/EDR Secure Connctions due to incompatibilities with SCO connections\n"); 476 gap_secure_connections_enable(false); 477 #endif 478 479 // status 480 #if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE 481 printf("SCO Demo: Sending and receiving audio via btstack_audio.\n"); 482 #endif 483 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 484 if (btstack_audio_sink_get_instance()){ 485 printf("SCO Demo: Sending sine wave, audio output via btstack_audio.\n"); 486 } else { 487 printf("SCO Demo: Sending sine wave, hexdump received data.\n"); 488 } 489 #endif 490 491 // Set SCO for CVSD (mSBC or other codecs automatically use 8-bit transparent mode) 492 hci_set_sco_voice_setting(0x60); // linear, unsigned, 16-bit, CVSD 493 } 494 495 void sco_demo_set_codec(uint8_t codec){ 496 if (negotiated_codec == codec) return; 497 negotiated_codec = codec; 498 499 switch (negotiated_codec){ 500 case HFP_CODEC_CVSD: 501 sco_demo_init_CVSD(); 502 break; 503 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 504 case HFP_CODEC_MSBC: 505 sco_demo_init_mSBC(); 506 break; 507 #endif 508 default: 509 btstack_assert(false); 510 break; 511 } 512 } 513 514 void sco_demo_receive(uint8_t * packet, uint16_t size){ 515 static uint32_t packets = 0; 516 static uint32_t crc_errors = 0; 517 static uint32_t data_received = 0; 518 static uint32_t byte_errors = 0; 519 520 count_received++; 521 522 data_received += size - 3; 523 packets++; 524 if (data_received > 100000){ 525 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); 526 crc_errors = 0; 527 byte_errors = 0; 528 data_received = 0; 529 packets = 0; 530 } 531 532 switch (negotiated_codec){ 533 case HFP_CODEC_CVSD: 534 sco_demo_receive_CVSD(packet, size); 535 break; 536 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 537 case HFP_CODEC_MSBC: 538 sco_demo_receive_mSBC(packet, size); 539 break; 540 #endif 541 default: 542 btstack_assert(false); 543 break; 544 } 545 } 546 547 void sco_demo_send(hci_con_handle_t sco_handle){ 548 549 if (sco_handle == HCI_CON_HANDLE_INVALID) return; 550 551 int sco_packet_length = hci_get_sco_packet_length(); 552 int sco_payload_length = sco_packet_length - 3; 553 554 hci_reserve_packet_buffer(); 555 uint8_t * sco_packet = hci_get_outgoing_packet_buffer(); 556 557 switch (negotiated_codec){ 558 case HFP_CODEC_CVSD: 559 sco_demo_fill_payload_CVSD(&sco_packet[3], sco_payload_length); 560 break; 561 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 562 case HFP_CODEC_MSBC: 563 sco_demo_fill_payload_mSBC(&sco_packet[3], sco_payload_length); 564 break; 565 #endif 566 default: 567 btstack_assert(false); 568 break; 569 } 570 571 // set handle + flags 572 little_endian_store_16(sco_packet, 0, sco_handle); 573 // set len 574 sco_packet[2] = sco_payload_length; 575 // finally send packet 576 hci_send_sco_packet_buffer(sco_packet_length); 577 578 // request another send event 579 hci_request_sco_can_send_now_event(); 580 581 count_sent++; 582 if ((count_sent % SCO_REPORT_PERIOD) == 0) { 583 printf("SCO: sent %u, received %u\n", count_sent, count_received); 584 } 585 } 586 587 void sco_demo_close(void){ 588 printf("SCO demo close\n"); 589 590 printf("SCO demo statistics: "); 591 switch (negotiated_codec){ 592 case HFP_CODEC_CVSD: 593 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); 594 break; 595 #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 596 case HFP_CODEC_MSBC: 597 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); 598 break; 599 #endif 600 default: 601 btstack_assert(false); 602 break; 603 } 604 605 negotiated_codec = -1; 606 607 #if defined(SCO_WAV_FILENAME) 608 wav_writer_close(); 609 #endif 610 611 audio_terminate(); 612 } 613