xref: /btstack/example/sco_demo_util.c (revision 1ac893701d22aa12e270373dc96fb887fb82905b)
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     hfp_msbc_init();
225     sco_demo_fill_audio_frame();
226 }
227 
228 static void sco_demo_init_CVSD(void){
229     wav_writer_state.wav_file = wav_init(SCO_WAV_FILENAME);
230     wav_writer_state.frame_count = 0;
231     wav_writer_state.total_num_samples = 0;
232 
233     const int sample_rate = 8000;
234     const int num_samples = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
235     const int num_channels = 1;
236     const int bytes_per_sample = 1;
237     num_samples_to_write = num_samples;
238     write_wav_header(wav_writer_state.wav_file, sample_rate, num_channels, num_samples, bytes_per_sample);
239 }
240 
241 
242 static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){
243     if (num_samples_to_write){
244         sbc_decoder_process_data(&decoder_state, (packet[1] >> 4) & 3, packet+3, size-3);
245         dump_data = 0;
246     }
247 }
248 
249 static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){
250     if (num_samples_to_write){
251         const int num_samples = size - 3;
252         const int samples_to_write = btstack_min(num_samples, num_samples_to_write);
253         // convert 8 bit signed to 8 bit unsigned
254         int i;
255         for (i=0;i<samples_to_write;i++){
256             packet[3+i] += 128;
257         }
258 
259         wav_writer_state_t * writer_state = (wav_writer_state_t*) decoder_state.context;
260         write_wav_data_uint8(writer_state->wav_file, samples_to_write, &packet[3]);
261         num_samples_to_write -= samples_to_write;
262         if (num_samples_to_write == 0){
263             sco_demo_close();
264         }
265         dump_data = 0;
266     }
267 }
268 
269 #endif
270 #endif
271 
272 void sco_demo_close(void){
273 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
274 #ifdef SCO_WAV_FILENAME
275 
276 #if 0
277     printf("SCO Demo: closing wav file\n");
278     if (negotiated_codec == HFP_CODEC_MSBC){
279         wav_writer_state_t * writer_state = (wav_writer_state_t*) decoder_state.context;
280         if (!writer_state->wav_file) return;
281         rewind(writer_state->wav_file);
282         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);
283         fclose(writer_state->wav_file);
284         writer_state->wav_file = NULL;
285     }
286 #endif
287 #endif
288 #endif
289 }
290 
291 void sco_demo_set_codec(uint8_t codec){
292     if (negotiated_codec == codec) return;
293     negotiated_codec = codec;
294 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
295 #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
296     if (negotiated_codec == HFP_CODEC_MSBC){
297         sco_demo_init_mSBC();
298     } else {
299         sco_demo_init_CVSD();
300     }
301 #endif
302 #endif
303 }
304 
305 void sco_demo_init(void){
306 
307 	// status
308 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
309 #ifdef HAVE_PORTAUDIO
310 	printf("SCO Demo: Sending sine wave, audio output via portaudio.\n");
311 #else
312 	printf("SCO Demo: Sending sine wave, hexdump received data.\n");
313 #endif
314 #endif
315 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
316 	printf("SCO Demo: Sending ASCII blocks, print received data.\n");
317 #endif
318 #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
319 	printf("SCO Demo: Sending counter value, hexdump received data.\n");
320 #endif
321 
322 #ifdef USE_PORTAUDIO
323     int err;
324     PaStreamParameters outputParameters;
325 
326     /* -- initialize PortAudio -- */
327     err = Pa_Initialize();
328     if( err != paNoError ) return;
329     /* -- setup input and output -- */
330     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
331     outputParameters.channelCount = NUM_CHANNELS;
332     outputParameters.sampleFormat = PA_SAMPLE_TYPE;
333     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
334     outputParameters.hostApiSpecificStreamInfo = NULL;
335     /* -- setup stream -- */
336     err = Pa_OpenStream(
337            &stream,
338            NULL, // &inputParameters,
339            &outputParameters,
340            SAMPLE_RATE,
341            FRAMES_PER_BUFFER,
342            paClipOff, /* we won't output out of range samples so don't bother clipping them */
343            NULL, 	  /* no callback, use blocking API */
344            NULL ); 	  /* no callback, so no callback userData */
345     if( err != paNoError ) return;
346     /* -- start stream -- */
347     err = Pa_StartStream( stream );
348     if( err != paNoError ) return;
349 #endif
350 
351 //#if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE
352     hci_set_sco_voice_setting(0x03);    // linear, unsigned, 8-bit, transparent
353 //#endif
354 
355 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
356     phase = 'a';
357 #endif
358 }
359 
360 static void sco_report(void){
361     printf("SCO: sent %u, received %u\n", count_sent, count_received);
362 }
363 
364 void sco_demo_send(hci_con_handle_t sco_handle){
365 
366     if (!sco_handle) return;
367 
368     const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length();
369     const int sco_payload_length = sco_packet_length - 3;
370 
371     hci_reserve_packet_buffer();
372     uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
373     // set handle + flags
374     little_endian_store_16(sco_packet, 0, sco_handle);
375     // set len
376     sco_packet[2] = sco_payload_length;
377     const int audio_samples_per_packet = sco_payload_length;    // for 8-bit data. for 16-bit data it's /2
378 
379 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
380     if (negotiated_codec == HFP_CODEC_MSBC){
381 
382         if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){
383             log_error("mSBC stream is empty.");
384         }
385         hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length);
386         sco_demo_fill_audio_frame();
387     } else {
388         int i;
389         for (i=0;i<audio_samples_per_packet;i++){
390             sco_packet[3+i] = sine[phase];
391             phase++;
392             if (phase >= sizeof(sine)) phase = 0;
393         }
394     }
395 #else
396 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
397     memset(&sco_packet[3], phase++, audio_samples_per_packet);
398     if (phase > 'z') phase = 'a';
399 #else
400     int j;
401     for (j=0;j<audio_samples_per_packet;j++){
402         sco_packet[3+j] = phase++;
403     }
404 #endif
405 #endif
406 
407     hci_send_sco_packet_buffer(sco_packet_length);
408 
409     // request another send event
410     hci_request_sco_can_send_now_event();
411 
412     count_sent++;
413     if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report();
414 }
415 
416 
417 /**
418  * @brief Process received data
419  */
420 void sco_demo_receive(uint8_t * packet, uint16_t size){
421 
422 
423     dump_data = 1;
424 
425     count_received++;
426     // if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report();
427 
428 
429 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
430 #ifdef SCO_WAV_FILENAME
431     if (negotiated_codec == HFP_CODEC_MSBC){
432         sco_demo_receive_mSBC(packet, size);
433     } else {
434         sco_demo_receive_CVSD(packet, size);
435     }
436 #endif
437 #endif
438 
439     if (packet[1] & 0xf0){
440         printf("SCO CRC Error: %x - data: ", packet[1] >> 4);
441         printf_hexdump(&packet[3], size-3);
442         return;
443     }
444 
445 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
446 #ifdef USE_PORTAUDIO
447     uint32_t start = btstack_run_loop_get_time_ms();
448     Pa_WriteStream( stream, &packet[3], size -3);
449     uint32_t end   = btstack_run_loop_get_time_ms();
450     if (end - start > 5){
451         printf("Portaudio: write stream took %u ms\n", end - start);
452     }
453     dump_data = 0;
454 #endif
455 #endif
456 
457     if (dump_data){
458         printf("data: ");
459 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
460         int i;
461         for (i=3;i<size;i++){
462             printf("%c", packet[i]);
463         }
464         printf("\n");
465         dump_data = 0;
466 #else
467         printf_hexdump(&packet[3], size-3);
468 #endif
469     }
470 }
471