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