xref: /btstack/example/sco_demo_util.c (revision 202da3172ef8f2db9dda12110ca38310dbc3bb75)
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 MATTHIAS
24  * RINGWALD 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 /*
39  * sco_demo_util.c - send/receive test data via SCO, used by hfp_*_demo and hsp_*_demo
40  */
41 
42 
43 #include <stdio.h>
44 
45 #include "sco_demo_util.h"
46 #include "btstack_debug.h"
47 #include "btstack_sbc.h"
48 #include "btstack_cvsd_plc.h"
49 #include "hfp_msbc.h"
50 #include "hfp.h"
51 
52 // configure test mode
53 #define SCO_DEMO_MODE_SINE		0
54 #define SCO_DEMO_MODE_ASCII		1
55 #define SCO_DEMO_MODE_COUNTER	2
56 
57 
58 // SCO demo configuration
59 #define SCO_DEMO_MODE SCO_DEMO_MODE_SINE
60 #define SCO_REPORT_PERIOD 100
61 
62 #ifdef HAVE_POSIX_FILE_IO
63 #define SCO_WAV_FILENAME      "sco_input.wav"
64 #define SCO_MSBC_OUT_FILENAME "sco_output.msbc"
65 #define SCO_MSBC_IN_FILENAME  "sco_input.mscb"
66 
67 #define SCO_WAV_DURATION_IN_SECONDS 15
68 #endif
69 
70 
71 #if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE)
72 #define USE_PORTAUDIO
73 #endif
74 
75 #ifdef USE_PORTAUDIO
76 #include <portaudio.h>
77 // portaudio config
78 #define NUM_CHANNELS 1
79 #define SAMPLE_RATE 8000
80 #define FRAMES_PER_BUFFER 24
81 #define PA_SAMPLE_TYPE paInt8
82 // portaudio globals
83 static  PaStream * stream;
84 #endif
85 
86 typedef struct wav_writer_state {
87     FILE * wav_file;
88     int total_num_samples;
89     int frame_count;
90 } wav_writer_state_t;
91 
92 static int dump_data = 1;
93 
94 static int phase = 0;
95 static int count_sent = 0;
96 static int count_received = 0;
97 static uint8_t negotiated_codec = 0;
98 static int num_audio_frames = 0;
99 
100 FILE * msbc_file_in;
101 FILE * msbc_file_out;
102 
103 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
104 
105 // input signal: pre-computed sine wave, 160 Hz at 8 kHz
106 static const uint8_t sine[] = {
107       0,  15,  31,  46,  61,  74,  86,  97, 107, 114,
108     120, 124, 126, 126, 124, 120, 114, 107,  97,  86,
109      74,  61,  46,  31,  15,   0, 241, 225, 210, 195,
110     182, 170, 159, 149, 142, 136, 132, 130, 130, 132,
111     136, 142, 149, 159, 170, 182, 195, 210, 225, 241,
112 };
113 
114 // input signal: pre-computed sine wave, 160 Hz at 16000 kHz
115 static const int16_t sine_int16[] = {
116      0,    2057,    4107,    6140,    8149,   10126,   12062,   13952,   15786,   17557,
117  19260,   20886,   22431,   23886,   25247,   26509,   27666,   28714,   29648,   30466,
118  31163,   31738,   32187,   32509,   32702,   32767,   32702,   32509,   32187,   31738,
119  31163,   30466,   29648,   28714,   27666,   26509,   25247,   23886,   22431,   20886,
120  19260,   17557,   15786,   13952,   12062,   10126,    8149,    6140,    4107,    2057,
121      0,   -2057,   -4107,   -6140,   -8149,  -10126,  -12062,  -13952,  -15786,  -17557,
122 -19260,  -20886,  -22431,  -23886,  -25247,  -26509,  -27666,  -28714,  -29648,  -30466,
123 -31163,  -31738,  -32187,  -32509,  -32702,  -32767,  -32702,  -32509,  -32187,  -31738,
124 -31163,  -30466,  -29648,  -28714,  -27666,  -26509,  -25247,  -23886,  -22431,  -20886,
125 -19260,  -17557,  -15786,  -13952,  -12062,  -10126,   -8149,   -6140,   -4107,   -2057,
126 };
127 
128 #ifdef SCO_WAV_FILENAME
129 
130 static int num_samples_to_write;
131 static wav_writer_state_t wav_writer_state;
132 
133 static btstack_sbc_decoder_state_t decoder_state;
134 static btstack_cvsd_plc_state_t cvsd_plc_state;
135 
136 static void little_endian_fstore_16(FILE * file, uint16_t value){
137     uint8_t buf[2];
138     little_endian_store_32(buf, 0, value);
139     fwrite(&buf, 1, 2, file);
140 }
141 
142 static void little_endian_fstore_32(FILE * file, uint32_t value){
143     uint8_t buf[4];
144     little_endian_store_32(buf, 0, value);
145     fwrite(&buf, 1, 4, file);
146 }
147 
148 static FILE * wav_init(const char * filename){
149     FILE * f = fopen(filename, "wb");
150     printf("SCO Demo: creating wav file %s, %p\n", filename, f);
151     return f;
152 }
153 
154 static void write_wav_header(FILE * file, int sample_rate, int num_channels, int num_samples, int bytes_per_sample){
155     /* write RIFF header */
156     fwrite("RIFF", 1, 4, file);
157     // num_samples = blocks * subbands
158     uint32_t data_bytes = (uint32_t) (bytes_per_sample * num_samples * num_channels);
159     little_endian_fstore_32(file, data_bytes + 36);
160     fwrite("WAVE", 1, 4, file);
161 
162     int byte_rate = sample_rate * num_channels * bytes_per_sample;
163     int bits_per_sample = 8 * bytes_per_sample;
164     int block_align = num_channels * bits_per_sample;
165     int fmt_length = 16;
166     int fmt_format_tag = 1; // PCM
167 
168     /* write fmt chunk */
169     fwrite("fmt ", 1, 4, file);
170     little_endian_fstore_32(file, fmt_length);
171     little_endian_fstore_16(file, fmt_format_tag);
172     little_endian_fstore_16(file, num_channels);
173     little_endian_fstore_32(file, sample_rate);
174     little_endian_fstore_32(file, byte_rate);
175     little_endian_fstore_16(file, block_align);
176     little_endian_fstore_16(file, bits_per_sample);
177 
178     /* write data chunk */
179     fwrite("data", 1, 4, file);
180     little_endian_fstore_32(file, data_bytes);
181 }
182 
183 static void write_wav_data_uint8(FILE * file, unsigned long num_samples, uint8_t * data){
184     fwrite(data, num_samples, 1, file);
185 }
186 
187 static void write_wav_data_int16(FILE * file, int num_samples, int16_t * data){
188     fwrite(data, num_samples, 2, file);
189 }
190 
191 static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){
192     log_info("handle_pcm_data num samples %u / %u", num_samples, num_samples_to_write);
193     if (!num_samples_to_write) return;
194 
195     wav_writer_state_t * writer_state = (wav_writer_state_t*) context;
196     num_samples = btstack_min(num_samples, num_samples_to_write);
197     num_samples_to_write -= num_samples;
198 
199     write_wav_data_int16(writer_state->wav_file, num_samples, data);
200     writer_state->total_num_samples+=num_samples;
201     writer_state->frame_count++;
202 
203     if (num_samples_to_write == 0){
204         sco_demo_close();
205     }
206 }
207 
208 static void sco_demo_fill_audio_frame(void){
209     if (!hfp_msbc_can_encode_audio_frame_now()) return;
210     int i;
211     int16_t sample_buffer[8*16*2];
212     for (i=0; i < hfp_msbc_num_audio_samples_per_frame(); i++){
213         sample_buffer[i] = sine_int16[phase++];
214         if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){
215             phase = 0;
216         }
217     }
218     hfp_msbc_encode_audio_frame(sample_buffer);
219     num_audio_frames++;
220 }
221 
222 static void sco_demo_init_mSBC(void){
223     wav_writer_state.wav_file = wav_init(SCO_WAV_FILENAME);
224     wav_writer_state.frame_count = 0;
225     wav_writer_state.total_num_samples = 0;
226 
227     btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, (void*)&wav_writer_state);
228 
229     const int sample_rate = 16000;
230     const int num_samples = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
231     const int bytes_per_sample = 2;
232     const int num_channels = 1;
233     num_samples_to_write = num_samples;
234 
235     write_wav_header(wav_writer_state.wav_file, sample_rate, num_channels, num_samples, bytes_per_sample);
236 
237     hfp_msbc_init();
238     sco_demo_fill_audio_frame();
239 
240 #ifdef SCO_MSBC_IN_FILENAME
241     msbc_file_in = fopen(SCO_MSBC_IN_FILENAME, "wb");
242     printf("SCO Demo: creating mSBC in file %s, %p\n", SCO_MSBC_IN_FILENAME, msbc_file_in);
243 #endif
244 #ifdef SCO_MSBC_OUT_FILENAME
245     msbc_file_out = fopen(SCO_MSBC_OUT_FILENAME, "wb");
246     printf("SCO Demo: creating mSBC out file %s, %p\n", SCO_MSBC_OUT_FILENAME, msbc_file_out);
247 #endif
248 }
249 
250 static void sco_demo_init_CVSD(void){
251     btstack_cvsd_plc_init(&cvsd_plc_state);
252     wav_writer_state.wav_file = wav_init(SCO_WAV_FILENAME);
253     wav_writer_state.frame_count = 0;
254     wav_writer_state.total_num_samples = 0;
255 
256     const int sample_rate = 8000;
257     const int num_samples = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
258     const int num_channels = 1;
259     const int bytes_per_sample = 1;
260     num_samples_to_write = num_samples;
261     write_wav_header(wav_writer_state.wav_file, sample_rate, num_channels, num_samples, bytes_per_sample);
262 }
263 
264 static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){
265     if (num_samples_to_write){
266         if (msbc_file_in){
267             // log incoming mSBC data for testing
268             fwrite(packet+3, size-3, 1, msbc_file_in);
269         }
270         btstack_sbc_decoder_process_data(&decoder_state, (packet[1] >> 4) & 3, packet+3, size-3);
271         dump_data = 0;
272     }
273 }
274 
275 static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){
276     if (num_samples_to_write){
277         const int num_samples = size - 3;
278         const int samples_to_write = btstack_min(num_samples, num_samples_to_write);
279         int8_t audio_frame_out[24];
280 
281         memcpy(audio_frame_out, (int8_t*)(packet+3), 24);
282         // btstack_cvsd_plc_process_data(&cvsd_plc_state, (int8_t *)(packet+3),num_samples,audio_frame_out);
283 
284         // convert 8 bit signed to 8 bit unsigned
285         int i;
286         for (i=0;i<samples_to_write;i++){
287             audio_frame_out[i] += 128;
288         }
289 
290         wav_writer_state_t * writer_state = &wav_writer_state;
291         write_wav_data_uint8(writer_state->wav_file, samples_to_write, (uint8_t*)audio_frame_out);
292         num_samples_to_write -= samples_to_write;
293         if (num_samples_to_write == 0){
294             sco_demo_close();
295         }
296         dump_data = 0;
297 
298         // convert back
299         for (i=0;i<samples_to_write;i++){
300             audio_frame_out[i] += 128;
301         }
302     }
303 }
304 
305 #endif
306 #endif
307 
308 void sco_demo_close(void){
309 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
310 #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
311     printf("SCO demo statistics: ");
312     if (negotiated_codec == HFP_CODEC_MSBC){
313         printf("Used mSBC with PLC, number of processed frames: \n - %d good frames, \n - %d zero frames, \n - %d bad frames.", decoder_state.good_frames_nr, decoder_state.zero_frames_nr, decoder_state.bad_frames_nr);
314     } else {
315         printf("Used CVSD with PLC, number of proccesed frames: \n - %d good frames, \n - %d bad frames.", cvsd_plc_state.good_frames_nr, cvsd_plc_state.bad_frames_nr);
316     }
317 #endif
318 #endif
319 
320 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
321 #ifdef SCO_WAV_FILENAME
322 
323 #if 0
324     printf("SCO Demo: closing wav file\n");
325     if (negotiated_codec == HFP_CODEC_MSBC){
326         wav_writer_state_t * writer_state = &wav_writer_state;
327         if (!writer_state->wav_file) return;
328         rewind(writer_state->wav_file);
329         write_wav_header(writer_state->wav_file, writer_state->total_num_samples, btstack_sbc_decoder_num_channels(&decoder_state), btstack_sbc_decoder_sample_rate(&decoder_state),2);
330         fclose(writer_state->wav_file);
331         writer_state->wav_file = NULL;
332     }
333 #endif
334 #endif
335 #endif
336 }
337 
338 void sco_demo_set_codec(uint8_t codec){
339     if (negotiated_codec == codec) return;
340     negotiated_codec = codec;
341 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
342 #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
343     if (negotiated_codec == HFP_CODEC_MSBC){
344         sco_demo_init_mSBC();
345     } else {
346         sco_demo_init_CVSD();
347     }
348 #endif
349 #endif
350 }
351 
352 void sco_demo_init(void){
353 
354 	// status
355 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
356 #ifdef HAVE_PORTAUDIO
357 	printf("SCO Demo: Sending sine wave, audio output via portaudio.\n");
358 #else
359 	printf("SCO Demo: Sending sine wave, hexdump received data.\n");
360 #endif
361 #endif
362 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
363 	printf("SCO Demo: Sending ASCII blocks, print received data.\n");
364 #endif
365 #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
366 	printf("SCO Demo: Sending counter value, hexdump received data.\n");
367 #endif
368 
369 #ifdef USE_PORTAUDIO
370     int err;
371     PaStreamParameters outputParameters;
372 
373     /* -- initialize PortAudio -- */
374     err = Pa_Initialize();
375     if( err != paNoError ) return;
376     /* -- setup input and output -- */
377     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
378     outputParameters.channelCount = NUM_CHANNELS;
379     outputParameters.sampleFormat = PA_SAMPLE_TYPE;
380     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
381     outputParameters.hostApiSpecificStreamInfo = NULL;
382     /* -- setup stream -- */
383     err = Pa_OpenStream(
384            &stream,
385            NULL, // &inputParameters,
386            &outputParameters,
387            SAMPLE_RATE,
388            FRAMES_PER_BUFFER,
389            paClipOff, /* we won't output out of range samples so don't bother clipping them */
390            NULL, 	  /* no callback, use blocking API */
391            NULL ); 	  /* no callback, so no callback userData */
392     if( err != paNoError ) return;
393     /* -- start stream -- */
394     err = Pa_StartStream( stream );
395     if( err != paNoError ) return;
396 #endif
397 
398 #if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE
399     hci_set_sco_voice_setting(0x03);    // linear, unsigned, 8-bit, transparent
400 #endif
401 
402 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
403     phase = 'a';
404 #endif
405 }
406 
407 static void sco_report(void){
408     printf("SCO: sent %u, received %u\n", count_sent, count_received);
409 }
410 
411 void sco_demo_send(hci_con_handle_t sco_handle){
412 
413     if (!sco_handle) return;
414 
415     const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length();
416     const int sco_payload_length = sco_packet_length - 3;
417 
418     hci_reserve_packet_buffer();
419     uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
420     // set handle + flags
421     little_endian_store_16(sco_packet, 0, sco_handle);
422     // set len
423     sco_packet[2] = sco_payload_length;
424     const int audio_samples_per_packet = sco_payload_length;    // for 8-bit data. for 16-bit data it's /2
425 
426 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
427     if (negotiated_codec == HFP_CODEC_MSBC){
428 
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(sco_packet + 3, sco_payload_length);
433         if (msbc_file_out){
434             // log outgoing mSBC data for testing
435             fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out);
436         }
437 
438         sco_demo_fill_audio_frame();
439     } else {
440         int i;
441         for (i=0;i<audio_samples_per_packet;i++){
442             sco_packet[3+i] = sine[phase];
443             phase++;
444             if (phase >= sizeof(sine)) phase = 0;
445         }
446     }
447 #else
448 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
449     memset(&sco_packet[3], phase++, audio_samples_per_packet);
450     if (phase > 'z') phase = 'a';
451 #else
452     int j;
453     for (j=0;j<audio_samples_per_packet;j++){
454         sco_packet[3+j] = phase++;
455     }
456 #endif
457 #endif
458 
459     hci_send_sco_packet_buffer(sco_packet_length);
460 
461     // request another send event
462     hci_request_sco_can_send_now_event();
463 
464     count_sent++;
465     if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report();
466 }
467 
468 /**
469  * @brief Process received data
470  */
471 void sco_demo_receive(uint8_t * packet, uint16_t size){
472 
473     dump_data = 1;
474 
475     count_received++;
476     // if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report();
477 
478 
479 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
480 #ifdef SCO_WAV_FILENAME
481     if (negotiated_codec == HFP_CODEC_MSBC){
482         sco_demo_receive_mSBC(packet, size);
483     } else {
484         sco_demo_receive_CVSD(packet, size);
485     }
486 #endif
487 #endif
488 
489     if (packet[1] & 0x30){
490         printf("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
491         log_info("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
492         printf_hexdump(&packet[3], size-3);
493 
494         return;
495     }
496 
497 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
498 #ifdef USE_PORTAUDIO
499     uint32_t start = btstack_run_loop_get_time_ms();
500     Pa_WriteStream( stream, &packet[3], size -3);
501     uint32_t end   = btstack_run_loop_get_time_ms();
502     if (end - start > 5){
503         printf("Portaudio: write stream took %u ms\n", end - start);
504     }
505     dump_data = 0;
506 #endif
507 #endif
508 
509     if (dump_data){
510         printf("data: ");
511 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
512         int i;
513         for (i=3;i<size;i++){
514             printf("%c", packet[i]);
515         }
516         printf("\n");
517         dump_data = 0;
518 #else
519         printf_hexdump(&packet[3], size-3);
520 #endif
521     }
522 }
523