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