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