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