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