xref: /btstack/example/sco_demo_util.c (revision 26463303c90044fdee6c9ce5f6ae3e922655d93c)
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 30
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         btstack_cvsd_plc_process_data(&cvsd_plc_state, (int8_t *)(packet+3),num_samples,audio_frame_out);
282 
283         // convert 8 bit signed to 8 bit unsigned
284         int i;
285         for (i=0;i<samples_to_write;i++){
286             audio_frame_out[i] += 128;
287         }
288 
289         wav_writer_state_t * writer_state = &wav_writer_state;
290         write_wav_data_uint8(writer_state->wav_file, samples_to_write, (uint8_t*)audio_frame_out);
291         num_samples_to_write -= samples_to_write;
292         if (num_samples_to_write == 0){
293             sco_demo_close();
294         }
295         dump_data = 0;
296 
297         // convert back
298         for (i=0;i<samples_to_write;i++){
299             audio_frame_out[i] += 128;
300         }
301     }
302 }
303 
304 #endif
305 #endif
306 
307 void sco_demo_close(void){
308 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
309 #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
310     printf("SCO demo statistics: ");
311     if (negotiated_codec == HFP_CODEC_MSBC){
312         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);
313     } else {
314         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);
315     }
316 #endif
317 #endif
318 
319 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
320 #ifdef SCO_WAV_FILENAME
321 
322 #if 0
323     printf("SCO Demo: closing wav file\n");
324     if (negotiated_codec == HFP_CODEC_MSBC){
325         wav_writer_state_t * writer_state = &wav_writer_state;
326         if (!writer_state->wav_file) return;
327         rewind(writer_state->wav_file);
328         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);
329         fclose(writer_state->wav_file);
330         writer_state->wav_file = NULL;
331     }
332 #endif
333 #endif
334 #endif
335 }
336 
337 void sco_demo_set_codec(uint8_t codec){
338     if (negotiated_codec == codec) return;
339     negotiated_codec = codec;
340 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
341 #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
342     if (negotiated_codec == HFP_CODEC_MSBC){
343         sco_demo_init_mSBC();
344     } else {
345         sco_demo_init_CVSD();
346     }
347 #endif
348 #endif
349 }
350 
351 void sco_demo_init(void){
352 
353 	// status
354 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
355 #ifdef HAVE_PORTAUDIO
356 	printf("SCO Demo: Sending sine wave, audio output via portaudio.\n");
357 #else
358 	printf("SCO Demo: Sending sine wave, hexdump received data.\n");
359 #endif
360 #endif
361 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
362 	printf("SCO Demo: Sending ASCII blocks, print received data.\n");
363 #endif
364 #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
365 	printf("SCO Demo: Sending counter value, hexdump received data.\n");
366 #endif
367 
368 #ifdef USE_PORTAUDIO
369     int err;
370     PaStreamParameters outputParameters;
371 
372     /* -- initialize PortAudio -- */
373     err = Pa_Initialize();
374     if( err != paNoError ) return;
375     /* -- setup input and output -- */
376     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
377     outputParameters.channelCount = NUM_CHANNELS;
378     outputParameters.sampleFormat = PA_SAMPLE_TYPE;
379     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
380     outputParameters.hostApiSpecificStreamInfo = NULL;
381     /* -- setup stream -- */
382     err = Pa_OpenStream(
383            &stream,
384            NULL, // &inputParameters,
385            &outputParameters,
386            SAMPLE_RATE,
387            FRAMES_PER_BUFFER,
388            paClipOff, /* we won't output out of range samples so don't bother clipping them */
389            NULL, 	  /* no callback, use blocking API */
390            NULL ); 	  /* no callback, so no callback userData */
391     if( err != paNoError ) return;
392     /* -- start stream -- */
393     err = Pa_StartStream( stream );
394     if( err != paNoError ) return;
395 #endif
396 
397 #if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE
398     hci_set_sco_voice_setting(0x03);    // linear, unsigned, 8-bit, transparent
399 #endif
400 
401 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
402     phase = 'a';
403 #endif
404 }
405 
406 static void sco_report(void){
407     printf("SCO: sent %u, received %u\n", count_sent, count_received);
408 }
409 
410 void sco_demo_send(hci_con_handle_t sco_handle){
411 
412     if (!sco_handle) return;
413 
414     const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length();
415     const int sco_payload_length = sco_packet_length - 3;
416 
417     hci_reserve_packet_buffer();
418     uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
419     // set handle + flags
420     little_endian_store_16(sco_packet, 0, sco_handle);
421     // set len
422     sco_packet[2] = sco_payload_length;
423     const int audio_samples_per_packet = sco_payload_length;    // for 8-bit data. for 16-bit data it's /2
424 
425 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
426     if (negotiated_codec == HFP_CODEC_MSBC){
427 
428         if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){
429             log_error("mSBC stream is empty.");
430         }
431         hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length);
432         if (msbc_file_out){
433             // log outgoing mSBC data for testing
434             fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out);
435         }
436 
437         sco_demo_fill_audio_frame();
438     } else {
439         int i;
440         for (i=0;i<audio_samples_per_packet;i++){
441             sco_packet[3+i] = sine[phase];
442             phase++;
443             if (phase >= sizeof(sine)) phase = 0;
444         }
445     }
446 #else
447 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
448     memset(&sco_packet[3], phase++, audio_samples_per_packet);
449     if (phase > 'z') phase = 'a';
450 #else
451     int j;
452     for (j=0;j<audio_samples_per_packet;j++){
453         sco_packet[3+j] = phase++;
454     }
455 #endif
456 #endif
457 
458     hci_send_sco_packet_buffer(sco_packet_length);
459 
460     // request another send event
461     hci_request_sco_can_send_now_event();
462 
463     count_sent++;
464     if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report();
465 }
466 
467 /**
468  * @brief Process received data
469  */
470 void sco_demo_receive(uint8_t * packet, uint16_t size){
471 
472     dump_data = 1;
473 
474     count_received++;
475     // if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report();
476 
477 
478 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
479 #ifdef SCO_WAV_FILENAME
480     if (negotiated_codec == HFP_CODEC_MSBC){
481         sco_demo_receive_mSBC(packet, size);
482     } else {
483         sco_demo_receive_CVSD(packet, size);
484     }
485 #endif
486 #endif
487 
488     if (packet[1] & 0x30){
489         printf("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
490         log_info("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
491         printf_hexdump(&packet[3], size-3);
492 
493         return;
494     }
495 
496 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
497 #ifdef USE_PORTAUDIO
498     uint32_t start = btstack_run_loop_get_time_ms();
499     Pa_WriteStream( stream, &packet[3], size -3);
500     uint32_t end   = btstack_run_loop_get_time_ms();
501     if (end - start > 5){
502         printf("Portaudio: write stream took %u ms\n", end - start);
503     }
504     dump_data = 0;
505 #endif
506 #endif
507 
508     if (dump_data){
509         printf("data: ");
510 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
511         int i;
512         for (i=3;i<size;i++){
513             printf("%c", packet[i]);
514         }
515         printf("\n");
516         dump_data = 0;
517 #else
518         printf_hexdump(&packet[3], size-3);
519 #endif
520     }
521 }
522