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