xref: /btstack/example/sco_demo_util.c (revision df5c73b695709016d0e1bdda843dae7976ffd157)
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 #include "sco_demo_util.h"
43 #include <stdio.h>
44 
45 // configure test mode
46 #define SCO_DEMO_MODE_SINE		0
47 #define SCO_DEMO_MODE_ASCII		1
48 #define SCO_DEMO_MODE_COUNTER	2
49 
50 
51 // SCO demo configuration
52 #define SCO_DEMO_MODE SCO_DEMO_MODE_ASCII
53 #define SCO_REPORT_PERIOD 100
54 
55 #ifdef HAVE_POSIX_FILE_IO
56 #define SCO_WAV_FILENAME "sco_input.wav"
57 #define SCO_WAV_DURATION_IN_SECONDS 30
58 #endif
59 
60 
61 #if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE)
62 #define USE_PORTAUDIO
63 #endif
64 
65 #ifdef USE_PORTAUDIO
66 #include <portaudio.h>
67 // portaudio config
68 #define NUM_CHANNELS 1
69 #define SAMPLE_RATE 8000
70 #define FRAMES_PER_BUFFER 24
71 #define PA_SAMPLE_TYPE paInt8
72 // portaudio globals
73 static  PaStream * stream;
74 #endif
75 
76 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
77 // input signal: pre-computed sine wave, 160 Hz
78 static const uint8_t sine[] = {
79       0,  15,  31,  46,  61,  74,  86,  97, 107, 114,
80     120, 124, 126, 126, 124, 120, 114, 107,  97,  86,
81      74,  61,  46,  31,  15,   0, 241, 225, 210, 195,
82     182, 170, 159, 149, 142, 136, 132, 130, 130, 132,
83     136, 142, 149, 159, 170, 182, 195, 210, 225, 241,
84 };
85 #endif
86 
87 static int phase;
88 static int count_sent = 0;
89 static int count_received = 0;
90 
91 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
92 #ifdef SCO_WAV_FILENAME
93 
94 static FILE * wav_file;
95 static int num_samples_to_write;
96 
97 
98 static void little_endian_fstore_16(FILE * file, uint16_t value){
99     uint8_t buf[2];
100     little_endian_store_32(buf, 0, value);
101     fwrite(&buf, 1, 2, file);
102 }
103 
104 static void little_endian_fstore_32(FILE * file, uint32_t value){
105     uint8_t buf[4];
106     little_endian_store_32(buf, 0, value);
107     fwrite(&buf, 1, 4, file);
108 }
109 
110 static FILE * wav_init(const char * filename){
111     printf("SCO Demo: creating wav file %s\n", filename);
112     return fopen(filename, "wb");
113 }
114 
115 static void write_wav_header(FILE * file, int sample_rate, int num_channels, int num_samples, int bytes_per_sample){
116     printf("SCO Demo: writing wav header: sample rate %u, num channels %u, duration %u s, bytes per sample %u\n",
117         sample_rate, num_channels, num_samples / sample_rate / num_channels, bytes_per_sample);
118 
119     /* write RIFF header */
120     fwrite("RIFF", 1, 4, file);
121     // num_samples = blocks * subbands
122     uint32_t data_bytes = (uint32_t) (bytes_per_sample * num_samples * num_channels);
123     little_endian_fstore_32(file, data_bytes + 36);
124     fwrite("WAVE", 1, 4, file);
125 
126     int byte_rate = sample_rate * num_channels * bytes_per_sample;
127     int bits_per_sample = 8 * bytes_per_sample;
128     int block_align = num_channels * bits_per_sample;
129     int fmt_length = 16;
130     int fmt_format_tag = 1; // PCM
131 
132     /* write fmt chunk */
133     fwrite("fmt ", 1, 4, file);
134     little_endian_fstore_32(file, fmt_length);
135     little_endian_fstore_16(file, fmt_format_tag);
136     little_endian_fstore_16(file, num_channels);
137     little_endian_fstore_32(file, sample_rate);
138     little_endian_fstore_32(file, byte_rate);
139     little_endian_fstore_16(file, block_align);
140     little_endian_fstore_16(file, bits_per_sample);
141 
142     /* write data chunk */
143     fwrite("data", 1, 4, file);
144     little_endian_fstore_32(file, data_bytes);
145 }
146 
147 static void write_wav_data_uint8(FILE * file, unsigned long num_samples, uint8_t * data){
148     fwrite(data, num_samples, 1, file);
149 }
150 
151 #endif
152 #endif
153 
154 void sco_demo_init(void){
155 
156 	// status
157 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
158 #ifdef HAVE_PORTAUDIO
159 	printf("SCO Demo: Sending sine wave, audio output via portaudio.\n");
160 #else
161 	printf("SCO Demo: Sending sine wave, hexdump received data.\n");
162 #endif
163 #endif
164 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
165 	printf("SCO Demo: Sending ASCII blocks, print received data.\n");
166 #endif
167 #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
168 	printf("SCO Demo: Sending counter value, hexdump received data.\n");
169 #endif
170 
171 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
172 #ifdef SCO_WAV_FILENAME
173     wav_file = wav_init(SCO_WAV_FILENAME);
174     const int sample_rate = 8000;
175     const int num_samples = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
176     const int num_channels = 1;
177     const int bytes_per_sample = 1;
178     write_wav_header(wav_file, sample_rate, num_channels, num_samples, bytes_per_sample);
179     num_samples_to_write = num_samples;
180 #endif
181 #endif
182 
183 #ifdef USE_PORTAUDIO
184     int err;
185     PaStreamParameters outputParameters;
186 
187     /* -- initialize PortAudio -- */
188     err = Pa_Initialize();
189     if( err != paNoError ) return;
190     /* -- setup input and output -- */
191     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
192     outputParameters.channelCount = NUM_CHANNELS;
193     outputParameters.sampleFormat = PA_SAMPLE_TYPE;
194     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
195     outputParameters.hostApiSpecificStreamInfo = NULL;
196     /* -- setup stream -- */
197     err = Pa_OpenStream(
198            &stream,
199            NULL, // &inputParameters,
200            &outputParameters,
201            SAMPLE_RATE,
202            FRAMES_PER_BUFFER,
203            paClipOff, /* we won't output out of range samples so don't bother clipping them */
204            NULL, 	  /* no callback, use blocking API */
205            NULL ); 	  /* no callback, so no callback userData */
206     if( err != paNoError ) return;
207     /* -- start stream -- */
208     err = Pa_StartStream( stream );
209     if( err != paNoError ) return;
210 #endif
211 
212 #if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE
213     hci_set_sco_voice_setting(0x03);    // linear, unsigned, 8-bit, transparent
214 #endif
215 
216 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
217     phase = 'a';
218 #endif
219 }
220 
221 static void sco_report(void){
222     printf("SCO: sent %u, received %u\n", count_sent, count_received);
223 }
224 
225 void sco_demo_send(hci_con_handle_t sco_handle){
226 
227     if (!sco_handle) return;
228 
229     const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length();
230     const int sco_payload_length = sco_packet_length - 3;
231 
232     hci_reserve_packet_buffer();
233     uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
234     // set handle + flags
235     little_endian_store_16(sco_packet, 0, sco_handle);
236     // set len
237     sco_packet[2] = sco_payload_length;
238     const int frames_per_packet = sco_payload_length;    // for 8-bit data. for 16-bit data it's /2
239 
240 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
241     int i;
242     for (i=0;i<frames_per_packet;i++){
243         sco_packet[3+i] = sine[phase];
244         phase++;
245         if (phase >= sizeof(sine)) phase = 0;
246     }
247 #else
248 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
249     memset(&sco_packet[3], phase++, frames_per_packet);
250     if (phase > 'z') phase = 'a';
251 #else
252     int j;
253     for (j=0;j<frames_per_packet;j++){
254         sco_packet[3+j] = phase++;
255     }
256 #endif
257 #endif
258     hci_send_sco_packet_buffer(sco_packet_length);
259 
260     // request another send event
261     hci_request_sco_can_send_now_event();
262 
263     count_sent++;
264     if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report();
265 }
266 
267 /**
268  * @brief Process received data
269  */
270 void sco_demo_receive(uint8_t * packet, uint16_t size){
271 
272 
273     int dump_data = 1;
274 
275     count_received++;
276     // if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report();
277 
278 
279 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
280 #ifdef SCO_WAV_FILENAME
281     if (num_samples_to_write){
282         const int num_samples = size - 3;
283         const int samples_to_write = btstack_min(num_samples, num_samples_to_write);
284         // convert 8 bit signed to 8 bit unsigned
285         int i;
286         for (i=0;i<samples_to_write;i++){
287             packet[3+i] += 128;
288         }
289         write_wav_data_uint8(wav_file, samples_to_write, &packet[3]);
290         num_samples_to_write -= samples_to_write;
291         if (num_samples_to_write == 0){
292             printf("SCO Demo: closing wav file\n");
293             fclose(wav_file);
294         }
295         dump_data = 0;
296     }
297 #endif
298 #endif
299 
300     if (packet[1] & 0xf0){
301         printf("SCO CRC Error: %x - data: ", packet[1] >> 4);
302         printf_hexdump(&packet[3], size-3);
303         return;
304     }
305 
306 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
307 #ifdef USE_PORTAUDIO
308     uint32_t start = btstack_run_loop_get_time_ms();
309     Pa_WriteStream( stream, &packet[3], size -3);
310     uint32_t end   = btstack_run_loop_get_time_ms();
311     if (end - start > 5){
312         printf("Portaudio: write stream took %u ms\n", end - start);
313     }
314     dump_data = 0;
315 #endif
316 #endif
317 
318     if (dump_data){
319         printf("data: ");
320 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
321         int i;
322         for (i=3;i<size;i++){
323             printf("%c", packet[i]);
324         }
325         printf("\n");
326         dump_data = 0;
327 #else
328         printf_hexdump(&packet[3], size-3);
329 #endif
330     }
331 }
332