xref: /btstack/platform/posix/btstack_audio_portaudio.c (revision 3d3351f352187ace4a1668eb322331de2991769a)
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