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; 59*bcd51729SMatthias Ringwald static int num_bytes_per_sample_sink; 60*bcd51729SMatthias Ringwald static int num_bytes_per_sample_source; 61a89df2dfSMatthias Ringwald 62a89df2dfSMatthias Ringwald // portaudio 633d3351f3SMatthias Ringwald static int portaudio_initialized; 643d3351f3SMatthias Ringwald 653d3351f3SMatthias Ringwald // state 663d3351f3SMatthias Ringwald static int source_initialized; 673d3351f3SMatthias Ringwald static int sink_initialized; 683d3351f3SMatthias Ringwald static int source_active; 693d3351f3SMatthias Ringwald static int sink_active; 703d3351f3SMatthias Ringwald 713d3351f3SMatthias Ringwald static PaStream * stream_source; 723d3351f3SMatthias Ringwald static PaStream * stream_sink; 73a89df2dfSMatthias Ringwald 74a89df2dfSMatthias Ringwald // client 75a89df2dfSMatthias Ringwald static void (*playback_callback)(int16_t * buffer, uint16_t num_samples); 76a89df2dfSMatthias Ringwald static void (*recording_callback)(const int16_t * buffer, uint16_t num_samples); 77a89df2dfSMatthias Ringwald 78a89df2dfSMatthias Ringwald // output buffer 79a89df2dfSMatthias Ringwald static int16_t output_buffer_a[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 80a89df2dfSMatthias Ringwald static int16_t output_buffer_b[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 81a89df2dfSMatthias Ringwald static int16_t output_buffer_c[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 82a89df2dfSMatthias Ringwald static int16_t * output_buffers[NUM_OUTPUT_BUFFERS] = { output_buffer_a, output_buffer_b, output_buffer_c}; 83a89df2dfSMatthias Ringwald static int output_buffer_to_play; 84a89df2dfSMatthias Ringwald static int output_buffer_to_fill; 85a89df2dfSMatthias Ringwald 86a89df2dfSMatthias Ringwald // input buffer 87a89df2dfSMatthias Ringwald static int16_t input_buffer_a[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 88a89df2dfSMatthias Ringwald static int16_t input_buffer_b[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 89a89df2dfSMatthias Ringwald static int16_t * input_buffers[NUM_INPUT_BUFFERS] = { input_buffer_a, input_buffer_b}; 90a89df2dfSMatthias Ringwald static int input_buffer_to_record; 91a89df2dfSMatthias Ringwald static int input_buffer_to_fill; 92a89df2dfSMatthias Ringwald 93a89df2dfSMatthias Ringwald 94a89df2dfSMatthias Ringwald // timer to fill output ring buffer 953d3351f3SMatthias Ringwald static btstack_timer_source_t driver_timer_sink; 963d3351f3SMatthias Ringwald static btstack_timer_source_t driver_timer_source; 97a89df2dfSMatthias Ringwald 983d3351f3SMatthias Ringwald static int portaudio_callback_sink( const void * inputBuffer, 99a89df2dfSMatthias Ringwald void * outputBuffer, 100a89df2dfSMatthias Ringwald unsigned long samples_per_buffer, 101a89df2dfSMatthias Ringwald const PaStreamCallbackTimeInfo * timeInfo, 102a89df2dfSMatthias Ringwald PaStreamCallbackFlags statusFlags, 103a89df2dfSMatthias Ringwald void * userData ) { 104a89df2dfSMatthias Ringwald 105a89df2dfSMatthias Ringwald /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */ 106a89df2dfSMatthias Ringwald 107a89df2dfSMatthias Ringwald (void) timeInfo; /* Prevent unused variable warnings. */ 108a89df2dfSMatthias Ringwald (void) statusFlags; 109a89df2dfSMatthias Ringwald (void) userData; 110a89df2dfSMatthias Ringwald (void) samples_per_buffer; 1113d3351f3SMatthias Ringwald (void) inputBuffer; 112a89df2dfSMatthias Ringwald 113a89df2dfSMatthias Ringwald // fill from one of our buffers 114*bcd51729SMatthias Ringwald memcpy(outputBuffer, output_buffers[output_buffer_to_play], NUM_FRAMES_PER_PA_BUFFER * num_bytes_per_sample_sink); 115a89df2dfSMatthias Ringwald 116a89df2dfSMatthias Ringwald // next 117a89df2dfSMatthias Ringwald output_buffer_to_play = (output_buffer_to_play + 1 ) % NUM_OUTPUT_BUFFERS; 1183d3351f3SMatthias Ringwald 1193d3351f3SMatthias Ringwald return 0; 120a89df2dfSMatthias Ringwald } 121a89df2dfSMatthias Ringwald 1223d3351f3SMatthias Ringwald static int portaudio_callback_source( const void * inputBuffer, 1233d3351f3SMatthias Ringwald void * outputBuffer, 1243d3351f3SMatthias Ringwald unsigned long samples_per_buffer, 1253d3351f3SMatthias Ringwald const PaStreamCallbackTimeInfo * timeInfo, 1263d3351f3SMatthias Ringwald PaStreamCallbackFlags statusFlags, 1273d3351f3SMatthias Ringwald void * userData ) { 1283d3351f3SMatthias Ringwald 1293d3351f3SMatthias Ringwald /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */ 1303d3351f3SMatthias Ringwald 1313d3351f3SMatthias Ringwald (void) timeInfo; /* Prevent unused variable warnings. */ 1323d3351f3SMatthias Ringwald (void) statusFlags; 1333d3351f3SMatthias Ringwald (void) userData; 1343d3351f3SMatthias Ringwald (void) samples_per_buffer; 1353d3351f3SMatthias Ringwald (void) outputBuffer; 136a89df2dfSMatthias Ringwald 137a89df2dfSMatthias Ringwald // store in one of our buffers 138*bcd51729SMatthias Ringwald memcpy(input_buffers[input_buffer_to_fill], inputBuffer, NUM_FRAMES_PER_PA_BUFFER * num_bytes_per_sample_source); 139a89df2dfSMatthias Ringwald 140a89df2dfSMatthias Ringwald // next 141a89df2dfSMatthias Ringwald input_buffer_to_fill = (input_buffer_to_fill + 1 ) % NUM_INPUT_BUFFERS; 142a89df2dfSMatthias Ringwald 143a89df2dfSMatthias Ringwald return 0; 144a89df2dfSMatthias Ringwald } 145a89df2dfSMatthias Ringwald 1463d3351f3SMatthias Ringwald static void driver_timer_handler_sink(btstack_timer_source_t * ts){ 147a89df2dfSMatthias Ringwald 148a89df2dfSMatthias Ringwald // playback buffer ready to fill 1493d3351f3SMatthias Ringwald if (output_buffer_to_play != output_buffer_to_fill){ 150a89df2dfSMatthias Ringwald (*playback_callback)(output_buffers[output_buffer_to_fill], NUM_FRAMES_PER_PA_BUFFER); 151a89df2dfSMatthias Ringwald 152a89df2dfSMatthias Ringwald // next 153a89df2dfSMatthias Ringwald output_buffer_to_fill = (output_buffer_to_fill + 1 ) % NUM_OUTPUT_BUFFERS; 154a89df2dfSMatthias Ringwald } 155a89df2dfSMatthias Ringwald 1563d3351f3SMatthias Ringwald // re-set timer 1573d3351f3SMatthias Ringwald btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS); 1583d3351f3SMatthias Ringwald btstack_run_loop_add_timer(ts); 1593d3351f3SMatthias Ringwald } 1603d3351f3SMatthias Ringwald 1613d3351f3SMatthias Ringwald static void driver_timer_handler_source(btstack_timer_source_t * ts){ 1623d3351f3SMatthias Ringwald 163a89df2dfSMatthias Ringwald // recording buffer ready to process 1643d3351f3SMatthias Ringwald if (input_buffer_to_record != input_buffer_to_fill){ 165a89df2dfSMatthias Ringwald 166a89df2dfSMatthias Ringwald (*recording_callback)(input_buffers[input_buffer_to_record], NUM_FRAMES_PER_PA_BUFFER); 167a89df2dfSMatthias Ringwald 168a89df2dfSMatthias Ringwald // next 169a89df2dfSMatthias Ringwald input_buffer_to_record = (input_buffer_to_record + 1 ) % NUM_INPUT_BUFFERS; 170a89df2dfSMatthias Ringwald } 171a89df2dfSMatthias Ringwald 172a89df2dfSMatthias Ringwald // re-set timer 173a89df2dfSMatthias Ringwald btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS); 174a89df2dfSMatthias Ringwald btstack_run_loop_add_timer(ts); 175a89df2dfSMatthias Ringwald } 176a89df2dfSMatthias Ringwald 1773d3351f3SMatthias Ringwald static int btstack_audio_portaudio_sink_init( 178a89df2dfSMatthias Ringwald uint8_t channels, 179a89df2dfSMatthias Ringwald uint32_t samplerate, 1803d3351f3SMatthias Ringwald void (*playback)(int16_t * buffer, uint16_t num_samples) 181a89df2dfSMatthias Ringwald ){ 1823d3351f3SMatthias Ringwald PaError err; 183a89df2dfSMatthias Ringwald 184a89df2dfSMatthias Ringwald num_channels = channels; 185*bcd51729SMatthias Ringwald num_bytes_per_sample_sink = 2 * channels; 186a89df2dfSMatthias Ringwald 1873d3351f3SMatthias Ringwald if (!playback){ 1883d3351f3SMatthias Ringwald log_error("No playback callback"); 1893d3351f3SMatthias Ringwald return 1; 1903d3351f3SMatthias Ringwald } 1913d3351f3SMatthias Ringwald 192a89df2dfSMatthias Ringwald /* -- initialize PortAudio -- */ 1933d3351f3SMatthias Ringwald if (!portaudio_initialized){ 1943d3351f3SMatthias Ringwald err = Pa_Initialize(); 195a89df2dfSMatthias Ringwald if (err != paNoError){ 19624cc6cccSMatthias Ringwald log_error("Portudio: error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 197a89df2dfSMatthias Ringwald return err; 198a89df2dfSMatthias Ringwald } 1993d3351f3SMatthias Ringwald portaudio_initialized = 1; 2003d3351f3SMatthias Ringwald } 201a89df2dfSMatthias Ringwald 202a89df2dfSMatthias Ringwald /* -- setup output -- */ 2033d3351f3SMatthias Ringwald PaStreamParameters theOutputParameters; 204a89df2dfSMatthias Ringwald theOutputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ 205a89df2dfSMatthias Ringwald theOutputParameters.channelCount = channels; 206a89df2dfSMatthias Ringwald theOutputParameters.sampleFormat = PA_SAMPLE_TYPE; 207a89df2dfSMatthias Ringwald theOutputParameters.suggestedLatency = Pa_GetDeviceInfo( theOutputParameters.device )->defaultHighOutputLatency; 208a89df2dfSMatthias Ringwald theOutputParameters.hostApiSpecificStreamInfo = NULL; 209a89df2dfSMatthias Ringwald 210a89df2dfSMatthias Ringwald const PaDeviceInfo *outputDeviceInfo; 211a89df2dfSMatthias Ringwald outputDeviceInfo = Pa_GetDeviceInfo( theOutputParameters.device ); 21224cc6cccSMatthias Ringwald log_info("PortAudio: sink device: %s", outputDeviceInfo->name); 213a89df2dfSMatthias Ringwald 2143d3351f3SMatthias Ringwald /* -- setup stream -- */ 2153d3351f3SMatthias Ringwald err = Pa_OpenStream( 2163d3351f3SMatthias Ringwald &stream_source, 2173d3351f3SMatthias Ringwald NULL, 2183d3351f3SMatthias Ringwald &theOutputParameters, 2193d3351f3SMatthias Ringwald samplerate, 2203d3351f3SMatthias Ringwald NUM_FRAMES_PER_PA_BUFFER, 2213d3351f3SMatthias Ringwald paClipOff, /* we won't output out of range samples so don't bother clipping them */ 2223d3351f3SMatthias Ringwald portaudio_callback_sink, /* use callback */ 2233d3351f3SMatthias Ringwald NULL ); 2243d3351f3SMatthias Ringwald 2253d3351f3SMatthias Ringwald if (err != paNoError){ 22624cc6cccSMatthias Ringwald log_error("Portudio: error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 2273d3351f3SMatthias Ringwald return err; 2283d3351f3SMatthias Ringwald } 22924cc6cccSMatthias Ringwald log_info("PortAudio: sink stream created"); 2303d3351f3SMatthias Ringwald 2313d3351f3SMatthias Ringwald const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream_source); 23224cc6cccSMatthias Ringwald log_info("PortAudio: sink latency: %f", stream_info->outputLatency); 2333d3351f3SMatthias Ringwald 2343d3351f3SMatthias Ringwald playback_callback = playback; 2353d3351f3SMatthias Ringwald 2363d3351f3SMatthias Ringwald sink_initialized = 1; 2373d3351f3SMatthias Ringwald 2383d3351f3SMatthias Ringwald return 0; 2393d3351f3SMatthias Ringwald } 2403d3351f3SMatthias Ringwald 2413d3351f3SMatthias Ringwald static int btstack_audio_portaudio_source_init( 2423d3351f3SMatthias Ringwald uint8_t channels, 2433d3351f3SMatthias Ringwald uint32_t samplerate, 2443d3351f3SMatthias Ringwald void (*recording)(const int16_t * buffer, uint16_t num_samples) 2453d3351f3SMatthias Ringwald ){ 2463d3351f3SMatthias Ringwald PaError err; 2473d3351f3SMatthias Ringwald 2483d3351f3SMatthias Ringwald num_channels = channels; 249*bcd51729SMatthias Ringwald num_bytes_per_sample_source = 2 * channels; 2503d3351f3SMatthias Ringwald 2513d3351f3SMatthias Ringwald if (!recording){ 2523d3351f3SMatthias Ringwald log_error("No recording callback"); 2533d3351f3SMatthias Ringwald return 1; 2543d3351f3SMatthias Ringwald } 2553d3351f3SMatthias Ringwald 2563d3351f3SMatthias Ringwald /* -- initialize PortAudio -- */ 2573d3351f3SMatthias Ringwald if (!portaudio_initialized){ 2583d3351f3SMatthias Ringwald err = Pa_Initialize(); 2593d3351f3SMatthias Ringwald if (err != paNoError){ 26024cc6cccSMatthias Ringwald log_error("Portudio: Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 2613d3351f3SMatthias Ringwald return err; 2623d3351f3SMatthias Ringwald } 2633d3351f3SMatthias Ringwald portaudio_initialized = 1; 264a89df2dfSMatthias Ringwald } 265a89df2dfSMatthias Ringwald 266a89df2dfSMatthias Ringwald /* -- setup input -- */ 2673d3351f3SMatthias Ringwald PaStreamParameters theInputParameters; 268a89df2dfSMatthias Ringwald theInputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ 269a89df2dfSMatthias Ringwald theInputParameters.channelCount = channels; 270a89df2dfSMatthias Ringwald theInputParameters.sampleFormat = PA_SAMPLE_TYPE; 271a89df2dfSMatthias Ringwald theInputParameters.suggestedLatency = Pa_GetDeviceInfo( theInputParameters.device )->defaultHighInputLatency; 272a89df2dfSMatthias Ringwald theInputParameters.hostApiSpecificStreamInfo = NULL; 273a89df2dfSMatthias Ringwald 274a89df2dfSMatthias Ringwald const PaDeviceInfo *inputDeviceInfo; 275a89df2dfSMatthias Ringwald inputDeviceInfo = Pa_GetDeviceInfo( theInputParameters.device ); 27624cc6cccSMatthias Ringwald log_info("PortAudio: source device: %s", inputDeviceInfo->name); 277a89df2dfSMatthias Ringwald 278a89df2dfSMatthias Ringwald /* -- setup stream -- */ 279a89df2dfSMatthias Ringwald err = Pa_OpenStream( 2803d3351f3SMatthias Ringwald &stream_sink, 2813d3351f3SMatthias Ringwald &theInputParameters, 2823d3351f3SMatthias Ringwald NULL, 283a89df2dfSMatthias Ringwald samplerate, 284a89df2dfSMatthias Ringwald NUM_FRAMES_PER_PA_BUFFER, 285a89df2dfSMatthias Ringwald paClipOff, /* we won't output out of range samples so don't bother clipping them */ 2863d3351f3SMatthias Ringwald portaudio_callback_source, /* use callback */ 287a89df2dfSMatthias Ringwald NULL ); 288a89df2dfSMatthias Ringwald 289a89df2dfSMatthias Ringwald if (err != paNoError){ 290a89df2dfSMatthias Ringwald log_error("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 291a89df2dfSMatthias Ringwald return err; 292a89df2dfSMatthias Ringwald } 29324cc6cccSMatthias Ringwald log_info("PortAudio: source stream created"); 294a89df2dfSMatthias Ringwald 2953d3351f3SMatthias Ringwald const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream_sink); 29624cc6cccSMatthias Ringwald log_info("PortAudio: source latency: %f", stream_info->inputLatency); 297a89df2dfSMatthias Ringwald 298a89df2dfSMatthias Ringwald recording_callback = recording; 299a89df2dfSMatthias Ringwald 3003d3351f3SMatthias Ringwald source_initialized = 1; 3013d3351f3SMatthias Ringwald 302a89df2dfSMatthias Ringwald return 0; 303a89df2dfSMatthias Ringwald } 304a89df2dfSMatthias Ringwald 3053d3351f3SMatthias Ringwald static void btstack_audio_portaudio_sink_start_stream(void){ 3063d3351f3SMatthias Ringwald 3073d3351f3SMatthias Ringwald if (!playback_callback) return; 308a89df2dfSMatthias Ringwald 309a89df2dfSMatthias Ringwald // fill buffer once 310a89df2dfSMatthias Ringwald (*playback_callback)(output_buffer_a, NUM_FRAMES_PER_PA_BUFFER); 311a89df2dfSMatthias Ringwald (*playback_callback)(output_buffer_b, NUM_FRAMES_PER_PA_BUFFER); 312a89df2dfSMatthias Ringwald output_buffer_to_play = 0; 313a89df2dfSMatthias Ringwald output_buffer_to_fill = 2; 314a89df2dfSMatthias Ringwald 315a89df2dfSMatthias Ringwald /* -- start stream -- */ 3163d3351f3SMatthias Ringwald PaError err = Pa_StartStream(stream_source); 317a89df2dfSMatthias Ringwald if (err != paNoError){ 31824cc6cccSMatthias Ringwald log_error("PortAudio: error starting sink stream: \"%s\"\n", Pa_GetErrorText(err)); 319a89df2dfSMatthias Ringwald return; 320a89df2dfSMatthias Ringwald } 321a89df2dfSMatthias Ringwald 322a89df2dfSMatthias Ringwald // start timer 3233d3351f3SMatthias Ringwald btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink); 3243d3351f3SMatthias Ringwald btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS); 3253d3351f3SMatthias Ringwald btstack_run_loop_add_timer(&driver_timer_sink); 3263d3351f3SMatthias Ringwald 3273d3351f3SMatthias Ringwald sink_active = 1; 328a89df2dfSMatthias Ringwald } 329a89df2dfSMatthias Ringwald 3303d3351f3SMatthias Ringwald static void btstack_audio_portaudio_source_start_stream(void){ 3313d3351f3SMatthias Ringwald 3323d3351f3SMatthias Ringwald if (!recording_callback) return; 3333d3351f3SMatthias Ringwald 3343d3351f3SMatthias Ringwald /* -- start stream -- */ 3353d3351f3SMatthias Ringwald PaError err = Pa_StartStream(stream_sink); 3363d3351f3SMatthias Ringwald if (err != paNoError){ 33724cc6cccSMatthias Ringwald log_error("PortAudio: error starting source stream: \"%s\"\n", Pa_GetErrorText(err)); 3383d3351f3SMatthias Ringwald return; 3393d3351f3SMatthias Ringwald } 3403d3351f3SMatthias Ringwald 3413d3351f3SMatthias Ringwald // start timer 3423d3351f3SMatthias Ringwald btstack_run_loop_set_timer_handler(&driver_timer_source, &driver_timer_handler_source); 3433d3351f3SMatthias Ringwald btstack_run_loop_set_timer(&driver_timer_source, DRIVER_POLL_INTERVAL_MS); 3443d3351f3SMatthias Ringwald btstack_run_loop_add_timer(&driver_timer_source); 3453d3351f3SMatthias Ringwald 3463d3351f3SMatthias Ringwald source_active = 1; 3473d3351f3SMatthias Ringwald } 3483d3351f3SMatthias Ringwald 3493d3351f3SMatthias Ringwald static void btstack_audio_portaudio_sink_stop_stream(void){ 3503d3351f3SMatthias Ringwald 3513d3351f3SMatthias Ringwald if (!playback_callback) return; 3523d3351f3SMatthias Ringwald if (!sink_active) return; 353a89df2dfSMatthias Ringwald 354a89df2dfSMatthias Ringwald // stop timer 3553d3351f3SMatthias Ringwald btstack_run_loop_remove_timer(&driver_timer_sink); 356a89df2dfSMatthias Ringwald 3573d3351f3SMatthias Ringwald PaError err = Pa_StopStream(stream_sink); 358a89df2dfSMatthias Ringwald if (err != paNoError){ 35924cc6cccSMatthias Ringwald log_error("PortAudio: error stopping sink stream: \"%s\"", Pa_GetErrorText(err)); 360a89df2dfSMatthias Ringwald return; 361a89df2dfSMatthias Ringwald } 36224cc6cccSMatthias Ringwald 36324cc6cccSMatthias Ringwald sink_active = 0; 3643d3351f3SMatthias Ringwald } 3653d3351f3SMatthias Ringwald 3663d3351f3SMatthias Ringwald static void btstack_audio_portaudio_source_stop_stream(void){ 3673d3351f3SMatthias Ringwald 3683d3351f3SMatthias Ringwald if (!recording_callback) return; 3693d3351f3SMatthias Ringwald if (!source_active) return; 3703d3351f3SMatthias Ringwald 3713d3351f3SMatthias Ringwald // stop timer 3723d3351f3SMatthias Ringwald btstack_run_loop_remove_timer(&driver_timer_source); 3733d3351f3SMatthias Ringwald 3743d3351f3SMatthias Ringwald PaError err = Pa_StopStream(stream_source); 375a89df2dfSMatthias Ringwald if (err != paNoError){ 37624cc6cccSMatthias Ringwald log_error("PortAudio: error stopping source stream: \"%s\"", Pa_GetErrorText(err)); 377a89df2dfSMatthias Ringwald return; 378a89df2dfSMatthias Ringwald } 37924cc6cccSMatthias Ringwald 38024cc6cccSMatthias Ringwald source_active = 0; 3813d3351f3SMatthias Ringwald } 3823d3351f3SMatthias Ringwald 3833d3351f3SMatthias Ringwald static void btstack_audio_portaudio_close_pa_if_not_needed(void){ 3843d3351f3SMatthias Ringwald if (source_initialized) return; 3853d3351f3SMatthias Ringwald if (sink_initialized) return; 3863d3351f3SMatthias Ringwald PaError err = Pa_Terminate(); 387a89df2dfSMatthias Ringwald if (err != paNoError){ 38824cc6cccSMatthias Ringwald log_error("Portudio: Error terminating portaudio: \"%s\"", Pa_GetErrorText(err)); 389a89df2dfSMatthias Ringwald return; 390a89df2dfSMatthias Ringwald } 3911faf5e55SMatthias Ringwald portaudio_initialized = 0; 392a89df2dfSMatthias Ringwald } 393a89df2dfSMatthias Ringwald 3943d3351f3SMatthias Ringwald static void btstack_audio_portaudio_sink_close(void){ 3953d3351f3SMatthias Ringwald 3963d3351f3SMatthias Ringwald if (!playback_callback) return; 3973d3351f3SMatthias Ringwald 3983d3351f3SMatthias Ringwald if (sink_active){ 3993d3351f3SMatthias Ringwald btstack_audio_portaudio_sink_stop_stream(); 4003d3351f3SMatthias Ringwald } 4013d3351f3SMatthias Ringwald 4021247fb8fSMatthias Ringwald PaError err = Pa_CloseStream(stream_sink); 4033d3351f3SMatthias Ringwald if (err != paNoError){ 40424cc6cccSMatthias Ringwald log_error("PortAudio: error closing sink stream: \"%s\"", Pa_GetErrorText(err)); 4053d3351f3SMatthias Ringwald return; 4063d3351f3SMatthias Ringwald } 4073d3351f3SMatthias Ringwald 4083d3351f3SMatthias Ringwald sink_initialized = 0; 4093d3351f3SMatthias Ringwald btstack_audio_portaudio_close_pa_if_not_needed(); 4103d3351f3SMatthias Ringwald } 4113d3351f3SMatthias Ringwald 4123d3351f3SMatthias Ringwald 4133d3351f3SMatthias Ringwald static void btstack_audio_portaudio_source_close(void){ 4143d3351f3SMatthias Ringwald 4153d3351f3SMatthias Ringwald if (!recording_callback) return; 4163d3351f3SMatthias Ringwald 4173d3351f3SMatthias Ringwald if (source_active){ 4183d3351f3SMatthias Ringwald btstack_audio_portaudio_sink_stop_stream(); 4193d3351f3SMatthias Ringwald } 4203d3351f3SMatthias Ringwald 4211247fb8fSMatthias Ringwald PaError err = Pa_CloseStream(stream_source); 4223d3351f3SMatthias Ringwald if (err != paNoError){ 42324cc6cccSMatthias Ringwald log_error("PortAudio: error closing source stream: \"%s\"", Pa_GetErrorText(err)); 4243d3351f3SMatthias Ringwald return; 4253d3351f3SMatthias Ringwald } 4263d3351f3SMatthias Ringwald 4273d3351f3SMatthias Ringwald source_initialized = 0; 4283d3351f3SMatthias Ringwald btstack_audio_portaudio_close_pa_if_not_needed(); 4293d3351f3SMatthias Ringwald } 4303d3351f3SMatthias Ringwald 4313d3351f3SMatthias Ringwald static const btstack_audio_sink_t btstack_audio_portaudio_sink = { 4323d3351f3SMatthias Ringwald /* int (*init)(..);*/ &btstack_audio_portaudio_sink_init, 4333d3351f3SMatthias Ringwald /* void (*start_stream(void));*/ &btstack_audio_portaudio_sink_start_stream, 4341247fb8fSMatthias Ringwald /* void (*stop_stream)(void) */ &btstack_audio_portaudio_sink_stop_stream, 4353d3351f3SMatthias Ringwald /* void (*close)(void); */ &btstack_audio_portaudio_sink_close 436a89df2dfSMatthias Ringwald }; 437a89df2dfSMatthias Ringwald 4383d3351f3SMatthias Ringwald static const btstack_audio_source_t btstack_audio_portaudio_source = { 4393d3351f3SMatthias Ringwald /* int (*init)(..);*/ &btstack_audio_portaudio_source_init, 4403d3351f3SMatthias Ringwald /* void (*start_stream(void));*/ &btstack_audio_portaudio_source_start_stream, 4411247fb8fSMatthias Ringwald /* void (*stop_stream)(void) */ &btstack_audio_portaudio_source_stop_stream, 4423d3351f3SMatthias Ringwald /* void (*close)(void); */ &btstack_audio_portaudio_source_close 4433d3351f3SMatthias Ringwald }; 4443d3351f3SMatthias Ringwald 4453d3351f3SMatthias Ringwald const btstack_audio_sink_t * btstack_audio_portaudio_sink_get_instance(void){ 4463d3351f3SMatthias Ringwald return &btstack_audio_portaudio_sink; 4473d3351f3SMatthias Ringwald } 4483d3351f3SMatthias Ringwald 4493d3351f3SMatthias Ringwald const btstack_audio_source_t * btstack_audio_portaudio_source_get_instance(void){ 4503d3351f3SMatthias Ringwald return &btstack_audio_portaudio_source; 451a89df2dfSMatthias Ringwald } 452a89df2dfSMatthias Ringwald 453a89df2dfSMatthias Ringwald #endif 454