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