xref: /btstack/platform/posix/btstack_audio_portaudio.c (revision 14200bb85ab1c4182dd670d334ece79a4891d165)
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
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH 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 
38e501bae0SMatthias 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
51595b1738SMatthias Ringwald #define NUM_OUTPUT_BUFFERS               5
52*14200bb8SDirk Helbig #define NUM_INPUT_BUFFERS                5
53a89df2dfSMatthias Ringwald #define DRIVER_POLL_INTERVAL_MS          5
54a89df2dfSMatthias Ringwald 
5530b419f4SMatthias Ringwald #ifndef MAX_NR_AUDIO_CHANNELS
5630b419f4SMatthias Ringwald #define MAX_NR_AUDIO_CHANNELS 2
5730b419f4SMatthias Ringwald #endif
5830b419f4SMatthias Ringwald 
59a89df2dfSMatthias Ringwald #include <portaudio.h>
60a89df2dfSMatthias Ringwald 
61a89df2dfSMatthias Ringwald // config
621a551b1bSMatthias Ringwald static int                    num_channels_sink;
631a551b1bSMatthias Ringwald static int                    num_channels_source;
64bcd51729SMatthias Ringwald static int                    num_bytes_per_sample_sink;
65bcd51729SMatthias Ringwald static int                    num_bytes_per_sample_source;
66a89df2dfSMatthias Ringwald 
67a89df2dfSMatthias Ringwald // portaudio
683d3351f3SMatthias Ringwald static int portaudio_initialized;
693d3351f3SMatthias Ringwald 
703d3351f3SMatthias Ringwald // state
713d3351f3SMatthias Ringwald static int source_initialized;
723d3351f3SMatthias Ringwald static int sink_initialized;
733d3351f3SMatthias Ringwald static int source_active;
743d3351f3SMatthias Ringwald static int sink_active;
753d3351f3SMatthias Ringwald 
761a551b1bSMatthias Ringwald static uint8_t sink_volume;
771a551b1bSMatthias Ringwald 
783d3351f3SMatthias Ringwald static PaStream * stream_source;
793d3351f3SMatthias Ringwald static PaStream * stream_sink;
80a89df2dfSMatthias Ringwald 
81a89df2dfSMatthias Ringwald // client
82a89df2dfSMatthias Ringwald static void (*playback_callback)(int16_t * buffer, uint16_t num_samples);
83a89df2dfSMatthias Ringwald static void (*recording_callback)(const int16_t * buffer, uint16_t num_samples);
84a89df2dfSMatthias Ringwald 
85a89df2dfSMatthias Ringwald // output buffer
8630b419f4SMatthias Ringwald static int16_t               output_buffer_storage[NUM_OUTPUT_BUFFERS * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS];
87ee37d7ffSMatthias Ringwald static int16_t             * output_buffers[NUM_OUTPUT_BUFFERS];
88a89df2dfSMatthias Ringwald static int                   output_buffer_to_play;
89a89df2dfSMatthias Ringwald static int                   output_buffer_to_fill;
90a89df2dfSMatthias Ringwald 
91a89df2dfSMatthias Ringwald // input buffer
92*14200bb8SDirk Helbig static int16_t               input_buffer_storage[NUM_INPUT_BUFFERS * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS];
93*14200bb8SDirk Helbig static int16_t             * input_buffers[NUM_INPUT_BUFFERS];
94a89df2dfSMatthias Ringwald static int                   input_buffer_to_record;
95a89df2dfSMatthias Ringwald static int                   input_buffer_to_fill;
96a89df2dfSMatthias Ringwald 
97a89df2dfSMatthias Ringwald 
98a89df2dfSMatthias Ringwald // timer to fill output ring buffer
993d3351f3SMatthias Ringwald static btstack_timer_source_t  driver_timer_sink;
1003d3351f3SMatthias Ringwald static btstack_timer_source_t  driver_timer_source;
101a89df2dfSMatthias Ringwald 
1023d3351f3SMatthias Ringwald static int portaudio_callback_sink( const void *                     inputBuffer,
103a89df2dfSMatthias Ringwald                                     void *                           outputBuffer,
104ee37d7ffSMatthias Ringwald                                     unsigned long                    frames_per_buffer,
105a89df2dfSMatthias Ringwald                                     const PaStreamCallbackTimeInfo * timeInfo,
106a89df2dfSMatthias Ringwald                                     PaStreamCallbackFlags            statusFlags,
107a89df2dfSMatthias Ringwald                                     void *                           userData ) {
108a89df2dfSMatthias Ringwald 
109a89df2dfSMatthias Ringwald     /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */
110a89df2dfSMatthias Ringwald 
111a89df2dfSMatthias Ringwald     (void) timeInfo; /* Prevent unused variable warnings. */
112a89df2dfSMatthias Ringwald     (void) statusFlags;
113a89df2dfSMatthias Ringwald     (void) userData;
114ee37d7ffSMatthias Ringwald     (void) frames_per_buffer;
1153d3351f3SMatthias Ringwald     (void) inputBuffer;
116a89df2dfSMatthias Ringwald 
1171a551b1bSMatthias Ringwald     // simplified volume control
1181a551b1bSMatthias Ringwald     uint16_t index;
1191a551b1bSMatthias Ringwald     int16_t * from_buffer = output_buffers[output_buffer_to_play];
1201a551b1bSMatthias Ringwald     int16_t * to_buffer = (int16_t *) outputBuffer;
121ee37d7ffSMatthias Ringwald     btstack_assert(frames_per_buffer == NUM_FRAMES_PER_PA_BUFFER);
1221a551b1bSMatthias Ringwald 
1231a551b1bSMatthias Ringwald #if 0
1241a551b1bSMatthias Ringwald     // up to 8 right shifts
1251a551b1bSMatthias Ringwald     int right_shift = 8 - btstack_min(8, ((sink_volume + 15) / 16));
1261a551b1bSMatthias Ringwald     for (index = 0; index < (NUM_FRAMES_PER_PA_BUFFER * num_channels_sink); index++){
1271a551b1bSMatthias Ringwald         *to_buffer++ = (*from_buffer++) >> right_shift;
1281a551b1bSMatthias Ringwald     }
1291a551b1bSMatthias Ringwald #else
1301a551b1bSMatthias Ringwald     // multiply with volume ^ 4
1311a551b1bSMatthias Ringwald     int16_t x2 = sink_volume * sink_volume;
1321a551b1bSMatthias Ringwald     int16_t x4 = (x2 * x2) >> 14;
1331a551b1bSMatthias Ringwald     for (index = 0; index < (NUM_FRAMES_PER_PA_BUFFER * num_channels_sink); index++){
1341a551b1bSMatthias Ringwald         *to_buffer++ = ((*from_buffer++) * x4) >> 14;
1351a551b1bSMatthias Ringwald     }
1361a551b1bSMatthias Ringwald #endif
137a89df2dfSMatthias Ringwald 
138a89df2dfSMatthias Ringwald     // next
139a89df2dfSMatthias Ringwald     output_buffer_to_play = (output_buffer_to_play + 1 ) % NUM_OUTPUT_BUFFERS;
1403d3351f3SMatthias Ringwald 
1413d3351f3SMatthias Ringwald     return 0;
142a89df2dfSMatthias Ringwald }
143a89df2dfSMatthias Ringwald 
1443d3351f3SMatthias Ringwald static int portaudio_callback_source( const void *                     inputBuffer,
1453d3351f3SMatthias Ringwald                                       void *                           outputBuffer,
1463d3351f3SMatthias Ringwald                                       unsigned long                    samples_per_buffer,
1473d3351f3SMatthias Ringwald                                       const PaStreamCallbackTimeInfo * timeInfo,
1483d3351f3SMatthias Ringwald                                       PaStreamCallbackFlags            statusFlags,
1493d3351f3SMatthias Ringwald                                       void *                           userData ) {
1503d3351f3SMatthias Ringwald 
1513d3351f3SMatthias Ringwald     /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */
1523d3351f3SMatthias Ringwald 
1533d3351f3SMatthias Ringwald     (void) timeInfo; /* Prevent unused variable warnings. */
1543d3351f3SMatthias Ringwald     (void) statusFlags;
1553d3351f3SMatthias Ringwald     (void) userData;
1563d3351f3SMatthias Ringwald     (void) samples_per_buffer;
1573d3351f3SMatthias Ringwald     (void) outputBuffer;
158a89df2dfSMatthias Ringwald 
159a89df2dfSMatthias Ringwald     // store in one of our buffers
160bcd51729SMatthias Ringwald     memcpy(input_buffers[input_buffer_to_fill], inputBuffer, NUM_FRAMES_PER_PA_BUFFER * num_bytes_per_sample_source);
161a89df2dfSMatthias Ringwald 
162a89df2dfSMatthias Ringwald     // next
163a89df2dfSMatthias Ringwald     input_buffer_to_fill = (input_buffer_to_fill + 1 ) % NUM_INPUT_BUFFERS;
164a89df2dfSMatthias Ringwald 
165a89df2dfSMatthias Ringwald     return 0;
166a89df2dfSMatthias Ringwald }
167a89df2dfSMatthias Ringwald 
1683d3351f3SMatthias Ringwald static void driver_timer_handler_sink(btstack_timer_source_t * ts){
169a89df2dfSMatthias Ringwald 
170a89df2dfSMatthias Ringwald     // playback buffer ready to fill
171a0473563SMatthias Ringwald     while (output_buffer_to_play != output_buffer_to_fill){
172a89df2dfSMatthias Ringwald         (*playback_callback)(output_buffers[output_buffer_to_fill], NUM_FRAMES_PER_PA_BUFFER);
173a89df2dfSMatthias Ringwald 
174a89df2dfSMatthias Ringwald         // next
175a89df2dfSMatthias Ringwald         output_buffer_to_fill = (output_buffer_to_fill + 1 ) % NUM_OUTPUT_BUFFERS;
176a89df2dfSMatthias Ringwald     }
177a89df2dfSMatthias Ringwald 
1783d3351f3SMatthias Ringwald     // re-set timer
1793d3351f3SMatthias Ringwald     btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
1803d3351f3SMatthias Ringwald     btstack_run_loop_add_timer(ts);
1813d3351f3SMatthias Ringwald }
1823d3351f3SMatthias Ringwald 
1833d3351f3SMatthias Ringwald static void driver_timer_handler_source(btstack_timer_source_t * ts){
1843d3351f3SMatthias Ringwald 
185a89df2dfSMatthias Ringwald     // recording buffer ready to process
1863d3351f3SMatthias Ringwald     if (input_buffer_to_record != input_buffer_to_fill){
187a89df2dfSMatthias Ringwald 
188a89df2dfSMatthias Ringwald         (*recording_callback)(input_buffers[input_buffer_to_record], NUM_FRAMES_PER_PA_BUFFER);
189a89df2dfSMatthias Ringwald 
190a89df2dfSMatthias Ringwald         // next
191a89df2dfSMatthias Ringwald         input_buffer_to_record = (input_buffer_to_record + 1 ) % NUM_INPUT_BUFFERS;
192a89df2dfSMatthias Ringwald     }
193a89df2dfSMatthias Ringwald 
194a89df2dfSMatthias Ringwald     // re-set timer
195a89df2dfSMatthias Ringwald     btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
196a89df2dfSMatthias Ringwald     btstack_run_loop_add_timer(ts);
197a89df2dfSMatthias Ringwald }
198a89df2dfSMatthias Ringwald 
1993d3351f3SMatthias Ringwald static int btstack_audio_portaudio_sink_init(
200a89df2dfSMatthias Ringwald     uint8_t channels,
201a89df2dfSMatthias Ringwald     uint32_t samplerate,
2023d3351f3SMatthias Ringwald     void (*playback)(int16_t * buffer, uint16_t num_samples)
203a89df2dfSMatthias Ringwald ){
2043d3351f3SMatthias Ringwald     PaError err;
205a89df2dfSMatthias Ringwald 
20630b419f4SMatthias Ringwald     btstack_assert(channels <= MAX_NR_AUDIO_CHANNELS);
20730b419f4SMatthias Ringwald 
2081a551b1bSMatthias Ringwald     num_channels_sink = channels;
209bcd51729SMatthias Ringwald     num_bytes_per_sample_sink = 2 * channels;
210a89df2dfSMatthias Ringwald 
2113d3351f3SMatthias Ringwald     if (!playback){
2123d3351f3SMatthias Ringwald         log_error("No playback callback");
2133d3351f3SMatthias Ringwald         return 1;
2143d3351f3SMatthias Ringwald     }
2153d3351f3SMatthias Ringwald 
216*14200bb8SDirk Helbig     for (int i=0;i<NUM_OUTPUT_BUFFERS;i++){
217*14200bb8SDirk Helbig         output_buffers[i] = &output_buffer_storage[i * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS];
218*14200bb8SDirk Helbig     }
219*14200bb8SDirk Helbig 
220a89df2dfSMatthias Ringwald     /* -- initialize PortAudio -- */
2213d3351f3SMatthias Ringwald     if (!portaudio_initialized){
2223d3351f3SMatthias Ringwald         err = Pa_Initialize();
223a89df2dfSMatthias Ringwald         if (err != paNoError){
22424cc6cccSMatthias Ringwald             log_error("Portudio: error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
225a89df2dfSMatthias Ringwald             return err;
226a89df2dfSMatthias Ringwald         }
2273d3351f3SMatthias Ringwald         portaudio_initialized = 1;
2283d3351f3SMatthias Ringwald     }
229a89df2dfSMatthias Ringwald 
230a89df2dfSMatthias Ringwald     /* -- setup output -- */
2313d3351f3SMatthias Ringwald     PaStreamParameters theOutputParameters;
232a89df2dfSMatthias Ringwald     theOutputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
233a89df2dfSMatthias Ringwald     theOutputParameters.channelCount = channels;
234a89df2dfSMatthias Ringwald     theOutputParameters.sampleFormat = PA_SAMPLE_TYPE;
235a89df2dfSMatthias Ringwald     theOutputParameters.suggestedLatency = Pa_GetDeviceInfo( theOutputParameters.device )->defaultHighOutputLatency;
236a89df2dfSMatthias Ringwald     theOutputParameters.hostApiSpecificStreamInfo = NULL;
237a89df2dfSMatthias Ringwald 
238a89df2dfSMatthias Ringwald     const PaDeviceInfo *outputDeviceInfo;
239a89df2dfSMatthias Ringwald     outputDeviceInfo = Pa_GetDeviceInfo( theOutputParameters.device );
24024cc6cccSMatthias Ringwald     log_info("PortAudio: sink device: %s", outputDeviceInfo->name);
241bb90a366SMatthias Ringwald     UNUSED(outputDeviceInfo);
242a89df2dfSMatthias Ringwald 
2433d3351f3SMatthias Ringwald     /* -- setup stream -- */
2443d3351f3SMatthias Ringwald     err = Pa_OpenStream(
2453abb6826SMatthias Ringwald            &stream_sink,
2463d3351f3SMatthias Ringwald            NULL,
2473d3351f3SMatthias Ringwald            &theOutputParameters,
2483d3351f3SMatthias Ringwald            samplerate,
2493d3351f3SMatthias Ringwald            NUM_FRAMES_PER_PA_BUFFER,
2503d3351f3SMatthias Ringwald            paClipOff,           /* we won't output out of range samples so don't bother clipping them */
2513d3351f3SMatthias Ringwald            portaudio_callback_sink,  /* use callback */
2523d3351f3SMatthias Ringwald            NULL );
2533d3351f3SMatthias Ringwald 
2543d3351f3SMatthias Ringwald     if (err != paNoError){
25524cc6cccSMatthias Ringwald         log_error("Portudio: error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
2563d3351f3SMatthias Ringwald         return err;
2573d3351f3SMatthias Ringwald     }
25824cc6cccSMatthias Ringwald     log_info("PortAudio: sink stream created");
2593d3351f3SMatthias Ringwald 
2603abb6826SMatthias Ringwald     const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream_sink);
26124cc6cccSMatthias Ringwald     log_info("PortAudio: sink latency: %f", stream_info->outputLatency);
262894c930cSMatthias Ringwald     UNUSED(stream_info);
2633d3351f3SMatthias Ringwald 
2643d3351f3SMatthias Ringwald     playback_callback  = playback;
2653d3351f3SMatthias Ringwald 
2663d3351f3SMatthias Ringwald     sink_initialized = 1;
2671a551b1bSMatthias Ringwald     sink_volume = 127;
2683d3351f3SMatthias Ringwald 
2693d3351f3SMatthias Ringwald     return 0;
2703d3351f3SMatthias Ringwald }
2713d3351f3SMatthias Ringwald 
2723d3351f3SMatthias Ringwald static int btstack_audio_portaudio_source_init(
2733d3351f3SMatthias Ringwald     uint8_t channels,
2743d3351f3SMatthias Ringwald     uint32_t samplerate,
2753d3351f3SMatthias Ringwald     void (*recording)(const int16_t * buffer, uint16_t num_samples)
2763d3351f3SMatthias Ringwald ){
2773d3351f3SMatthias Ringwald     PaError err;
2783d3351f3SMatthias Ringwald 
27930b419f4SMatthias Ringwald     btstack_assert(channels <= MAX_NR_AUDIO_CHANNELS);
28030b419f4SMatthias Ringwald 
2811a551b1bSMatthias Ringwald     num_channels_source = channels;
282bcd51729SMatthias Ringwald     num_bytes_per_sample_source = 2 * channels;
2833d3351f3SMatthias Ringwald 
2843d3351f3SMatthias Ringwald     if (!recording){
2853d3351f3SMatthias Ringwald         log_error("No recording callback");
2863d3351f3SMatthias Ringwald         return 1;
2873d3351f3SMatthias Ringwald     }
2883d3351f3SMatthias Ringwald 
289*14200bb8SDirk Helbig     for (int i=0;i<NUM_INPUT_BUFFERS;i++){
290*14200bb8SDirk Helbig         input_buffers[i] = &input_buffer_storage[i * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS];
291*14200bb8SDirk Helbig     }
292*14200bb8SDirk Helbig 
2933d3351f3SMatthias Ringwald     /* -- initialize PortAudio -- */
2943d3351f3SMatthias Ringwald     if (!portaudio_initialized){
2953d3351f3SMatthias Ringwald         err = Pa_Initialize();
2963d3351f3SMatthias Ringwald         if (err != paNoError){
29724cc6cccSMatthias Ringwald             log_error("Portudio: Error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
2983d3351f3SMatthias Ringwald             return err;
2993d3351f3SMatthias Ringwald         }
3003d3351f3SMatthias Ringwald         portaudio_initialized = 1;
301a89df2dfSMatthias Ringwald     }
302a89df2dfSMatthias Ringwald 
303a89df2dfSMatthias Ringwald     /* -- setup input -- */
3043d3351f3SMatthias Ringwald     PaStreamParameters theInputParameters;
305a89df2dfSMatthias Ringwald     theInputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
306a89df2dfSMatthias Ringwald     theInputParameters.channelCount = channels;
307a89df2dfSMatthias Ringwald     theInputParameters.sampleFormat = PA_SAMPLE_TYPE;
308a89df2dfSMatthias Ringwald     theInputParameters.suggestedLatency = Pa_GetDeviceInfo( theInputParameters.device )->defaultHighInputLatency;
309a89df2dfSMatthias Ringwald     theInputParameters.hostApiSpecificStreamInfo = NULL;
310a89df2dfSMatthias Ringwald 
311a89df2dfSMatthias Ringwald     const PaDeviceInfo *inputDeviceInfo;
312a89df2dfSMatthias Ringwald     inputDeviceInfo = Pa_GetDeviceInfo( theInputParameters.device );
31324cc6cccSMatthias Ringwald     log_info("PortAudio: source device: %s", inputDeviceInfo->name);
314bb90a366SMatthias Ringwald     UNUSED(inputDeviceInfo);
315a89df2dfSMatthias Ringwald 
316a89df2dfSMatthias Ringwald     /* -- setup stream -- */
317a89df2dfSMatthias Ringwald     err = Pa_OpenStream(
3183abb6826SMatthias Ringwald            &stream_source,
3193d3351f3SMatthias Ringwald            &theInputParameters,
3203d3351f3SMatthias Ringwald            NULL,
321a89df2dfSMatthias Ringwald            samplerate,
322a89df2dfSMatthias Ringwald            NUM_FRAMES_PER_PA_BUFFER,
323a89df2dfSMatthias Ringwald            paClipOff,           /* we won't output out of range samples so don't bother clipping them */
3243d3351f3SMatthias Ringwald            portaudio_callback_source,  /* use callback */
325a89df2dfSMatthias Ringwald            NULL );
326a89df2dfSMatthias Ringwald 
327a89df2dfSMatthias Ringwald     if (err != paNoError){
328a89df2dfSMatthias Ringwald         log_error("Error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
329a89df2dfSMatthias Ringwald         return err;
330a89df2dfSMatthias Ringwald     }
33124cc6cccSMatthias Ringwald     log_info("PortAudio: source stream created");
332a89df2dfSMatthias Ringwald 
3333abb6826SMatthias Ringwald     const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream_source);
33424cc6cccSMatthias Ringwald     log_info("PortAudio: source latency: %f", stream_info->inputLatency);
335894c930cSMatthias Ringwald     UNUSED(stream_info);
336a89df2dfSMatthias Ringwald 
337a89df2dfSMatthias Ringwald     recording_callback = recording;
338a89df2dfSMatthias Ringwald 
3393d3351f3SMatthias Ringwald     source_initialized = 1;
3403d3351f3SMatthias Ringwald 
341a89df2dfSMatthias Ringwald     return 0;
342a89df2dfSMatthias Ringwald }
343a89df2dfSMatthias Ringwald 
3446926c297SDirk Helbig static uint32_t btstack_audio_portaudio_sink_get_samplerate(void) {
3456926c297SDirk Helbig     const PaStreamInfo *stream_info = Pa_GetStreamInfo(stream_sink);
3466926c297SDirk Helbig     return stream_info->sampleRate;
3476926c297SDirk Helbig }
3486926c297SDirk Helbig 
3496926c297SDirk Helbig static uint32_t btstack_audio_portaudio_source_get_samplerate(void) {
3506926c297SDirk Helbig     const PaStreamInfo *stream_info = Pa_GetStreamInfo(stream_source);
3516926c297SDirk Helbig     return stream_info->sampleRate;
3526926c297SDirk Helbig }
3536926c297SDirk Helbig 
3541b7f8fa1SMatthias Ringwald static void btstack_audio_portaudio_sink_set_volume(uint8_t volume){
3551a551b1bSMatthias Ringwald     sink_volume = volume;
3561b7f8fa1SMatthias Ringwald }
3571b7f8fa1SMatthias Ringwald 
3581b7f8fa1SMatthias Ringwald static void btstack_audio_portaudio_source_set_gain(uint8_t gain){
3591b7f8fa1SMatthias Ringwald     UNUSED(gain);
3601b7f8fa1SMatthias Ringwald }
3611b7f8fa1SMatthias Ringwald 
3623d3351f3SMatthias Ringwald static void btstack_audio_portaudio_sink_start_stream(void){
3633d3351f3SMatthias Ringwald 
3643d3351f3SMatthias Ringwald     if (!playback_callback) return;
365a89df2dfSMatthias Ringwald 
366ee37d7ffSMatthias Ringwald     // fill buffers once
367ee37d7ffSMatthias Ringwald     uint8_t i;
368ee37d7ffSMatthias Ringwald     for (i=0;i<NUM_OUTPUT_BUFFERS-1;i++){
36930b419f4SMatthias Ringwald         (*playback_callback)(&output_buffer_storage[i * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS], NUM_FRAMES_PER_PA_BUFFER);
370ee37d7ffSMatthias Ringwald     }
371a89df2dfSMatthias Ringwald     output_buffer_to_play = 0;
372ee37d7ffSMatthias Ringwald     output_buffer_to_fill = NUM_OUTPUT_BUFFERS-1;
373a89df2dfSMatthias Ringwald 
374a89df2dfSMatthias Ringwald     /* -- start stream -- */
375df991ce9SMatthias Ringwald     PaError err = Pa_StartStream(stream_sink);
376a89df2dfSMatthias Ringwald     if (err != paNoError){
37724cc6cccSMatthias Ringwald         log_error("PortAudio: error starting sink stream: \"%s\"\n",  Pa_GetErrorText(err));
378a89df2dfSMatthias Ringwald         return;
379a89df2dfSMatthias Ringwald     }
380a89df2dfSMatthias Ringwald 
381a89df2dfSMatthias Ringwald     // start timer
3823d3351f3SMatthias Ringwald     btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink);
3833d3351f3SMatthias Ringwald     btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS);
3843d3351f3SMatthias Ringwald     btstack_run_loop_add_timer(&driver_timer_sink);
3853d3351f3SMatthias Ringwald 
3863d3351f3SMatthias Ringwald     sink_active = 1;
387a89df2dfSMatthias Ringwald }
388a89df2dfSMatthias Ringwald 
3893d3351f3SMatthias Ringwald static void btstack_audio_portaudio_source_start_stream(void){
3903d3351f3SMatthias Ringwald 
3913d3351f3SMatthias Ringwald     if (!recording_callback) return;
3923d3351f3SMatthias Ringwald 
3933d3351f3SMatthias Ringwald     /* -- start stream -- */
394df991ce9SMatthias Ringwald     PaError err = Pa_StartStream(stream_source);
3953d3351f3SMatthias Ringwald     if (err != paNoError){
39624cc6cccSMatthias Ringwald         log_error("PortAudio: error starting source stream: \"%s\"\n",  Pa_GetErrorText(err));
3973d3351f3SMatthias Ringwald         return;
3983d3351f3SMatthias Ringwald     }
3993d3351f3SMatthias Ringwald 
4003d3351f3SMatthias Ringwald     // start timer
4013d3351f3SMatthias Ringwald     btstack_run_loop_set_timer_handler(&driver_timer_source, &driver_timer_handler_source);
4023d3351f3SMatthias Ringwald     btstack_run_loop_set_timer(&driver_timer_source, DRIVER_POLL_INTERVAL_MS);
4033d3351f3SMatthias Ringwald     btstack_run_loop_add_timer(&driver_timer_source);
4043d3351f3SMatthias Ringwald 
4053d3351f3SMatthias Ringwald     source_active = 1;
4063d3351f3SMatthias Ringwald }
4073d3351f3SMatthias Ringwald 
4083d3351f3SMatthias Ringwald static void btstack_audio_portaudio_sink_stop_stream(void){
4093d3351f3SMatthias Ringwald 
4103d3351f3SMatthias Ringwald     if (!playback_callback) return;
4113d3351f3SMatthias Ringwald     if (!sink_active)       return;
412a89df2dfSMatthias Ringwald 
413a89df2dfSMatthias Ringwald     // stop timer
4143d3351f3SMatthias Ringwald     btstack_run_loop_remove_timer(&driver_timer_sink);
415a89df2dfSMatthias Ringwald 
4163d3351f3SMatthias Ringwald     PaError err = Pa_StopStream(stream_sink);
417a89df2dfSMatthias Ringwald     if (err != paNoError){
41824cc6cccSMatthias Ringwald         log_error("PortAudio: error stopping sink stream: \"%s\"",  Pa_GetErrorText(err));
419a89df2dfSMatthias Ringwald         return;
420a89df2dfSMatthias Ringwald     }
42124cc6cccSMatthias Ringwald 
42224cc6cccSMatthias Ringwald     sink_active = 0;
4233d3351f3SMatthias Ringwald }
4243d3351f3SMatthias Ringwald 
4253d3351f3SMatthias Ringwald static void btstack_audio_portaudio_source_stop_stream(void){
4263d3351f3SMatthias Ringwald 
4273d3351f3SMatthias Ringwald     if (!recording_callback) return;
4283d3351f3SMatthias Ringwald     if (!source_active)      return;
4293d3351f3SMatthias Ringwald 
4303d3351f3SMatthias Ringwald     // stop timer
4313d3351f3SMatthias Ringwald     btstack_run_loop_remove_timer(&driver_timer_source);
4323d3351f3SMatthias Ringwald 
4333d3351f3SMatthias Ringwald     PaError err = Pa_StopStream(stream_source);
434a89df2dfSMatthias Ringwald     if (err != paNoError){
43524cc6cccSMatthias Ringwald         log_error("PortAudio: error stopping source stream: \"%s\"",  Pa_GetErrorText(err));
436a89df2dfSMatthias Ringwald         return;
437a89df2dfSMatthias Ringwald     }
43824cc6cccSMatthias Ringwald 
43924cc6cccSMatthias Ringwald     source_active = 0;
4403d3351f3SMatthias Ringwald }
4413d3351f3SMatthias Ringwald 
4423d3351f3SMatthias Ringwald static void btstack_audio_portaudio_close_pa_if_not_needed(void){
4433d3351f3SMatthias Ringwald     if (source_initialized) return;
4443d3351f3SMatthias Ringwald     if (sink_initialized) return;
4453d3351f3SMatthias Ringwald     PaError err = Pa_Terminate();
446a89df2dfSMatthias Ringwald     if (err != paNoError){
44724cc6cccSMatthias Ringwald         log_error("Portudio: Error terminating portaudio: \"%s\"",  Pa_GetErrorText(err));
448a89df2dfSMatthias Ringwald         return;
449a89df2dfSMatthias Ringwald     }
4501faf5e55SMatthias Ringwald     portaudio_initialized = 0;
451a89df2dfSMatthias Ringwald }
452a89df2dfSMatthias Ringwald 
4533d3351f3SMatthias Ringwald static void btstack_audio_portaudio_sink_close(void){
4543d3351f3SMatthias Ringwald 
4553d3351f3SMatthias Ringwald     if (!playback_callback) return;
4563d3351f3SMatthias Ringwald 
4573d3351f3SMatthias Ringwald     if (sink_active){
4583d3351f3SMatthias Ringwald         btstack_audio_portaudio_sink_stop_stream();
4593d3351f3SMatthias Ringwald     }
4603d3351f3SMatthias Ringwald 
4611247fb8fSMatthias Ringwald     PaError err = Pa_CloseStream(stream_sink);
4623d3351f3SMatthias Ringwald     if (err != paNoError){
46324cc6cccSMatthias Ringwald         log_error("PortAudio: error closing sink stream: \"%s\"",  Pa_GetErrorText(err));
4643d3351f3SMatthias Ringwald         return;
4653d3351f3SMatthias Ringwald     }
4663d3351f3SMatthias Ringwald 
4673d3351f3SMatthias Ringwald     sink_initialized = 0;
4683d3351f3SMatthias Ringwald     btstack_audio_portaudio_close_pa_if_not_needed();
4693d3351f3SMatthias Ringwald }
4703d3351f3SMatthias Ringwald 
4713d3351f3SMatthias Ringwald static void btstack_audio_portaudio_source_close(void){
4723d3351f3SMatthias Ringwald 
4733d3351f3SMatthias Ringwald     if (!recording_callback) return;
4743d3351f3SMatthias Ringwald 
4753d3351f3SMatthias Ringwald     if (source_active){
4762d66e797SMatthias Ringwald         btstack_audio_portaudio_source_stop_stream();
4773d3351f3SMatthias Ringwald     }
4783d3351f3SMatthias Ringwald 
4791247fb8fSMatthias Ringwald     PaError err = Pa_CloseStream(stream_source);
4803d3351f3SMatthias Ringwald     if (err != paNoError){
48124cc6cccSMatthias Ringwald         log_error("PortAudio: error closing source stream: \"%s\"",  Pa_GetErrorText(err));
4823d3351f3SMatthias Ringwald         return;
4833d3351f3SMatthias Ringwald     }
4843d3351f3SMatthias Ringwald 
4853d3351f3SMatthias Ringwald     source_initialized = 0;
4863d3351f3SMatthias Ringwald     btstack_audio_portaudio_close_pa_if_not_needed();
4873d3351f3SMatthias Ringwald }
4883d3351f3SMatthias Ringwald 
4893d3351f3SMatthias Ringwald static const btstack_audio_sink_t btstack_audio_portaudio_sink = {
4903d3351f3SMatthias Ringwald     /* int (*init)(..);*/                                       &btstack_audio_portaudio_sink_init,
4916926c297SDirk Helbig     /* uint32_t (*get_samplerate)() */                          &btstack_audio_portaudio_sink_get_samplerate,
4921b7f8fa1SMatthias Ringwald     /* void (*set_volume)(uint8_t volume); */                   &btstack_audio_portaudio_sink_set_volume,
4933d3351f3SMatthias Ringwald     /* void (*start_stream(void));*/                            &btstack_audio_portaudio_sink_start_stream,
4941247fb8fSMatthias Ringwald     /* void (*stop_stream)(void)  */                            &btstack_audio_portaudio_sink_stop_stream,
4953d3351f3SMatthias Ringwald     /* void (*close)(void); */                                  &btstack_audio_portaudio_sink_close
496a89df2dfSMatthias Ringwald };
497a89df2dfSMatthias Ringwald 
4983d3351f3SMatthias Ringwald static const btstack_audio_source_t btstack_audio_portaudio_source = {
4993d3351f3SMatthias Ringwald     /* int (*init)(..);*/                                       &btstack_audio_portaudio_source_init,
5006926c297SDirk Helbig     /* uint32_t (*get_samplerate)() */                          &btstack_audio_portaudio_source_get_samplerate,
5011b7f8fa1SMatthias Ringwald     /* void (*set_gain)(uint8_t gain); */                       &btstack_audio_portaudio_source_set_gain,
5023d3351f3SMatthias Ringwald     /* void (*start_stream(void));*/                            &btstack_audio_portaudio_source_start_stream,
5031247fb8fSMatthias Ringwald     /* void (*stop_stream)(void)  */                            &btstack_audio_portaudio_source_stop_stream,
5043d3351f3SMatthias Ringwald     /* void (*close)(void); */                                  &btstack_audio_portaudio_source_close
5053d3351f3SMatthias Ringwald };
5063d3351f3SMatthias Ringwald 
5073d3351f3SMatthias Ringwald const btstack_audio_sink_t * btstack_audio_portaudio_sink_get_instance(void){
5083d3351f3SMatthias Ringwald     return &btstack_audio_portaudio_sink;
5093d3351f3SMatthias Ringwald }
5103d3351f3SMatthias Ringwald 
5113d3351f3SMatthias Ringwald const btstack_audio_source_t * btstack_audio_portaudio_source_get_instance(void){
5123d3351f3SMatthias Ringwald     return &btstack_audio_portaudio_source;
513a89df2dfSMatthias Ringwald }
514a89df2dfSMatthias Ringwald 
515a89df2dfSMatthias Ringwald #endif
516