1 /* 2 * Copyright (C) 2017 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 BLUEKITCHEN 24 * GMBH 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 #define BTSTACK_FILE__ "btstack_audio_portaudio.c" 39 40 41 #include <stdint.h> 42 #include <string.h> 43 #include "btstack_debug.h" 44 #include "btstack_audio.h" 45 #include "btstack_run_loop.h" 46 47 #ifdef HAVE_PORTAUDIO 48 49 #define PA_SAMPLE_TYPE paInt16 50 #define NUM_FRAMES_PER_PA_BUFFER 512 51 #define NUM_OUTPUT_BUFFERS 5 52 #define NUM_INPUT_BUFFERS 5 53 #define DRIVER_POLL_INTERVAL_MS 5 54 55 #ifndef MAX_NR_AUDIO_CHANNELS 56 #define MAX_NR_AUDIO_CHANNELS 2 57 #endif 58 59 #include <portaudio.h> 60 61 // config 62 static int num_channels_sink; 63 static int num_channels_source; 64 static int num_bytes_per_sample_sink; 65 static int num_bytes_per_sample_source; 66 67 static const char * sink_device_name; 68 static const char * source_device_name = "4-chan"; 69 70 // portaudio 71 static int portaudio_initialized; 72 73 // state 74 static int source_initialized; 75 static int sink_initialized; 76 static int source_active; 77 static int sink_active; 78 79 static uint8_t sink_volume; 80 81 static PaStream * stream_source; 82 static PaStream * stream_sink; 83 84 // client 85 static void (*playback_callback)(int16_t * buffer, uint16_t num_samples); 86 static void (*recording_callback)(const int16_t * buffer, uint16_t num_samples); 87 88 // output buffer 89 static int16_t output_buffer_storage[NUM_OUTPUT_BUFFERS * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS]; 90 static int16_t * output_buffers[NUM_OUTPUT_BUFFERS]; 91 static int output_buffer_to_play; 92 static int output_buffer_to_fill; 93 94 // input buffer 95 static int16_t input_buffer_storage[NUM_INPUT_BUFFERS * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS]; 96 static int16_t * input_buffers[NUM_INPUT_BUFFERS]; 97 static int input_buffer_to_record; 98 static int input_buffer_to_fill; 99 100 101 // timer to fill output ring buffer 102 static btstack_timer_source_t driver_timer_sink; 103 static btstack_timer_source_t driver_timer_source; 104 105 static int portaudio_callback_sink( const void * inputBuffer, 106 void * outputBuffer, 107 unsigned long frames_per_buffer, 108 const PaStreamCallbackTimeInfo * timeInfo, 109 PaStreamCallbackFlags statusFlags, 110 void * userData ) { 111 112 /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */ 113 114 (void) timeInfo; /* Prevent unused variable warnings. */ 115 (void) statusFlags; 116 (void) userData; 117 (void) frames_per_buffer; 118 (void) inputBuffer; 119 120 // simplified volume control 121 uint16_t index; 122 int16_t * from_buffer = output_buffers[output_buffer_to_play]; 123 int16_t * to_buffer = (int16_t *) outputBuffer; 124 btstack_assert(frames_per_buffer == NUM_FRAMES_PER_PA_BUFFER); 125 126 #if 0 127 // up to 8 right shifts 128 int right_shift = 8 - btstack_min(8, ((sink_volume + 15) / 16)); 129 for (index = 0; index < (NUM_FRAMES_PER_PA_BUFFER * num_channels_sink); index++){ 130 *to_buffer++ = (*from_buffer++) >> right_shift; 131 } 132 #else 133 // multiply with volume ^ 4 134 int16_t x2 = sink_volume * sink_volume; 135 int16_t x4 = (x2 * x2) >> 14; 136 for (index = 0; index < (NUM_FRAMES_PER_PA_BUFFER * num_channels_sink); index++){ 137 *to_buffer++ = ((*from_buffer++) * x4) >> 14; 138 } 139 #endif 140 141 // next 142 output_buffer_to_play = (output_buffer_to_play + 1 ) % NUM_OUTPUT_BUFFERS; 143 144 return 0; 145 } 146 147 static int portaudio_callback_source( const void * inputBuffer, 148 void * outputBuffer, 149 unsigned long samples_per_buffer, 150 const PaStreamCallbackTimeInfo * timeInfo, 151 PaStreamCallbackFlags statusFlags, 152 void * userData ) { 153 154 /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */ 155 156 (void) timeInfo; /* Prevent unused variable warnings. */ 157 (void) statusFlags; 158 (void) userData; 159 (void) samples_per_buffer; 160 (void) outputBuffer; 161 162 // store in one of our buffers 163 memcpy(input_buffers[input_buffer_to_fill], inputBuffer, NUM_FRAMES_PER_PA_BUFFER * num_bytes_per_sample_source); 164 165 // next 166 input_buffer_to_fill = (input_buffer_to_fill + 1 ) % NUM_INPUT_BUFFERS; 167 168 return 0; 169 } 170 171 static void driver_timer_handler_sink(btstack_timer_source_t * ts){ 172 173 // playback buffer ready to fill 174 while (output_buffer_to_play != output_buffer_to_fill){ 175 (*playback_callback)(output_buffers[output_buffer_to_fill], NUM_FRAMES_PER_PA_BUFFER); 176 177 // next 178 output_buffer_to_fill = (output_buffer_to_fill + 1 ) % NUM_OUTPUT_BUFFERS; 179 } 180 181 // re-set timer 182 btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS); 183 btstack_run_loop_add_timer(ts); 184 } 185 186 static void driver_timer_handler_source(btstack_timer_source_t * ts){ 187 188 // recording buffer ready to process 189 if (input_buffer_to_record != input_buffer_to_fill){ 190 191 (*recording_callback)(input_buffers[input_buffer_to_record], NUM_FRAMES_PER_PA_BUFFER); 192 193 // next 194 input_buffer_to_record = (input_buffer_to_record + 1 ) % NUM_INPUT_BUFFERS; 195 } 196 197 // re-set timer 198 btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS); 199 btstack_run_loop_add_timer(ts); 200 } 201 202 static int btstack_audio_portaudio_sink_init( 203 uint8_t channels, 204 uint32_t samplerate, 205 void (*playback)(int16_t * buffer, uint16_t num_samples) 206 ){ 207 PaError err; 208 209 btstack_assert(channels <= MAX_NR_AUDIO_CHANNELS); 210 211 num_channels_sink = channels; 212 num_bytes_per_sample_sink = 2 * channels; 213 214 if (!playback){ 215 log_error("No playback callback"); 216 return 1; 217 } 218 219 for (int i=0;i<NUM_OUTPUT_BUFFERS;i++){ 220 output_buffers[i] = &output_buffer_storage[i * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS]; 221 } 222 223 /* -- initialize PortAudio -- */ 224 if (!portaudio_initialized){ 225 err = Pa_Initialize(); 226 if (err != paNoError){ 227 log_error("Portudio: error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 228 return err; 229 } 230 portaudio_initialized = 1; 231 } 232 233 /* -- find output device by name if requested -- */ 234 PaDeviceIndex device_index = -1; 235 const PaDeviceInfo *output_device_info; 236 if (sink_device_name != NULL){ 237 int num_devices = Pa_GetDeviceCount(); 238 for (int i = 0; i < num_devices; i++) { 239 output_device_info = Pa_GetDeviceInfo(i); 240 // Match device by prefix 241 if (strncmp(output_device_info->name, sink_device_name, strlen(sink_device_name)) == 0) { 242 device_index = i; 243 break; 244 } 245 } 246 } 247 248 /* -- use default device otherwise -- */ 249 if (device_index < 0){ 250 device_index = Pa_GetDefaultOutputDevice(); 251 output_device_info = Pa_GetDeviceInfo(device_index ); 252 } 253 /* -- setup output -- */ 254 PaStreamParameters output_parameters; 255 output_parameters.device = device_index; 256 output_parameters.channelCount = channels; 257 output_parameters.sampleFormat = PA_SAMPLE_TYPE; 258 output_parameters.suggestedLatency = output_device_info->defaultHighOutputLatency; 259 output_parameters.hostApiSpecificStreamInfo = NULL; 260 261 log_info("PortAudio: sink device: %s", output_device_info->name); 262 UNUSED(output_device_info); 263 264 /* -- setup stream -- */ 265 err = Pa_OpenStream( 266 &stream_sink, 267 NULL, 268 &output_parameters, 269 samplerate, 270 NUM_FRAMES_PER_PA_BUFFER, 271 paClipOff, /* we won't output out of range samples so don't bother clipping them */ 272 portaudio_callback_sink, /* use callback */ 273 NULL ); 274 275 if (err != paNoError){ 276 log_error("Portudio: error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 277 return err; 278 } 279 log_info("PortAudio: sink stream created"); 280 281 const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream_sink); 282 log_info("PortAudio: sink latency: %f", stream_info->outputLatency); 283 UNUSED(stream_info); 284 285 playback_callback = playback; 286 287 sink_initialized = 1; 288 sink_volume = 127; 289 290 return 0; 291 } 292 293 static int btstack_audio_portaudio_source_init( 294 uint8_t channels, 295 uint32_t samplerate, 296 void (*recording)(const int16_t * buffer, uint16_t num_samples) 297 ){ 298 PaError err; 299 300 btstack_assert(channels <= MAX_NR_AUDIO_CHANNELS); 301 302 num_channels_source = channels; 303 num_bytes_per_sample_source = 2 * channels; 304 305 if (!recording){ 306 log_error("No recording callback"); 307 return 1; 308 } 309 310 for (int i=0;i<NUM_INPUT_BUFFERS;i++){ 311 input_buffers[i] = &input_buffer_storage[i * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS]; 312 } 313 314 /* -- initialize PortAudio -- */ 315 if (!portaudio_initialized){ 316 err = Pa_Initialize(); 317 if (err != paNoError){ 318 log_error("Portudio: Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 319 return err; 320 } 321 portaudio_initialized = 1; 322 } 323 324 /* -- find input device by name if requested -- */ 325 PaDeviceIndex device_index = -1; 326 const PaDeviceInfo *input_device_info; 327 if (source_device_name != NULL){ 328 int num_devices = Pa_GetDeviceCount(); 329 for (int i = 0; i < num_devices; i++) { 330 input_device_info = Pa_GetDeviceInfo(i); 331 // Match device by prefix 332 if (strncmp(input_device_info->name, source_device_name, strlen(source_device_name)) == 0) { 333 device_index = i; 334 break; 335 } 336 } 337 } 338 339 /* -- use default device otherwise -- */ 340 if (device_index < 0){ 341 device_index = Pa_GetDefaultInputDevice(); 342 input_device_info = Pa_GetDeviceInfo(device_index ); 343 } 344 345 /* -- setup input -- */ 346 PaStreamParameters theInputParameters; 347 theInputParameters.device = device_index; 348 theInputParameters.channelCount = channels; 349 theInputParameters.sampleFormat = PA_SAMPLE_TYPE; 350 theInputParameters.suggestedLatency = input_device_info->defaultHighInputLatency; 351 theInputParameters.hostApiSpecificStreamInfo = NULL; 352 353 log_info("PortAudio: source device: %s", input_device_info->name); 354 UNUSED(input_device_info); 355 356 /* -- setup stream -- */ 357 err = Pa_OpenStream( 358 &stream_source, 359 &theInputParameters, 360 NULL, 361 samplerate, 362 NUM_FRAMES_PER_PA_BUFFER, 363 paClipOff, /* we won't output out of range samples so don't bother clipping them */ 364 portaudio_callback_source, /* use callback */ 365 NULL ); 366 367 if (err != paNoError){ 368 log_error("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 369 return err; 370 } 371 log_info("PortAudio: source stream created"); 372 373 const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream_source); 374 log_info("PortAudio: source latency: %f", stream_info->inputLatency); 375 UNUSED(stream_info); 376 377 recording_callback = recording; 378 379 source_initialized = 1; 380 381 return 0; 382 } 383 384 static uint32_t btstack_audio_portaudio_sink_get_samplerate(void) { 385 const PaStreamInfo *stream_info = Pa_GetStreamInfo(stream_sink); 386 return stream_info->sampleRate; 387 } 388 389 static uint32_t btstack_audio_portaudio_source_get_samplerate(void) { 390 const PaStreamInfo *stream_info = Pa_GetStreamInfo(stream_source); 391 return stream_info->sampleRate; 392 } 393 394 static void btstack_audio_portaudio_sink_set_volume(uint8_t volume){ 395 sink_volume = volume; 396 } 397 398 static void btstack_audio_portaudio_source_set_gain(uint8_t gain){ 399 UNUSED(gain); 400 } 401 402 static void btstack_audio_portaudio_sink_start_stream(void){ 403 404 if (!playback_callback) return; 405 406 // fill buffers once 407 uint8_t i; 408 for (i=0;i<NUM_OUTPUT_BUFFERS-1;i++){ 409 (*playback_callback)(&output_buffer_storage[i * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS], NUM_FRAMES_PER_PA_BUFFER); 410 } 411 output_buffer_to_play = 0; 412 output_buffer_to_fill = NUM_OUTPUT_BUFFERS-1; 413 414 /* -- start stream -- */ 415 PaError err = Pa_StartStream(stream_sink); 416 if (err != paNoError){ 417 log_error("PortAudio: error starting sink stream: \"%s\"\n", Pa_GetErrorText(err)); 418 return; 419 } 420 421 // start timer 422 btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink); 423 btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS); 424 btstack_run_loop_add_timer(&driver_timer_sink); 425 426 sink_active = 1; 427 } 428 429 static void btstack_audio_portaudio_source_start_stream(void){ 430 431 if (!recording_callback) return; 432 433 /* -- start stream -- */ 434 PaError err = Pa_StartStream(stream_source); 435 if (err != paNoError){ 436 log_error("PortAudio: error starting source stream: \"%s\"\n", Pa_GetErrorText(err)); 437 return; 438 } 439 440 // start timer 441 btstack_run_loop_set_timer_handler(&driver_timer_source, &driver_timer_handler_source); 442 btstack_run_loop_set_timer(&driver_timer_source, DRIVER_POLL_INTERVAL_MS); 443 btstack_run_loop_add_timer(&driver_timer_source); 444 445 source_active = 1; 446 } 447 448 static void btstack_audio_portaudio_sink_stop_stream(void){ 449 450 if (!playback_callback) return; 451 if (!sink_active) return; 452 453 // stop timer 454 btstack_run_loop_remove_timer(&driver_timer_sink); 455 456 PaError err = Pa_StopStream(stream_sink); 457 if (err != paNoError){ 458 log_error("PortAudio: error stopping sink stream: \"%s\"", Pa_GetErrorText(err)); 459 return; 460 } 461 462 sink_active = 0; 463 } 464 465 static void btstack_audio_portaudio_source_stop_stream(void){ 466 467 if (!recording_callback) return; 468 if (!source_active) return; 469 470 // stop timer 471 btstack_run_loop_remove_timer(&driver_timer_source); 472 473 PaError err = Pa_StopStream(stream_source); 474 if (err != paNoError){ 475 log_error("PortAudio: error stopping source stream: \"%s\"", Pa_GetErrorText(err)); 476 return; 477 } 478 479 source_active = 0; 480 } 481 482 static void btstack_audio_portaudio_close_pa_if_not_needed(void){ 483 if (source_initialized) return; 484 if (sink_initialized) return; 485 PaError err = Pa_Terminate(); 486 if (err != paNoError){ 487 log_error("Portudio: Error terminating portaudio: \"%s\"", Pa_GetErrorText(err)); 488 return; 489 } 490 portaudio_initialized = 0; 491 } 492 493 static void btstack_audio_portaudio_sink_close(void){ 494 495 if (!playback_callback) return; 496 497 if (sink_active){ 498 btstack_audio_portaudio_sink_stop_stream(); 499 } 500 501 PaError err = Pa_CloseStream(stream_sink); 502 if (err != paNoError){ 503 log_error("PortAudio: error closing sink stream: \"%s\"", Pa_GetErrorText(err)); 504 return; 505 } 506 507 sink_initialized = 0; 508 btstack_audio_portaudio_close_pa_if_not_needed(); 509 } 510 511 static void btstack_audio_portaudio_source_close(void){ 512 513 if (!recording_callback) return; 514 515 if (source_active){ 516 btstack_audio_portaudio_source_stop_stream(); 517 } 518 519 PaError err = Pa_CloseStream(stream_source); 520 if (err != paNoError){ 521 log_error("PortAudio: error closing source stream: \"%s\"", Pa_GetErrorText(err)); 522 return; 523 } 524 525 source_initialized = 0; 526 btstack_audio_portaudio_close_pa_if_not_needed(); 527 } 528 529 static const btstack_audio_sink_t btstack_audio_portaudio_sink = { 530 /* int (*init)(..);*/ &btstack_audio_portaudio_sink_init, 531 /* uint32_t (*get_samplerate)() */ &btstack_audio_portaudio_sink_get_samplerate, 532 /* void (*set_volume)(uint8_t volume); */ &btstack_audio_portaudio_sink_set_volume, 533 /* void (*start_stream(void));*/ &btstack_audio_portaudio_sink_start_stream, 534 /* void (*stop_stream)(void) */ &btstack_audio_portaudio_sink_stop_stream, 535 /* void (*close)(void); */ &btstack_audio_portaudio_sink_close 536 }; 537 538 static const btstack_audio_source_t btstack_audio_portaudio_source = { 539 /* int (*init)(..);*/ &btstack_audio_portaudio_source_init, 540 /* uint32_t (*get_samplerate)() */ &btstack_audio_portaudio_source_get_samplerate, 541 /* void (*set_gain)(uint8_t gain); */ &btstack_audio_portaudio_source_set_gain, 542 /* void (*start_stream(void));*/ &btstack_audio_portaudio_source_start_stream, 543 /* void (*stop_stream)(void) */ &btstack_audio_portaudio_source_stop_stream, 544 /* void (*close)(void); */ &btstack_audio_portaudio_source_close 545 }; 546 547 const btstack_audio_sink_t * btstack_audio_portaudio_sink_get_instance(void){ 548 return &btstack_audio_portaudio_sink; 549 } 550 551 const btstack_audio_source_t * btstack_audio_portaudio_source_get_instance(void){ 552 return &btstack_audio_portaudio_source; 553 } 554 555 void btstack_audio_portaudio_sink_set_device(const char * device_name){ 556 sink_device_name = device_name; 557 } 558 559 void btstack_audio_portaudio_source_set_device(const char * device_name){ 560 source_device_name = device_name; 561 } 562 563 #endif 564