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