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[1] >> 4) & 3, 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 #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME) 297 if (negotiated_codec == HFP_CODEC_MSBC){ 298 sco_demo_init_mSBC(); 299 } else { 300 sco_demo_init_CVSD(); 301 } 302 #endif 303 #endif 304 } 305 306 void sco_demo_init(void){ 307 308 // status 309 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 310 #ifdef HAVE_PORTAUDIO 311 printf("SCO Demo: Sending sine wave, audio output via portaudio.\n"); 312 #else 313 printf("SCO Demo: Sending sine wave, hexdump received data.\n"); 314 #endif 315 #endif 316 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII 317 printf("SCO Demo: Sending ASCII blocks, print received data.\n"); 318 #endif 319 #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER 320 printf("SCO Demo: Sending counter value, hexdump received data.\n"); 321 #endif 322 323 #ifdef USE_PORTAUDIO 324 int err; 325 PaStreamParameters outputParameters; 326 327 /* -- initialize PortAudio -- */ 328 err = Pa_Initialize(); 329 if( err != paNoError ) return; 330 /* -- setup input and output -- */ 331 outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ 332 outputParameters.channelCount = NUM_CHANNELS; 333 outputParameters.sampleFormat = PA_SAMPLE_TYPE; 334 outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; 335 outputParameters.hostApiSpecificStreamInfo = NULL; 336 /* -- setup stream -- */ 337 err = Pa_OpenStream( 338 &stream, 339 NULL, // &inputParameters, 340 &outputParameters, 341 SAMPLE_RATE, 342 FRAMES_PER_BUFFER, 343 paClipOff, /* we won't output out of range samples so don't bother clipping them */ 344 NULL, /* no callback, use blocking API */ 345 NULL ); /* no callback, so no callback userData */ 346 if( err != paNoError ) return; 347 /* -- start stream -- */ 348 err = Pa_StartStream( stream ); 349 if( err != paNoError ) return; 350 #endif 351 352 //#if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE 353 hci_set_sco_voice_setting(0x03); // linear, unsigned, 8-bit, transparent 354 //#endif 355 356 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII 357 phase = 'a'; 358 #endif 359 } 360 361 static void sco_report(void){ 362 printf("SCO: sent %u, received %u\n", count_sent, count_received); 363 } 364 365 void sco_demo_send(hci_con_handle_t sco_handle){ 366 367 if (!sco_handle) return; 368 369 const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length(); 370 const int sco_payload_length = sco_packet_length - 3; 371 372 hci_reserve_packet_buffer(); 373 uint8_t * sco_packet = hci_get_outgoing_packet_buffer(); 374 // set handle + flags 375 little_endian_store_16(sco_packet, 0, sco_handle); 376 // set len 377 sco_packet[2] = sco_payload_length; 378 const int audio_samples_per_packet = sco_payload_length; // for 8-bit data. for 16-bit data it's /2 379 380 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 381 if (negotiated_codec == HFP_CODEC_MSBC){ 382 383 if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ 384 log_error("mSBC stream is empty."); 385 } 386 hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length); 387 sco_demo_fill_audio_frame(); 388 } else { 389 int i; 390 for (i=0;i<audio_samples_per_packet;i++){ 391 sco_packet[3+i] = sine[phase]; 392 phase++; 393 if (phase >= sizeof(sine)) phase = 0; 394 } 395 } 396 #else 397 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII 398 memset(&sco_packet[3], phase++, audio_samples_per_packet); 399 if (phase > 'z') phase = 'a'; 400 #else 401 int j; 402 for (j=0;j<audio_samples_per_packet;j++){ 403 sco_packet[3+j] = phase++; 404 } 405 #endif 406 #endif 407 408 hci_send_sco_packet_buffer(sco_packet_length); 409 410 // request another send event 411 hci_request_sco_can_send_now_event(); 412 413 count_sent++; 414 if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report(); 415 } 416 417 418 /** 419 * @brief Process received data 420 */ 421 void sco_demo_receive(uint8_t * packet, uint16_t size){ 422 423 424 dump_data = 1; 425 426 count_received++; 427 // if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report(); 428 429 430 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 431 #ifdef SCO_WAV_FILENAME 432 if (negotiated_codec == HFP_CODEC_MSBC){ 433 sco_demo_receive_mSBC(packet, size); 434 } else { 435 sco_demo_receive_CVSD(packet, size); 436 } 437 #endif 438 #endif 439 440 if (packet[1] & 0xf0){ 441 printf("SCO CRC Error: %x - data: ", packet[1] >> 4); 442 printf_hexdump(&packet[3], size-3); 443 return; 444 } 445 446 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE 447 #ifdef USE_PORTAUDIO 448 uint32_t start = btstack_run_loop_get_time_ms(); 449 Pa_WriteStream( stream, &packet[3], size -3); 450 uint32_t end = btstack_run_loop_get_time_ms(); 451 if (end - start > 5){ 452 printf("Portaudio: write stream took %u ms\n", end - start); 453 } 454 dump_data = 0; 455 #endif 456 #endif 457 458 if (dump_data){ 459 printf("data: "); 460 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII 461 int i; 462 for (i=3;i<size;i++){ 463 printf("%c", packet[i]); 464 } 465 printf("\n"); 466 dump_data = 0; 467 #else 468 printf_hexdump(&packet[3], size-3); 469 #endif 470 } 471 } 472