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