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