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