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