1*a89df2dfSMatthias Ringwald /* 2*a89df2dfSMatthias Ringwald * Copyright (C) 2017 BlueKitchen GmbH 3*a89df2dfSMatthias Ringwald * 4*a89df2dfSMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5*a89df2dfSMatthias Ringwald * modification, are permitted provided that the following conditions 6*a89df2dfSMatthias Ringwald * are met: 7*a89df2dfSMatthias Ringwald * 8*a89df2dfSMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9*a89df2dfSMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10*a89df2dfSMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11*a89df2dfSMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12*a89df2dfSMatthias Ringwald * documentation and/or other materials provided with the distribution. 13*a89df2dfSMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14*a89df2dfSMatthias Ringwald * contributors may be used to endorse or promote products derived 15*a89df2dfSMatthias Ringwald * from this software without specific prior written permission. 16*a89df2dfSMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17*a89df2dfSMatthias Ringwald * personal benefit and not for any commercial purpose or for 18*a89df2dfSMatthias Ringwald * monetary gain. 19*a89df2dfSMatthias Ringwald * 20*a89df2dfSMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21*a89df2dfSMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*a89df2dfSMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23*a89df2dfSMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24*a89df2dfSMatthias Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25*a89df2dfSMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26*a89df2dfSMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27*a89df2dfSMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28*a89df2dfSMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29*a89df2dfSMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30*a89df2dfSMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31*a89df2dfSMatthias Ringwald * SUCH DAMAGE. 32*a89df2dfSMatthias Ringwald * 33*a89df2dfSMatthias Ringwald * Please inquire about commercial licensing options at 34*a89df2dfSMatthias Ringwald * [email protected] 35*a89df2dfSMatthias Ringwald * 36*a89df2dfSMatthias Ringwald */ 37*a89df2dfSMatthias Ringwald 38*a89df2dfSMatthias Ringwald #define __BTSTACK_FILE__ "btstack_audio_portaudio.c" 39*a89df2dfSMatthias Ringwald 40*a89df2dfSMatthias Ringwald 41*a89df2dfSMatthias Ringwald #include <stdint.h> 42*a89df2dfSMatthias Ringwald #include <string.h> 43*a89df2dfSMatthias Ringwald #include "btstack_debug.h" 44*a89df2dfSMatthias Ringwald #include "btstack_audio.h" 45*a89df2dfSMatthias Ringwald #include "btstack_run_loop.h" 46*a89df2dfSMatthias Ringwald 47*a89df2dfSMatthias Ringwald #ifdef HAVE_PORTAUDIO 48*a89df2dfSMatthias Ringwald 49*a89df2dfSMatthias Ringwald #define PA_SAMPLE_TYPE paInt16 50*a89df2dfSMatthias Ringwald #define NUM_FRAMES_PER_PA_BUFFER 512 51*a89df2dfSMatthias Ringwald #define NUM_OUTPUT_BUFFERS 3 52*a89df2dfSMatthias Ringwald #define NUM_INPUT_BUFFERS 2 53*a89df2dfSMatthias Ringwald #define DRIVER_POLL_INTERVAL_MS 5 54*a89df2dfSMatthias Ringwald 55*a89df2dfSMatthias Ringwald #include <portaudio.h> 56*a89df2dfSMatthias Ringwald 57*a89df2dfSMatthias Ringwald // config 58*a89df2dfSMatthias Ringwald static int num_channels; 59*a89df2dfSMatthias Ringwald static int num_bytes_per_sample; 60*a89df2dfSMatthias Ringwald 61*a89df2dfSMatthias Ringwald // portaudio 62*a89df2dfSMatthias Ringwald static PaStream * stream; 63*a89df2dfSMatthias Ringwald 64*a89df2dfSMatthias Ringwald // client 65*a89df2dfSMatthias Ringwald static void (*playback_callback)(int16_t * buffer, uint16_t num_samples); 66*a89df2dfSMatthias Ringwald static void (*recording_callback)(const int16_t * buffer, uint16_t num_samples); 67*a89df2dfSMatthias Ringwald 68*a89df2dfSMatthias Ringwald // output buffer 69*a89df2dfSMatthias Ringwald static int16_t output_buffer_a[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 70*a89df2dfSMatthias Ringwald static int16_t output_buffer_b[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 71*a89df2dfSMatthias Ringwald static int16_t output_buffer_c[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 72*a89df2dfSMatthias Ringwald static int16_t * output_buffers[NUM_OUTPUT_BUFFERS] = { output_buffer_a, output_buffer_b, output_buffer_c}; 73*a89df2dfSMatthias Ringwald static int output_buffer_to_play; 74*a89df2dfSMatthias Ringwald static int output_buffer_to_fill; 75*a89df2dfSMatthias Ringwald 76*a89df2dfSMatthias Ringwald // input buffer 77*a89df2dfSMatthias Ringwald static int16_t input_buffer_a[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 78*a89df2dfSMatthias Ringwald static int16_t input_buffer_b[NUM_FRAMES_PER_PA_BUFFER * 2]; // stereo 79*a89df2dfSMatthias Ringwald static int16_t * input_buffers[NUM_INPUT_BUFFERS] = { input_buffer_a, input_buffer_b}; 80*a89df2dfSMatthias Ringwald static int input_buffer_to_record; 81*a89df2dfSMatthias Ringwald static int input_buffer_to_fill; 82*a89df2dfSMatthias Ringwald 83*a89df2dfSMatthias Ringwald 84*a89df2dfSMatthias Ringwald // timer to fill output ring buffer 85*a89df2dfSMatthias Ringwald static btstack_timer_source_t driver_timer; 86*a89df2dfSMatthias Ringwald 87*a89df2dfSMatthias Ringwald static int portaudio_callback( const void * inputBuffer, 88*a89df2dfSMatthias Ringwald void * outputBuffer, 89*a89df2dfSMatthias Ringwald unsigned long samples_per_buffer, 90*a89df2dfSMatthias Ringwald const PaStreamCallbackTimeInfo * timeInfo, 91*a89df2dfSMatthias Ringwald PaStreamCallbackFlags statusFlags, 92*a89df2dfSMatthias Ringwald void * userData ) { 93*a89df2dfSMatthias Ringwald 94*a89df2dfSMatthias Ringwald /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */ 95*a89df2dfSMatthias Ringwald 96*a89df2dfSMatthias Ringwald (void) timeInfo; /* Prevent unused variable warnings. */ 97*a89df2dfSMatthias Ringwald (void) statusFlags; 98*a89df2dfSMatthias Ringwald (void) userData; 99*a89df2dfSMatthias Ringwald (void) samples_per_buffer; 100*a89df2dfSMatthias Ringwald 101*a89df2dfSMatthias Ringwald // -- playback / output 102*a89df2dfSMatthias Ringwald if (playback_callback){ 103*a89df2dfSMatthias Ringwald 104*a89df2dfSMatthias Ringwald // fill from one of our buffers 105*a89df2dfSMatthias Ringwald memcpy(outputBuffer, output_buffers[output_buffer_to_play], NUM_FRAMES_PER_PA_BUFFER * num_bytes_per_sample); 106*a89df2dfSMatthias Ringwald 107*a89df2dfSMatthias Ringwald // next 108*a89df2dfSMatthias Ringwald output_buffer_to_play = (output_buffer_to_play + 1 ) % NUM_OUTPUT_BUFFERS; 109*a89df2dfSMatthias Ringwald } 110*a89df2dfSMatthias Ringwald 111*a89df2dfSMatthias Ringwald // -- recording / input 112*a89df2dfSMatthias Ringwald if (recording_callback){ 113*a89df2dfSMatthias Ringwald 114*a89df2dfSMatthias Ringwald // store in one of our buffers 115*a89df2dfSMatthias Ringwald memcpy(input_buffers[input_buffer_to_fill], inputBuffer, NUM_FRAMES_PER_PA_BUFFER * num_bytes_per_sample); 116*a89df2dfSMatthias Ringwald 117*a89df2dfSMatthias Ringwald // next 118*a89df2dfSMatthias Ringwald input_buffer_to_fill = (input_buffer_to_fill + 1 ) % NUM_INPUT_BUFFERS; 119*a89df2dfSMatthias Ringwald } 120*a89df2dfSMatthias Ringwald 121*a89df2dfSMatthias Ringwald return 0; 122*a89df2dfSMatthias Ringwald } 123*a89df2dfSMatthias Ringwald 124*a89df2dfSMatthias Ringwald static void driver_timer_handler(btstack_timer_source_t * ts){ 125*a89df2dfSMatthias Ringwald 126*a89df2dfSMatthias Ringwald // playback buffer ready to fill 127*a89df2dfSMatthias Ringwald if (playback_callback && output_buffer_to_play != output_buffer_to_fill){ 128*a89df2dfSMatthias Ringwald (*playback_callback)(output_buffers[output_buffer_to_fill], NUM_FRAMES_PER_PA_BUFFER); 129*a89df2dfSMatthias Ringwald 130*a89df2dfSMatthias Ringwald // next 131*a89df2dfSMatthias Ringwald output_buffer_to_fill = (output_buffer_to_fill + 1 ) % NUM_OUTPUT_BUFFERS; 132*a89df2dfSMatthias Ringwald } 133*a89df2dfSMatthias Ringwald 134*a89df2dfSMatthias Ringwald // recording buffer ready to process 135*a89df2dfSMatthias Ringwald if (recording_callback && input_buffer_to_record != input_buffer_to_fill){ 136*a89df2dfSMatthias Ringwald 137*a89df2dfSMatthias Ringwald (*recording_callback)(input_buffers[input_buffer_to_record], NUM_FRAMES_PER_PA_BUFFER); 138*a89df2dfSMatthias Ringwald 139*a89df2dfSMatthias Ringwald // next 140*a89df2dfSMatthias Ringwald input_buffer_to_record = (input_buffer_to_record + 1 ) % NUM_INPUT_BUFFERS; 141*a89df2dfSMatthias Ringwald } 142*a89df2dfSMatthias Ringwald 143*a89df2dfSMatthias Ringwald // re-set timer 144*a89df2dfSMatthias Ringwald btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS); 145*a89df2dfSMatthias Ringwald btstack_run_loop_add_timer(ts); 146*a89df2dfSMatthias Ringwald } 147*a89df2dfSMatthias Ringwald 148*a89df2dfSMatthias Ringwald static int btstack_audio_portaudio_init( 149*a89df2dfSMatthias Ringwald uint8_t channels, 150*a89df2dfSMatthias Ringwald uint32_t samplerate, 151*a89df2dfSMatthias Ringwald void (*playback)(int16_t * buffer, uint16_t num_samples), 152*a89df2dfSMatthias Ringwald void (*recording)(const int16_t * buffer, uint16_t num_samples) 153*a89df2dfSMatthias Ringwald ){ 154*a89df2dfSMatthias Ringwald 155*a89df2dfSMatthias Ringwald num_channels = channels; 156*a89df2dfSMatthias Ringwald num_bytes_per_sample = 2 * channels; 157*a89df2dfSMatthias Ringwald 158*a89df2dfSMatthias Ringwald /* -- initialize PortAudio -- */ 159*a89df2dfSMatthias Ringwald PaError err = Pa_Initialize(); 160*a89df2dfSMatthias Ringwald if (err != paNoError){ 161*a89df2dfSMatthias Ringwald log_error("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 162*a89df2dfSMatthias Ringwald return err; 163*a89df2dfSMatthias Ringwald } 164*a89df2dfSMatthias Ringwald 165*a89df2dfSMatthias Ringwald PaStreamParameters theInputParameters; 166*a89df2dfSMatthias Ringwald PaStreamParameters theOutputParameters; 167*a89df2dfSMatthias Ringwald 168*a89df2dfSMatthias Ringwald PaStreamParameters * inputParameters = NULL; 169*a89df2dfSMatthias Ringwald PaStreamParameters * outputParameters = NULL; 170*a89df2dfSMatthias Ringwald 171*a89df2dfSMatthias Ringwald /* -- setup output -- */ 172*a89df2dfSMatthias Ringwald if (playback){ 173*a89df2dfSMatthias Ringwald theOutputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ 174*a89df2dfSMatthias Ringwald theOutputParameters.channelCount = channels; 175*a89df2dfSMatthias Ringwald theOutputParameters.sampleFormat = PA_SAMPLE_TYPE; 176*a89df2dfSMatthias Ringwald theOutputParameters.suggestedLatency = Pa_GetDeviceInfo( theOutputParameters.device )->defaultHighOutputLatency; 177*a89df2dfSMatthias Ringwald theOutputParameters.hostApiSpecificStreamInfo = NULL; 178*a89df2dfSMatthias Ringwald 179*a89df2dfSMatthias Ringwald const PaDeviceInfo *outputDeviceInfo; 180*a89df2dfSMatthias Ringwald outputDeviceInfo = Pa_GetDeviceInfo( theOutputParameters.device ); 181*a89df2dfSMatthias Ringwald log_info("PortAudio: Output device: %s", outputDeviceInfo->name); 182*a89df2dfSMatthias Ringwald 183*a89df2dfSMatthias Ringwald outputParameters = &theOutputParameters; 184*a89df2dfSMatthias Ringwald } 185*a89df2dfSMatthias Ringwald 186*a89df2dfSMatthias Ringwald /* -- setup input -- */ 187*a89df2dfSMatthias Ringwald if (recording){ 188*a89df2dfSMatthias Ringwald theInputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ 189*a89df2dfSMatthias Ringwald theInputParameters.channelCount = channels; 190*a89df2dfSMatthias Ringwald theInputParameters.sampleFormat = PA_SAMPLE_TYPE; 191*a89df2dfSMatthias Ringwald theInputParameters.suggestedLatency = Pa_GetDeviceInfo( theInputParameters.device )->defaultHighInputLatency; 192*a89df2dfSMatthias Ringwald theInputParameters.hostApiSpecificStreamInfo = NULL; 193*a89df2dfSMatthias Ringwald 194*a89df2dfSMatthias Ringwald const PaDeviceInfo *inputDeviceInfo; 195*a89df2dfSMatthias Ringwald inputDeviceInfo = Pa_GetDeviceInfo( theInputParameters.device ); 196*a89df2dfSMatthias Ringwald log_info("PortAudio: Input device: %s", inputDeviceInfo->name); 197*a89df2dfSMatthias Ringwald 198*a89df2dfSMatthias Ringwald inputParameters = &theInputParameters; 199*a89df2dfSMatthias Ringwald } 200*a89df2dfSMatthias Ringwald 201*a89df2dfSMatthias Ringwald /* -- setup stream -- */ 202*a89df2dfSMatthias Ringwald err = Pa_OpenStream( 203*a89df2dfSMatthias Ringwald &stream, 204*a89df2dfSMatthias Ringwald inputParameters, 205*a89df2dfSMatthias Ringwald outputParameters, 206*a89df2dfSMatthias Ringwald samplerate, 207*a89df2dfSMatthias Ringwald NUM_FRAMES_PER_PA_BUFFER, 208*a89df2dfSMatthias Ringwald paClipOff, /* we won't output out of range samples so don't bother clipping them */ 209*a89df2dfSMatthias Ringwald portaudio_callback, /* use callback */ 210*a89df2dfSMatthias Ringwald NULL ); 211*a89df2dfSMatthias Ringwald 212*a89df2dfSMatthias Ringwald if (err != paNoError){ 213*a89df2dfSMatthias Ringwald log_error("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); 214*a89df2dfSMatthias Ringwald return err; 215*a89df2dfSMatthias Ringwald } 216*a89df2dfSMatthias Ringwald log_info("PortAudio: stream opened"); 217*a89df2dfSMatthias Ringwald 218*a89df2dfSMatthias Ringwald const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream); 219*a89df2dfSMatthias Ringwald log_info("PortAudio: Input latency: %f", stream_info->inputLatency); 220*a89df2dfSMatthias Ringwald log_info("PortAudio: Output latency: %f", stream_info->outputLatency); 221*a89df2dfSMatthias Ringwald 222*a89df2dfSMatthias Ringwald playback_callback = playback; 223*a89df2dfSMatthias Ringwald recording_callback = recording; 224*a89df2dfSMatthias Ringwald 225*a89df2dfSMatthias Ringwald return 0; 226*a89df2dfSMatthias Ringwald } 227*a89df2dfSMatthias Ringwald 228*a89df2dfSMatthias Ringwald static void btstack_audio_portaudio_start_stream(void){ 229*a89df2dfSMatthias Ringwald 230*a89df2dfSMatthias Ringwald // fill buffer once 231*a89df2dfSMatthias Ringwald (*playback_callback)(output_buffer_a, NUM_FRAMES_PER_PA_BUFFER); 232*a89df2dfSMatthias Ringwald (*playback_callback)(output_buffer_b, NUM_FRAMES_PER_PA_BUFFER); 233*a89df2dfSMatthias Ringwald output_buffer_to_play = 0; 234*a89df2dfSMatthias Ringwald output_buffer_to_fill = 2; 235*a89df2dfSMatthias Ringwald 236*a89df2dfSMatthias Ringwald /* -- start stream -- */ 237*a89df2dfSMatthias Ringwald PaError err = Pa_StartStream(stream); 238*a89df2dfSMatthias Ringwald if (err != paNoError){ 239*a89df2dfSMatthias Ringwald log_error("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); 240*a89df2dfSMatthias Ringwald return; 241*a89df2dfSMatthias Ringwald } 242*a89df2dfSMatthias Ringwald 243*a89df2dfSMatthias Ringwald // start timer 244*a89df2dfSMatthias Ringwald btstack_run_loop_set_timer_handler(&driver_timer, &driver_timer_handler); 245*a89df2dfSMatthias Ringwald btstack_run_loop_set_timer(&driver_timer, DRIVER_POLL_INTERVAL_MS); 246*a89df2dfSMatthias Ringwald btstack_run_loop_add_timer(&driver_timer); 247*a89df2dfSMatthias Ringwald } 248*a89df2dfSMatthias Ringwald 249*a89df2dfSMatthias Ringwald static void btstack_audio_portaudio_close(void){ 250*a89df2dfSMatthias Ringwald 251*a89df2dfSMatthias Ringwald // stop timer 252*a89df2dfSMatthias Ringwald btstack_run_loop_remove_timer(&driver_timer); 253*a89df2dfSMatthias Ringwald 254*a89df2dfSMatthias Ringwald log_info("PortAudio: Stream closed"); 255*a89df2dfSMatthias Ringwald PaError err = Pa_StopStream(stream); 256*a89df2dfSMatthias Ringwald if (err != paNoError){ 257*a89df2dfSMatthias Ringwald log_error("Error stopping the stream: \"%s\"", Pa_GetErrorText(err)); 258*a89df2dfSMatthias Ringwald return; 259*a89df2dfSMatthias Ringwald } 260*a89df2dfSMatthias Ringwald err = Pa_CloseStream(stream); 261*a89df2dfSMatthias Ringwald if (err != paNoError){ 262*a89df2dfSMatthias Ringwald log_error("Error closing the stream: \"%s\"", Pa_GetErrorText(err)); 263*a89df2dfSMatthias Ringwald return; 264*a89df2dfSMatthias Ringwald } 265*a89df2dfSMatthias Ringwald err = Pa_Terminate(); 266*a89df2dfSMatthias Ringwald if (err != paNoError){ 267*a89df2dfSMatthias Ringwald log_error("Error terminating portaudio: \"%s\"", Pa_GetErrorText(err)); 268*a89df2dfSMatthias Ringwald return; 269*a89df2dfSMatthias Ringwald } 270*a89df2dfSMatthias Ringwald } 271*a89df2dfSMatthias Ringwald 272*a89df2dfSMatthias Ringwald static const btstack_audio_t btstack_audio_portaudio = { 273*a89df2dfSMatthias Ringwald /* int (*init)(..);*/ &btstack_audio_portaudio_init, 274*a89df2dfSMatthias Ringwald /* void (*start_stream(void));*/ &btstack_audio_portaudio_start_stream, 275*a89df2dfSMatthias Ringwald /* void (*close)(void); */ &btstack_audio_portaudio_close 276*a89df2dfSMatthias Ringwald }; 277*a89df2dfSMatthias Ringwald 278*a89df2dfSMatthias Ringwald const btstack_audio_t * btstack_audio_portaudio_get_instance(void){ 279*a89df2dfSMatthias Ringwald return &btstack_audio_portaudio; 280*a89df2dfSMatthias Ringwald } 281*a89df2dfSMatthias Ringwald 282*a89df2dfSMatthias Ringwald #endif 283