xref: /btstack/example/sco_demo_util.c (revision bd1920a3cca67680da0de3788427ca0f57ef31c5)
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[1] >> 4) & 3, 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 #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
297     if (negotiated_codec == HFP_CODEC_MSBC){
298         sco_demo_init_mSBC();
299     } else {
300         sco_demo_init_CVSD();
301     }
302 #endif
303 #endif
304 }
305 
306 void sco_demo_init(void){
307 
308 	// status
309 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
310 #ifdef HAVE_PORTAUDIO
311 	printf("SCO Demo: Sending sine wave, audio output via portaudio.\n");
312 #else
313 	printf("SCO Demo: Sending sine wave, hexdump received data.\n");
314 #endif
315 #endif
316 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
317 	printf("SCO Demo: Sending ASCII blocks, print received data.\n");
318 #endif
319 #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
320 	printf("SCO Demo: Sending counter value, hexdump received data.\n");
321 #endif
322 
323 #ifdef USE_PORTAUDIO
324     int err;
325     PaStreamParameters outputParameters;
326 
327     /* -- initialize PortAudio -- */
328     err = Pa_Initialize();
329     if( err != paNoError ) return;
330     /* -- setup input and output -- */
331     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
332     outputParameters.channelCount = NUM_CHANNELS;
333     outputParameters.sampleFormat = PA_SAMPLE_TYPE;
334     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
335     outputParameters.hostApiSpecificStreamInfo = NULL;
336     /* -- setup stream -- */
337     err = Pa_OpenStream(
338            &stream,
339            NULL, // &inputParameters,
340            &outputParameters,
341            SAMPLE_RATE,
342            FRAMES_PER_BUFFER,
343            paClipOff, /* we won't output out of range samples so don't bother clipping them */
344            NULL, 	  /* no callback, use blocking API */
345            NULL ); 	  /* no callback, so no callback userData */
346     if( err != paNoError ) return;
347     /* -- start stream -- */
348     err = Pa_StartStream( stream );
349     if( err != paNoError ) return;
350 #endif
351 
352 //#if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE
353     hci_set_sco_voice_setting(0x03);    // linear, unsigned, 8-bit, transparent
354 //#endif
355 
356 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
357     phase = 'a';
358 #endif
359 }
360 
361 static void sco_report(void){
362     printf("SCO: sent %u, received %u\n", count_sent, count_received);
363 }
364 
365 void sco_demo_send(hci_con_handle_t sco_handle){
366 
367     if (!sco_handle) return;
368 
369     const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length();
370     const int sco_payload_length = sco_packet_length - 3;
371 
372     hci_reserve_packet_buffer();
373     uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
374     // set handle + flags
375     little_endian_store_16(sco_packet, 0, sco_handle);
376     // set len
377     sco_packet[2] = sco_payload_length;
378     const int audio_samples_per_packet = sco_payload_length;    // for 8-bit data. for 16-bit data it's /2
379 
380 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
381     if (negotiated_codec == HFP_CODEC_MSBC){
382 
383         if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){
384             log_error("mSBC stream is empty.");
385         }
386         hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length);
387         sco_demo_fill_audio_frame();
388     } else {
389         int i;
390         for (i=0;i<audio_samples_per_packet;i++){
391             sco_packet[3+i] = sine[phase];
392             phase++;
393             if (phase >= sizeof(sine)) phase = 0;
394         }
395     }
396 #else
397 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
398     memset(&sco_packet[3], phase++, audio_samples_per_packet);
399     if (phase > 'z') phase = 'a';
400 #else
401     int j;
402     for (j=0;j<audio_samples_per_packet;j++){
403         sco_packet[3+j] = phase++;
404     }
405 #endif
406 #endif
407 
408     hci_send_sco_packet_buffer(sco_packet_length);
409 
410     // request another send event
411     hci_request_sco_can_send_now_event();
412 
413     count_sent++;
414     if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report();
415 }
416 
417 
418 /**
419  * @brief Process received data
420  */
421 void sco_demo_receive(uint8_t * packet, uint16_t size){
422 
423 
424     dump_data = 1;
425 
426     count_received++;
427     // if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report();
428 
429 
430 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
431 #ifdef SCO_WAV_FILENAME
432     if (negotiated_codec == HFP_CODEC_MSBC){
433         sco_demo_receive_mSBC(packet, size);
434     } else {
435         sco_demo_receive_CVSD(packet, size);
436     }
437 #endif
438 #endif
439 
440     if (packet[1] & 0xf0){
441         printf("SCO CRC Error: %x - data: ", packet[1] >> 4);
442         printf_hexdump(&packet[3], size-3);
443         return;
444     }
445 
446 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
447 #ifdef USE_PORTAUDIO
448     uint32_t start = btstack_run_loop_get_time_ms();
449     Pa_WriteStream( stream, &packet[3], size -3);
450     uint32_t end   = btstack_run_loop_get_time_ms();
451     if (end - start > 5){
452         printf("Portaudio: write stream took %u ms\n", end - start);
453     }
454     dump_data = 0;
455 #endif
456 #endif
457 
458     if (dump_data){
459         printf("data: ");
460 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
461         int i;
462         for (i=3;i<size;i++){
463             printf("%c", packet[i]);
464         }
465         printf("\n");
466         dump_data = 0;
467 #else
468         printf_hexdump(&packet[3], size-3);
469 #endif
470     }
471 }
472