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