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