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