xref: /btstack/example/sco_demo_util.c (revision 6fb1424bd87188679df1bf16ba69aa3d2fc7d35a)
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