1a89df2dfSMatthias Ringwald /* 2a89df2dfSMatthias Ringwald * Copyright (C) 2017 BlueKitchen GmbH 3a89df2dfSMatthias Ringwald * 4a89df2dfSMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5a89df2dfSMatthias Ringwald * modification, are permitted provided that the following conditions 6a89df2dfSMatthias Ringwald * are met: 7a89df2dfSMatthias Ringwald * 8a89df2dfSMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9a89df2dfSMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10a89df2dfSMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11a89df2dfSMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12a89df2dfSMatthias Ringwald * documentation and/or other materials provided with the distribution. 13a89df2dfSMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14a89df2dfSMatthias Ringwald * contributors may be used to endorse or promote products derived 15a89df2dfSMatthias Ringwald * from this software without specific prior written permission. 16a89df2dfSMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17a89df2dfSMatthias Ringwald * personal benefit and not for any commercial purpose or for 18a89df2dfSMatthias Ringwald * monetary gain. 19a89df2dfSMatthias Ringwald * 20a89df2dfSMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21a89df2dfSMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22a89df2dfSMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23a89df2dfSMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24a89df2dfSMatthias Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25a89df2dfSMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26a89df2dfSMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27a89df2dfSMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28a89df2dfSMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29a89df2dfSMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30a89df2dfSMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31a89df2dfSMatthias Ringwald * SUCH DAMAGE. 32a89df2dfSMatthias Ringwald * 33a89df2dfSMatthias Ringwald * Please inquire about commercial licensing options at 34a89df2dfSMatthias Ringwald * [email protected] 35a89df2dfSMatthias Ringwald * 36a89df2dfSMatthias Ringwald */ 37a89df2dfSMatthias Ringwald 38a89df2dfSMatthias Ringwald #define __BTSTACK_FILE__ "btstack_audio_portaudio.c" 39a89df2dfSMatthias Ringwald 40a89df2dfSMatthias Ringwald 41a89df2dfSMatthias Ringwald #include <stdint.h> 42a89df2dfSMatthias Ringwald #include <string.h> 43a89df2dfSMatthias Ringwald #include "btstack_debug.h" 44a89df2dfSMatthias Ringwald #include "btstack_audio.h" 45a89df2dfSMatthias Ringwald #include "btstack_run_loop.h" 46a89df2dfSMatthias Ringwald 47a89df2dfSMatthias Ringwald #ifdef HAVE_PORTAUDIO 48a89df2dfSMatthias Ringwald 49a89df2dfSMatthias Ringwald #define PA_SAMPLE_TYPE paInt16 50a89df2dfSMatthias Ringwald #define NUM_FRAMES_PER_PA_BUFFER 512 51a89df2dfSMatthias Ringwald #define NUM_OUTPUT_BUFFERS 3 52a89df2dfSMatthias Ringwald #define NUM_INPUT_BUFFERS 2 53a89df2dfSMatthias Ringwald #define DRIVER_POLL_INTERVAL_MS 5 54a89df2dfSMatthias Ringwald 55a89df2dfSMatthias Ringwald #include <portaudio.h> 56a89df2dfSMatthias Ringwald 57a89df2dfSMatthias Ringwald // config 58a89df2dfSMatthias Ringwald static int num_channels; 59a89df2dfSMatthias Ringwald static int num_bytes_per_sample; 60a89df2dfSMatthias Ringwald 61a89df2dfSMatthias Ringwald // portaudio 62*3d3351f3SMatthias Ringwald static int portaudio_initialized; 63*3d3351f3SMatthias Ringwald 64*3d3351f3SMatthias Ringwald // state 65*3d3351f3SMatthias Ringwald static int source_initialized; 66*3d3351f3SMatthias Ringwald static int sink_initialized; 67*3d3351f3SMatthias Ringwald static int source_active; 68*3d3351f3SMatthias Ringwald static int sink_active; 69*3d3351f3SMatthias Ringwald 70*3d3351f3SMatthias Ringwald static PaStream * stream_source; 71*3d3351f3SMatthias Ringwald static PaStream * stream_sink; 72a89df2dfSMatthias Ringwald 73a89df2dfSMatthias Ringwald // client 74a89df2dfSMatthias Ringwald static void (*playback_callback)(int16_t * buffer, uint16_t num_samples); 75a89df2dfSMatthias Ringwald static void (*recording_callback)(const int16_t * buffer, uint16_t num_samples); 76a89df2dfSMatthias Ringwald 77a89df2dfSMatthias Ringwald // output buffer 78a89df2dfSMatthias Ringwald static int16_t output_buffer_a[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 79a89df2dfSMatthias Ringwald static int16_t output_buffer_b[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 80a89df2dfSMatthias Ringwald static int16_t output_buffer_c[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 81a89df2dfSMatthias Ringwald static int16_t * output_buffers[NUM_OUTPUT_BUFFERS] = { output_buffer_a, output_buffer_b, output_buffer_c}; 82a89df2dfSMatthias Ringwald static int output_buffer_to_play; 83a89df2dfSMatthias Ringwald static int output_buffer_to_fill; 84a89df2dfSMatthias Ringwald 85a89df2dfSMatthias Ringwald // input buffer 86a89df2dfSMatthias Ringwald static int16_t input_buffer_a[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 87a89df2dfSMatthias Ringwald static int16_t input_buffer_b[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 88a89df2dfSMatthias Ringwald static int16_t * input_buffers[NUM_INPUT_BUFFERS] = { input_buffer_a, input_buffer_b}; 89a89df2dfSMatthias Ringwald static int input_buffer_to_record; 90a89df2dfSMatthias Ringwald static int input_buffer_to_fill; 91a89df2dfSMatthias Ringwald 92a89df2dfSMatthias Ringwald 93a89df2dfSMatthias Ringwald // timer to fill output ring buffer 94*3d3351f3SMatthias Ringwald static btstack_timer_source_t driver_timer_sink; 95*3d3351f3SMatthias Ringwald static btstack_timer_source_t driver_timer_source; 96a89df2dfSMatthias Ringwald 97*3d3351f3SMatthias Ringwald static int portaudio_callback_sink( const void * inputBuffer, 98a89df2dfSMatthias Ringwald void * outputBuffer, 99a89df2dfSMatthias Ringwald unsigned long samples_per_buffer, 100a89df2dfSMatthias Ringwald const PaStreamCallbackTimeInfo * timeInfo, 101a89df2dfSMatthias Ringwald PaStreamCallbackFlags statusFlags, 102a89df2dfSMatthias Ringwald void * userData ) { 103a89df2dfSMatthias Ringwald 104a89df2dfSMatthias Ringwald /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */ 105a89df2dfSMatthias Ringwald 106a89df2dfSMatthias Ringwald (void) timeInfo; /* Prevent unused variable warnings. */ 107a89df2dfSMatthias Ringwald (void) statusFlags; 108a89df2dfSMatthias Ringwald (void) userData; 109a89df2dfSMatthias Ringwald (void) samples_per_buffer; 110*3d3351f3SMatthias Ringwald (void) inputBuffer; 111a89df2dfSMatthias Ringwald 112a89df2dfSMatthias Ringwald // fill from one of our buffers 113a89df2dfSMatthias Ringwald memcpy(outputBuffer, output_buffers[output_buffer_to_play], NUM_FRAMES_PER_PA_BUFFER * num_bytes_per_sample); 114a89df2dfSMatthias Ringwald 115a89df2dfSMatthias Ringwald // next 116a89df2dfSMatthias Ringwald output_buffer_to_play = (output_buffer_to_play + 1 ) % NUM_OUTPUT_BUFFERS; 117*3d3351f3SMatthias Ringwald 118*3d3351f3SMatthias Ringwald return 0; 119a89df2dfSMatthias Ringwald } 120a89df2dfSMatthias Ringwald 121*3d3351f3SMatthias Ringwald static int portaudio_callback_source( const void * inputBuffer, 122*3d3351f3SMatthias Ringwald void * outputBuffer, 123*3d3351f3SMatthias Ringwald unsigned long samples_per_buffer, 124*3d3351f3SMatthias Ringwald const PaStreamCallbackTimeInfo * timeInfo, 125*3d3351f3SMatthias Ringwald PaStreamCallbackFlags statusFlags, 126*3d3351f3SMatthias Ringwald void * userData ) { 127*3d3351f3SMatthias Ringwald 128*3d3351f3SMatthias Ringwald /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */ 129*3d3351f3SMatthias Ringwald 130*3d3351f3SMatthias Ringwald (void) timeInfo; /* Prevent unused variable warnings. */ 131*3d3351f3SMatthias Ringwald (void) statusFlags; 132*3d3351f3SMatthias Ringwald (void) userData; 133*3d3351f3SMatthias Ringwald (void) samples_per_buffer; 134*3d3351f3SMatthias Ringwald (void) outputBuffer; 135a89df2dfSMatthias Ringwald 136a89df2dfSMatthias Ringwald // store in one of our buffers 137a89df2dfSMatthias Ringwald memcpy(input_buffers[input_buffer_to_fill], inputBuffer, NUM_FRAMES_PER_PA_BUFFER * num_bytes_per_sample); 138a89df2dfSMatthias Ringwald 139a89df2dfSMatthias Ringwald // next 140a89df2dfSMatthias Ringwald input_buffer_to_fill = (input_buffer_to_fill + 1 ) % NUM_INPUT_BUFFERS; 141a89df2dfSMatthias Ringwald 142a89df2dfSMatthias Ringwald return 0; 143a89df2dfSMatthias Ringwald } 144a89df2dfSMatthias Ringwald 145*3d3351f3SMatthias Ringwald static void driver_timer_handler_sink(btstack_timer_source_t * ts){ 146a89df2dfSMatthias Ringwald 147a89df2dfSMatthias Ringwald // playback buffer ready to fill 148*3d3351f3SMatthias Ringwald if (output_buffer_to_play != output_buffer_to_fill){ 149a89df2dfSMatthias Ringwald (*playback_callback)(output_buffers[output_buffer_to_fill], NUM_FRAMES_PER_PA_BUFFER); 150a89df2dfSMatthias Ringwald 151a89df2dfSMatthias Ringwald // next 152a89df2dfSMatthias Ringwald output_buffer_to_fill = (output_buffer_to_fill + 1 ) % NUM_OUTPUT_BUFFERS; 153a89df2dfSMatthias Ringwald } 154a89df2dfSMatthias Ringwald 155*3d3351f3SMatthias Ringwald // re-set timer 156*3d3351f3SMatthias Ringwald btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS); 157*3d3351f3SMatthias Ringwald btstack_run_loop_add_timer(ts); 158*3d3351f3SMatthias Ringwald } 159*3d3351f3SMatthias Ringwald 160*3d3351f3SMatthias Ringwald static void driver_timer_handler_source(btstack_timer_source_t * ts){ 161*3d3351f3SMatthias Ringwald 162a89df2dfSMatthias Ringwald // recording buffer ready to process 163*3d3351f3SMatthias Ringwald if (input_buffer_to_record != input_buffer_to_fill){ 164a89df2dfSMatthias Ringwald 165a89df2dfSMatthias Ringwald (*recording_callback)(input_buffers[input_buffer_to_record], NUM_FRAMES_PER_PA_BUFFER); 166a89df2dfSMatthias Ringwald 167a89df2dfSMatthias Ringwald // next 168a89df2dfSMatthias Ringwald input_buffer_to_record = (input_buffer_to_record + 1 ) % NUM_INPUT_BUFFERS; 169a89df2dfSMatthias Ringwald } 170a89df2dfSMatthias Ringwald 171a89df2dfSMatthias Ringwald // re-set timer 172a89df2dfSMatthias Ringwald btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS); 173a89df2dfSMatthias Ringwald btstack_run_loop_add_timer(ts); 174a89df2dfSMatthias Ringwald } 175a89df2dfSMatthias Ringwald 176*3d3351f3SMatthias Ringwald static int btstack_audio_portaudio_sink_init( 177a89df2dfSMatthias Ringwald uint8_t channels, 178a89df2dfSMatthias Ringwald uint32_t samplerate, 179*3d3351f3SMatthias Ringwald void (*playback)(int16_t * buffer, uint16_t num_samples) 180a89df2dfSMatthias Ringwald ){ 181*3d3351f3SMatthias Ringwald PaError err; 182a89df2dfSMatthias Ringwald 183a89df2dfSMatthias Ringwald num_channels = channels; 184a89df2dfSMatthias Ringwald num_bytes_per_sample = 2 * channels; 185a89df2dfSMatthias Ringwald 186*3d3351f3SMatthias Ringwald if (!playback){ 187*3d3351f3SMatthias Ringwald log_error("No playback callback"); 188*3d3351f3SMatthias Ringwald return 1; 189*3d3351f3SMatthias Ringwald } 190*3d3351f3SMatthias Ringwald 191a89df2dfSMatthias Ringwald /* -- initialize PortAudio -- */ 192*3d3351f3SMatthias Ringwald if (!portaudio_initialized){ 193*3d3351f3SMatthias Ringwald err = Pa_Initialize(); 194a89df2dfSMatthias Ringwald if (err != paNoError){ 195a89df2dfSMatthias Ringwald log_error("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 196a89df2dfSMatthias Ringwald return err; 197a89df2dfSMatthias Ringwald } 198*3d3351f3SMatthias Ringwald portaudio_initialized = 1; 199*3d3351f3SMatthias Ringwald } 200a89df2dfSMatthias Ringwald 201a89df2dfSMatthias Ringwald /* -- setup output -- */ 202*3d3351f3SMatthias Ringwald PaStreamParameters theOutputParameters; 203a89df2dfSMatthias Ringwald theOutputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ 204a89df2dfSMatthias Ringwald theOutputParameters.channelCount = channels; 205a89df2dfSMatthias Ringwald theOutputParameters.sampleFormat = PA_SAMPLE_TYPE; 206a89df2dfSMatthias Ringwald theOutputParameters.suggestedLatency = Pa_GetDeviceInfo( theOutputParameters.device )->defaultHighOutputLatency; 207a89df2dfSMatthias Ringwald theOutputParameters.hostApiSpecificStreamInfo = NULL; 208a89df2dfSMatthias Ringwald 209a89df2dfSMatthias Ringwald const PaDeviceInfo *outputDeviceInfo; 210a89df2dfSMatthias Ringwald outputDeviceInfo = Pa_GetDeviceInfo( theOutputParameters.device ); 211a89df2dfSMatthias Ringwald log_info("PortAudio: Output device: %s", outputDeviceInfo->name); 212a89df2dfSMatthias Ringwald 213*3d3351f3SMatthias Ringwald /* -- setup stream -- */ 214*3d3351f3SMatthias Ringwald err = Pa_OpenStream( 215*3d3351f3SMatthias Ringwald &stream_source, 216*3d3351f3SMatthias Ringwald NULL, 217*3d3351f3SMatthias Ringwald &theOutputParameters, 218*3d3351f3SMatthias Ringwald samplerate, 219*3d3351f3SMatthias Ringwald NUM_FRAMES_PER_PA_BUFFER, 220*3d3351f3SMatthias Ringwald paClipOff, /* we won't output out of range samples so don't bother clipping them */ 221*3d3351f3SMatthias Ringwald portaudio_callback_sink, /* use callback */ 222*3d3351f3SMatthias Ringwald NULL ); 223*3d3351f3SMatthias Ringwald 224*3d3351f3SMatthias Ringwald if (err != paNoError){ 225*3d3351f3SMatthias Ringwald log_error("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 226*3d3351f3SMatthias Ringwald return err; 227*3d3351f3SMatthias Ringwald } 228*3d3351f3SMatthias Ringwald log_info("PortAudio: stream opened"); 229*3d3351f3SMatthias Ringwald 230*3d3351f3SMatthias Ringwald const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream_source); 231*3d3351f3SMatthias Ringwald log_info("PortAudio: Output latency: %f", stream_info->outputLatency); 232*3d3351f3SMatthias Ringwald 233*3d3351f3SMatthias Ringwald playback_callback = playback; 234*3d3351f3SMatthias Ringwald 235*3d3351f3SMatthias Ringwald sink_initialized = 1; 236*3d3351f3SMatthias Ringwald 237*3d3351f3SMatthias Ringwald return 0; 238*3d3351f3SMatthias Ringwald } 239*3d3351f3SMatthias Ringwald 240*3d3351f3SMatthias Ringwald static int btstack_audio_portaudio_source_init( 241*3d3351f3SMatthias Ringwald uint8_t channels, 242*3d3351f3SMatthias Ringwald uint32_t samplerate, 243*3d3351f3SMatthias Ringwald void (*recording)(const int16_t * buffer, uint16_t num_samples) 244*3d3351f3SMatthias Ringwald ){ 245*3d3351f3SMatthias Ringwald PaError err; 246*3d3351f3SMatthias Ringwald 247*3d3351f3SMatthias Ringwald num_channels = channels; 248*3d3351f3SMatthias Ringwald num_bytes_per_sample = 2 * channels; 249*3d3351f3SMatthias Ringwald 250*3d3351f3SMatthias Ringwald if (!recording){ 251*3d3351f3SMatthias Ringwald log_error("No recording callback"); 252*3d3351f3SMatthias Ringwald return 1; 253*3d3351f3SMatthias Ringwald } 254*3d3351f3SMatthias Ringwald 255*3d3351f3SMatthias Ringwald /* -- initialize PortAudio -- */ 256*3d3351f3SMatthias Ringwald if (!portaudio_initialized){ 257*3d3351f3SMatthias Ringwald err = Pa_Initialize(); 258*3d3351f3SMatthias Ringwald if (err != paNoError){ 259*3d3351f3SMatthias Ringwald log_error("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 260*3d3351f3SMatthias Ringwald return err; 261*3d3351f3SMatthias Ringwald } 262*3d3351f3SMatthias Ringwald portaudio_initialized = 1; 263a89df2dfSMatthias Ringwald } 264a89df2dfSMatthias Ringwald 265a89df2dfSMatthias Ringwald /* -- setup input -- */ 266*3d3351f3SMatthias Ringwald PaStreamParameters theInputParameters; 267a89df2dfSMatthias Ringwald theInputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ 268a89df2dfSMatthias Ringwald theInputParameters.channelCount = channels; 269a89df2dfSMatthias Ringwald theInputParameters.sampleFormat = PA_SAMPLE_TYPE; 270a89df2dfSMatthias Ringwald theInputParameters.suggestedLatency = Pa_GetDeviceInfo( theInputParameters.device )->defaultHighInputLatency; 271a89df2dfSMatthias Ringwald theInputParameters.hostApiSpecificStreamInfo = NULL; 272a89df2dfSMatthias Ringwald 273a89df2dfSMatthias Ringwald const PaDeviceInfo *inputDeviceInfo; 274a89df2dfSMatthias Ringwald inputDeviceInfo = Pa_GetDeviceInfo( theInputParameters.device ); 275a89df2dfSMatthias Ringwald log_info("PortAudio: Input device: %s", inputDeviceInfo->name); 276a89df2dfSMatthias Ringwald 277a89df2dfSMatthias Ringwald /* -- setup stream -- */ 278a89df2dfSMatthias Ringwald err = Pa_OpenStream( 279*3d3351f3SMatthias Ringwald &stream_sink, 280*3d3351f3SMatthias Ringwald &theInputParameters, 281*3d3351f3SMatthias Ringwald NULL, 282a89df2dfSMatthias Ringwald samplerate, 283a89df2dfSMatthias Ringwald NUM_FRAMES_PER_PA_BUFFER, 284a89df2dfSMatthias Ringwald paClipOff, /* we won't output out of range samples so don't bother clipping them */ 285*3d3351f3SMatthias Ringwald portaudio_callback_source, /* use callback */ 286a89df2dfSMatthias Ringwald NULL ); 287a89df2dfSMatthias Ringwald 288a89df2dfSMatthias Ringwald if (err != paNoError){ 289a89df2dfSMatthias Ringwald log_error("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 290a89df2dfSMatthias Ringwald return err; 291a89df2dfSMatthias Ringwald } 292a89df2dfSMatthias Ringwald log_info("PortAudio: stream opened"); 293a89df2dfSMatthias Ringwald 294*3d3351f3SMatthias Ringwald const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream_sink); 295a89df2dfSMatthias Ringwald log_info("PortAudio: Input latency: %f", stream_info->inputLatency); 296a89df2dfSMatthias Ringwald 297a89df2dfSMatthias Ringwald recording_callback = recording; 298a89df2dfSMatthias Ringwald 299*3d3351f3SMatthias Ringwald source_initialized = 1; 300*3d3351f3SMatthias Ringwald 301a89df2dfSMatthias Ringwald return 0; 302a89df2dfSMatthias Ringwald } 303a89df2dfSMatthias Ringwald 304*3d3351f3SMatthias Ringwald static void btstack_audio_portaudio_sink_start_stream(void){ 305*3d3351f3SMatthias Ringwald 306*3d3351f3SMatthias Ringwald if (!playback_callback) return; 307a89df2dfSMatthias Ringwald 308a89df2dfSMatthias Ringwald // fill buffer once 309a89df2dfSMatthias Ringwald (*playback_callback)(output_buffer_a, NUM_FRAMES_PER_PA_BUFFER); 310a89df2dfSMatthias Ringwald (*playback_callback)(output_buffer_b, NUM_FRAMES_PER_PA_BUFFER); 311a89df2dfSMatthias Ringwald output_buffer_to_play = 0; 312a89df2dfSMatthias Ringwald output_buffer_to_fill = 2; 313a89df2dfSMatthias Ringwald 314a89df2dfSMatthias Ringwald /* -- start stream -- */ 315*3d3351f3SMatthias Ringwald PaError err = Pa_StartStream(stream_source); 316a89df2dfSMatthias Ringwald if (err != paNoError){ 317a89df2dfSMatthias Ringwald log_error("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); 318a89df2dfSMatthias Ringwald return; 319a89df2dfSMatthias Ringwald } 320a89df2dfSMatthias Ringwald 321a89df2dfSMatthias Ringwald // start timer 322*3d3351f3SMatthias Ringwald btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink); 323*3d3351f3SMatthias Ringwald btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS); 324*3d3351f3SMatthias Ringwald btstack_run_loop_add_timer(&driver_timer_sink); 325*3d3351f3SMatthias Ringwald 326*3d3351f3SMatthias Ringwald sink_active = 1; 327a89df2dfSMatthias Ringwald } 328a89df2dfSMatthias Ringwald 329*3d3351f3SMatthias Ringwald static void btstack_audio_portaudio_source_start_stream(void){ 330*3d3351f3SMatthias Ringwald 331*3d3351f3SMatthias Ringwald if (!recording_callback) return; 332*3d3351f3SMatthias Ringwald 333*3d3351f3SMatthias Ringwald /* -- start stream -- */ 334*3d3351f3SMatthias Ringwald PaError err = Pa_StartStream(stream_sink); 335*3d3351f3SMatthias Ringwald if (err != paNoError){ 336*3d3351f3SMatthias Ringwald log_error("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); 337*3d3351f3SMatthias Ringwald return; 338*3d3351f3SMatthias Ringwald } 339*3d3351f3SMatthias Ringwald 340*3d3351f3SMatthias Ringwald // start timer 341*3d3351f3SMatthias Ringwald btstack_run_loop_set_timer_handler(&driver_timer_source, &driver_timer_handler_source); 342*3d3351f3SMatthias Ringwald btstack_run_loop_set_timer(&driver_timer_source, DRIVER_POLL_INTERVAL_MS); 343*3d3351f3SMatthias Ringwald btstack_run_loop_add_timer(&driver_timer_source); 344*3d3351f3SMatthias Ringwald 345*3d3351f3SMatthias Ringwald source_active = 1; 346*3d3351f3SMatthias Ringwald } 347*3d3351f3SMatthias Ringwald 348*3d3351f3SMatthias Ringwald static void btstack_audio_portaudio_sink_stop_stream(void){ 349*3d3351f3SMatthias Ringwald 350*3d3351f3SMatthias Ringwald if (!playback_callback) return; 351*3d3351f3SMatthias Ringwald if (!sink_active) return; 352a89df2dfSMatthias Ringwald 353a89df2dfSMatthias Ringwald // stop timer 354*3d3351f3SMatthias Ringwald btstack_run_loop_remove_timer(&driver_timer_sink); 355a89df2dfSMatthias Ringwald 356a89df2dfSMatthias Ringwald log_info("PortAudio: Stream closed"); 357*3d3351f3SMatthias Ringwald PaError err = Pa_StopStream(stream_sink); 358a89df2dfSMatthias Ringwald if (err != paNoError){ 359a89df2dfSMatthias Ringwald log_error("Error stopping the stream: \"%s\"", Pa_GetErrorText(err)); 360a89df2dfSMatthias Ringwald return; 361a89df2dfSMatthias Ringwald } 362*3d3351f3SMatthias Ringwald } 363*3d3351f3SMatthias Ringwald 364*3d3351f3SMatthias Ringwald static void btstack_audio_portaudio_source_stop_stream(void){ 365*3d3351f3SMatthias Ringwald 366*3d3351f3SMatthias Ringwald if (!recording_callback) return; 367*3d3351f3SMatthias Ringwald if (!source_active) return; 368*3d3351f3SMatthias Ringwald 369*3d3351f3SMatthias Ringwald // stop timer 370*3d3351f3SMatthias Ringwald btstack_run_loop_remove_timer(&driver_timer_source); 371*3d3351f3SMatthias Ringwald 372*3d3351f3SMatthias Ringwald log_info("PortAudio: Stream closed"); 373*3d3351f3SMatthias Ringwald PaError err = Pa_StopStream(stream_source); 374a89df2dfSMatthias Ringwald if (err != paNoError){ 375*3d3351f3SMatthias Ringwald log_error("Error stopping the stream: \"%s\"", Pa_GetErrorText(err)); 376a89df2dfSMatthias Ringwald return; 377a89df2dfSMatthias Ringwald } 378*3d3351f3SMatthias Ringwald } 379*3d3351f3SMatthias Ringwald 380*3d3351f3SMatthias Ringwald static void btstack_audio_portaudio_close_pa_if_not_needed(void){ 381*3d3351f3SMatthias Ringwald if (source_initialized) return; 382*3d3351f3SMatthias Ringwald if (sink_initialized) return; 383*3d3351f3SMatthias Ringwald PaError err = Pa_Terminate(); 384a89df2dfSMatthias Ringwald if (err != paNoError){ 385a89df2dfSMatthias Ringwald log_error("Error terminating portaudio: \"%s\"", Pa_GetErrorText(err)); 386a89df2dfSMatthias Ringwald return; 387a89df2dfSMatthias Ringwald } 388a89df2dfSMatthias Ringwald } 389a89df2dfSMatthias Ringwald 390*3d3351f3SMatthias Ringwald static void btstack_audio_portaudio_sink_close(void){ 391*3d3351f3SMatthias Ringwald 392*3d3351f3SMatthias Ringwald if (!playback_callback) return; 393*3d3351f3SMatthias Ringwald 394*3d3351f3SMatthias Ringwald if (sink_active){ 395*3d3351f3SMatthias Ringwald btstack_audio_portaudio_sink_stop_stream(); 396*3d3351f3SMatthias Ringwald } 397*3d3351f3SMatthias Ringwald 398*3d3351f3SMatthias Ringwald err = Pa_CloseStream(stream_sink); 399*3d3351f3SMatthias Ringwald if (err != paNoError){ 400*3d3351f3SMatthias Ringwald log_error("Error closing the stream: \"%s\"", Pa_GetErrorText(err)); 401*3d3351f3SMatthias Ringwald return; 402*3d3351f3SMatthias Ringwald } 403*3d3351f3SMatthias Ringwald 404*3d3351f3SMatthias Ringwald sink_initialized = 0; 405*3d3351f3SMatthias Ringwald btstack_audio_portaudio_close_pa_if_not_needed(); 406*3d3351f3SMatthias Ringwald } 407*3d3351f3SMatthias Ringwald 408*3d3351f3SMatthias Ringwald 409*3d3351f3SMatthias Ringwald static void btstack_audio_portaudio_source_close(void){ 410*3d3351f3SMatthias Ringwald 411*3d3351f3SMatthias Ringwald if (!recording_callback) return; 412*3d3351f3SMatthias Ringwald 413*3d3351f3SMatthias Ringwald if (source_active){ 414*3d3351f3SMatthias Ringwald btstack_audio_portaudio_sink_stop_stream(); 415*3d3351f3SMatthias Ringwald } 416*3d3351f3SMatthias Ringwald 417*3d3351f3SMatthias Ringwald err = Pa_CloseStream(stream_source); 418*3d3351f3SMatthias Ringwald if (err != paNoError){ 419*3d3351f3SMatthias Ringwald log_error("Error closing the stream: \"%s\"", Pa_GetErrorText(err)); 420*3d3351f3SMatthias Ringwald return; 421*3d3351f3SMatthias Ringwald } 422*3d3351f3SMatthias Ringwald 423*3d3351f3SMatthias Ringwald source_initialized = 0; 424*3d3351f3SMatthias Ringwald btstack_audio_portaudio_close_pa_if_not_needed(); 425*3d3351f3SMatthias Ringwald } 426*3d3351f3SMatthias Ringwald 427*3d3351f3SMatthias Ringwald static const btstack_audio_sink_t btstack_audio_portaudio_sink = { 428*3d3351f3SMatthias Ringwald /* int (*init)(..);*/ &btstack_audio_portaudio_sink_init, 429*3d3351f3SMatthias Ringwald /* void (*start_stream(void));*/ &btstack_audio_portaudio_sink_start_stream, 430*3d3351f3SMatthias Ringwald /* void (*stop_stream)(void) */ &btstack_audio_portaudio_sink_stop_stream; 431*3d3351f3SMatthias Ringwald /* void (*close)(void); */ &btstack_audio_portaudio_sink_close 432a89df2dfSMatthias Ringwald }; 433a89df2dfSMatthias Ringwald 434*3d3351f3SMatthias Ringwald static const btstack_audio_source_t btstack_audio_portaudio_source = { 435*3d3351f3SMatthias Ringwald /* int (*init)(..);*/ &btstack_audio_portaudio_source_init, 436*3d3351f3SMatthias Ringwald /* void (*start_stream(void));*/ &btstack_audio_portaudio_source_start_stream, 437*3d3351f3SMatthias Ringwald /* void (*stop_stream)(void) */ &btstack_audio_portaudio_source_stop_stream; 438*3d3351f3SMatthias Ringwald /* void (*close)(void); */ &btstack_audio_portaudio_source_close 439*3d3351f3SMatthias Ringwald }; 440*3d3351f3SMatthias Ringwald 441*3d3351f3SMatthias Ringwald const btstack_audio_sink_t * btstack_audio_portaudio_sink_get_instance(void){ 442*3d3351f3SMatthias Ringwald return &btstack_audio_portaudio_sink; 443*3d3351f3SMatthias Ringwald } 444*3d3351f3SMatthias Ringwald 445*3d3351f3SMatthias Ringwald const btstack_audio_source_t * btstack_audio_portaudio_source_get_instance(void){ 446*3d3351f3SMatthias Ringwald return &btstack_audio_portaudio_source; 447a89df2dfSMatthias Ringwald } 448a89df2dfSMatthias Ringwald 449a89df2dfSMatthias Ringwald #endif 450