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