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 BLUEKITCHEN
24 * GMBH 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 5
52 #define NUM_INPUT_BUFFERS 5
53 #define DRIVER_POLL_INTERVAL_MS 5
54
55 #ifndef MAX_NR_AUDIO_CHANNELS
56 #define MAX_NR_AUDIO_CHANNELS 2
57 #endif
58
59 #include <portaudio.h>
60
61 // config
62 static int num_channels_sink;
63 static int num_channels_source;
64 static int num_bytes_per_sample_sink;
65 static int num_bytes_per_sample_source;
66
67 static const char * sink_device_name;
68 static const char * source_device_name = "4-chan";
69
70 // portaudio
71 static int portaudio_initialized;
72
73 // state
74 static int source_initialized;
75 static int sink_initialized;
76 static int source_active;
77 static int sink_active;
78
79 static uint8_t sink_volume;
80
81 static PaStream * stream_source;
82 static PaStream * stream_sink;
83
84 // client
85 static void (*playback_callback)(int16_t * buffer, uint16_t num_samples);
86 static void (*recording_callback)(const int16_t * buffer, uint16_t num_samples);
87
88 // output buffer
89 static int16_t output_buffer_storage[NUM_OUTPUT_BUFFERS * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS];
90 static int16_t * output_buffers[NUM_OUTPUT_BUFFERS];
91 static int output_buffer_to_play;
92 static int output_buffer_to_fill;
93
94 // input buffer
95 static int16_t input_buffer_storage[NUM_INPUT_BUFFERS * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS];
96 static int16_t * input_buffers[NUM_INPUT_BUFFERS];
97 static int input_buffer_to_record;
98 static int input_buffer_to_fill;
99
100
101 // timer to fill output ring buffer
102 static btstack_timer_source_t driver_timer_sink;
103 static btstack_timer_source_t driver_timer_source;
104
portaudio_callback_sink(const void * inputBuffer,void * outputBuffer,unsigned long frames_per_buffer,const PaStreamCallbackTimeInfo * timeInfo,PaStreamCallbackFlags statusFlags,void * userData)105 static int portaudio_callback_sink( const void * inputBuffer,
106 void * outputBuffer,
107 unsigned long frames_per_buffer,
108 const PaStreamCallbackTimeInfo * timeInfo,
109 PaStreamCallbackFlags statusFlags,
110 void * userData ) {
111
112 /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */
113
114 (void) timeInfo; /* Prevent unused variable warnings. */
115 (void) statusFlags;
116 (void) userData;
117 (void) frames_per_buffer;
118 (void) inputBuffer;
119
120 // simplified volume control
121 uint16_t index;
122 int16_t * from_buffer = output_buffers[output_buffer_to_play];
123 int16_t * to_buffer = (int16_t *) outputBuffer;
124 btstack_assert(frames_per_buffer == NUM_FRAMES_PER_PA_BUFFER);
125
126 #if 0
127 // up to 8 right shifts
128 int right_shift = 8 - btstack_min(8, ((sink_volume + 15) / 16));
129 for (index = 0; index < (NUM_FRAMES_PER_PA_BUFFER * num_channels_sink); index++){
130 *to_buffer++ = (*from_buffer++) >> right_shift;
131 }
132 #else
133 // multiply with volume ^ 4
134 int16_t x2 = sink_volume * sink_volume;
135 int16_t x4 = (x2 * x2) >> 14;
136 for (index = 0; index < (NUM_FRAMES_PER_PA_BUFFER * num_channels_sink); index++){
137 *to_buffer++ = ((*from_buffer++) * x4) >> 14;
138 }
139 #endif
140
141 // next
142 output_buffer_to_play = (output_buffer_to_play + 1 ) % NUM_OUTPUT_BUFFERS;
143
144 return 0;
145 }
146
portaudio_callback_source(const void * inputBuffer,void * outputBuffer,unsigned long samples_per_buffer,const PaStreamCallbackTimeInfo * timeInfo,PaStreamCallbackFlags statusFlags,void * userData)147 static int portaudio_callback_source( const void * inputBuffer,
148 void * outputBuffer,
149 unsigned long samples_per_buffer,
150 const PaStreamCallbackTimeInfo * timeInfo,
151 PaStreamCallbackFlags statusFlags,
152 void * userData ) {
153
154 /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */
155
156 (void) timeInfo; /* Prevent unused variable warnings. */
157 (void) statusFlags;
158 (void) userData;
159 (void) samples_per_buffer;
160 (void) outputBuffer;
161
162 // store in one of our buffers
163 memcpy(input_buffers[input_buffer_to_fill], inputBuffer, NUM_FRAMES_PER_PA_BUFFER * num_bytes_per_sample_source);
164
165 // next
166 input_buffer_to_fill = (input_buffer_to_fill + 1 ) % NUM_INPUT_BUFFERS;
167
168 return 0;
169 }
170
driver_timer_handler_sink(btstack_timer_source_t * ts)171 static void driver_timer_handler_sink(btstack_timer_source_t * ts){
172
173 // playback buffer ready to fill
174 while (output_buffer_to_play != output_buffer_to_fill){
175 (*playback_callback)(output_buffers[output_buffer_to_fill], NUM_FRAMES_PER_PA_BUFFER);
176
177 // next
178 output_buffer_to_fill = (output_buffer_to_fill + 1 ) % NUM_OUTPUT_BUFFERS;
179 }
180
181 // re-set timer
182 btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
183 btstack_run_loop_add_timer(ts);
184 }
185
driver_timer_handler_source(btstack_timer_source_t * ts)186 static void driver_timer_handler_source(btstack_timer_source_t * ts){
187
188 // recording buffer ready to process
189 if (input_buffer_to_record != input_buffer_to_fill){
190
191 (*recording_callback)(input_buffers[input_buffer_to_record], NUM_FRAMES_PER_PA_BUFFER);
192
193 // next
194 input_buffer_to_record = (input_buffer_to_record + 1 ) % NUM_INPUT_BUFFERS;
195 }
196
197 // re-set timer
198 btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
199 btstack_run_loop_add_timer(ts);
200 }
201
btstack_audio_portaudio_sink_init(uint8_t channels,uint32_t samplerate,void (* playback)(int16_t * buffer,uint16_t num_samples))202 static int btstack_audio_portaudio_sink_init(
203 uint8_t channels,
204 uint32_t samplerate,
205 void (*playback)(int16_t * buffer, uint16_t num_samples)
206 ){
207 PaError err;
208
209 btstack_assert(channels <= MAX_NR_AUDIO_CHANNELS);
210
211 num_channels_sink = channels;
212 num_bytes_per_sample_sink = 2 * channels;
213
214 if (!playback){
215 log_error("No playback callback");
216 return 1;
217 }
218
219 for (int i=0;i<NUM_OUTPUT_BUFFERS;i++){
220 output_buffers[i] = &output_buffer_storage[i * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS];
221 }
222
223 /* -- initialize PortAudio -- */
224 if (!portaudio_initialized){
225 err = Pa_Initialize();
226 if (err != paNoError){
227 log_error("Portudio: error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err));
228 return err;
229 }
230 portaudio_initialized = 1;
231 }
232
233 /* -- find output device by name if requested -- */
234 PaDeviceIndex device_index = -1;
235 const PaDeviceInfo *output_device_info;
236 if (sink_device_name != NULL){
237 int num_devices = Pa_GetDeviceCount();
238 for (int i = 0; i < num_devices; i++) {
239 output_device_info = Pa_GetDeviceInfo(i);
240 // Match device by prefix
241 if (strncmp(output_device_info->name, sink_device_name, strlen(sink_device_name)) == 0) {
242 device_index = i;
243 break;
244 }
245 }
246 }
247
248 /* -- use default device otherwise -- */
249 if (device_index < 0){
250 device_index = Pa_GetDefaultOutputDevice();
251 output_device_info = Pa_GetDeviceInfo(device_index );
252 }
253 /* -- setup output -- */
254 PaStreamParameters output_parameters;
255 output_parameters.device = device_index;
256 output_parameters.channelCount = channels;
257 output_parameters.sampleFormat = PA_SAMPLE_TYPE;
258 output_parameters.suggestedLatency = output_device_info->defaultHighOutputLatency;
259 output_parameters.hostApiSpecificStreamInfo = NULL;
260
261 log_info("PortAudio: sink device: %s", output_device_info->name);
262 UNUSED(output_device_info);
263
264 /* -- setup stream -- */
265 err = Pa_OpenStream(
266 &stream_sink,
267 NULL,
268 &output_parameters,
269 samplerate,
270 NUM_FRAMES_PER_PA_BUFFER,
271 paClipOff, /* we won't output out of range samples so don't bother clipping them */
272 portaudio_callback_sink, /* use callback */
273 NULL );
274
275 if (err != paNoError){
276 log_error("Portudio: error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err));
277 return err;
278 }
279 log_info("PortAudio: sink stream created");
280
281 const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream_sink);
282 log_info("PortAudio: sink latency: %f", stream_info->outputLatency);
283 UNUSED(stream_info);
284
285 playback_callback = playback;
286
287 sink_initialized = 1;
288 sink_volume = 127;
289
290 return 0;
291 }
292
btstack_audio_portaudio_source_init(uint8_t channels,uint32_t samplerate,void (* recording)(const int16_t * buffer,uint16_t num_samples))293 static int btstack_audio_portaudio_source_init(
294 uint8_t channels,
295 uint32_t samplerate,
296 void (*recording)(const int16_t * buffer, uint16_t num_samples)
297 ){
298 PaError err;
299
300 btstack_assert(channels <= MAX_NR_AUDIO_CHANNELS);
301
302 num_channels_source = channels;
303 num_bytes_per_sample_source = 2 * channels;
304
305 if (!recording){
306 log_error("No recording callback");
307 return 1;
308 }
309
310 for (int i=0;i<NUM_INPUT_BUFFERS;i++){
311 input_buffers[i] = &input_buffer_storage[i * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS];
312 }
313
314 /* -- initialize PortAudio -- */
315 if (!portaudio_initialized){
316 err = Pa_Initialize();
317 if (err != paNoError){
318 log_error("Portudio: Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err));
319 return err;
320 }
321 portaudio_initialized = 1;
322 }
323
324 /* -- find input device by name if requested -- */
325 PaDeviceIndex device_index = -1;
326 const PaDeviceInfo *input_device_info;
327 if (source_device_name != NULL){
328 int num_devices = Pa_GetDeviceCount();
329 for (int i = 0; i < num_devices; i++) {
330 input_device_info = Pa_GetDeviceInfo(i);
331 // Match device by prefix
332 if (strncmp(input_device_info->name, source_device_name, strlen(source_device_name)) == 0) {
333 device_index = i;
334 break;
335 }
336 }
337 }
338
339 /* -- use default device otherwise -- */
340 if (device_index < 0){
341 device_index = Pa_GetDefaultInputDevice();
342 input_device_info = Pa_GetDeviceInfo(device_index );
343 }
344
345 /* -- setup input -- */
346 PaStreamParameters theInputParameters;
347 theInputParameters.device = device_index;
348 theInputParameters.channelCount = channels;
349 theInputParameters.sampleFormat = PA_SAMPLE_TYPE;
350 theInputParameters.suggestedLatency = input_device_info->defaultHighInputLatency;
351 theInputParameters.hostApiSpecificStreamInfo = NULL;
352
353 log_info("PortAudio: source device: %s", input_device_info->name);
354 UNUSED(input_device_info);
355
356 /* -- setup stream -- */
357 err = Pa_OpenStream(
358 &stream_source,
359 &theInputParameters,
360 NULL,
361 samplerate,
362 NUM_FRAMES_PER_PA_BUFFER,
363 paClipOff, /* we won't output out of range samples so don't bother clipping them */
364 portaudio_callback_source, /* use callback */
365 NULL );
366
367 if (err != paNoError){
368 log_error("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err));
369 return err;
370 }
371 log_info("PortAudio: source stream created");
372
373 const PaStreamInfo * stream_info = Pa_GetStreamInfo(stream_source);
374 log_info("PortAudio: source latency: %f", stream_info->inputLatency);
375 UNUSED(stream_info);
376
377 recording_callback = recording;
378
379 source_initialized = 1;
380
381 return 0;
382 }
383
btstack_audio_portaudio_sink_get_samplerate(void)384 static uint32_t btstack_audio_portaudio_sink_get_samplerate(void) {
385 const PaStreamInfo *stream_info = Pa_GetStreamInfo(stream_sink);
386 return stream_info->sampleRate;
387 }
388
btstack_audio_portaudio_source_get_samplerate(void)389 static uint32_t btstack_audio_portaudio_source_get_samplerate(void) {
390 const PaStreamInfo *stream_info = Pa_GetStreamInfo(stream_source);
391 return stream_info->sampleRate;
392 }
393
btstack_audio_portaudio_sink_set_volume(uint8_t volume)394 static void btstack_audio_portaudio_sink_set_volume(uint8_t volume){
395 sink_volume = volume;
396 }
397
btstack_audio_portaudio_source_set_gain(uint8_t gain)398 static void btstack_audio_portaudio_source_set_gain(uint8_t gain){
399 UNUSED(gain);
400 }
401
btstack_audio_portaudio_sink_start_stream(void)402 static void btstack_audio_portaudio_sink_start_stream(void){
403
404 if (!playback_callback) return;
405
406 // fill buffers once
407 uint8_t i;
408 for (i=0;i<NUM_OUTPUT_BUFFERS-1;i++){
409 (*playback_callback)(&output_buffer_storage[i * NUM_FRAMES_PER_PA_BUFFER * MAX_NR_AUDIO_CHANNELS], NUM_FRAMES_PER_PA_BUFFER);
410 }
411 output_buffer_to_play = 0;
412 output_buffer_to_fill = NUM_OUTPUT_BUFFERS-1;
413
414 /* -- start stream -- */
415 PaError err = Pa_StartStream(stream_sink);
416 if (err != paNoError){
417 log_error("PortAudio: error starting sink stream: \"%s\"\n", Pa_GetErrorText(err));
418 return;
419 }
420
421 // start timer
422 btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink);
423 btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS);
424 btstack_run_loop_add_timer(&driver_timer_sink);
425
426 sink_active = 1;
427 }
428
btstack_audio_portaudio_source_start_stream(void)429 static void btstack_audio_portaudio_source_start_stream(void){
430
431 if (!recording_callback) return;
432
433 /* -- start stream -- */
434 PaError err = Pa_StartStream(stream_source);
435 if (err != paNoError){
436 log_error("PortAudio: error starting source stream: \"%s\"\n", Pa_GetErrorText(err));
437 return;
438 }
439
440 // start timer
441 btstack_run_loop_set_timer_handler(&driver_timer_source, &driver_timer_handler_source);
442 btstack_run_loop_set_timer(&driver_timer_source, DRIVER_POLL_INTERVAL_MS);
443 btstack_run_loop_add_timer(&driver_timer_source);
444
445 source_active = 1;
446 }
447
btstack_audio_portaudio_sink_stop_stream(void)448 static void btstack_audio_portaudio_sink_stop_stream(void){
449
450 if (!playback_callback) return;
451 if (!sink_active) return;
452
453 // stop timer
454 btstack_run_loop_remove_timer(&driver_timer_sink);
455
456 PaError err = Pa_StopStream(stream_sink);
457 if (err != paNoError){
458 log_error("PortAudio: error stopping sink stream: \"%s\"", Pa_GetErrorText(err));
459 return;
460 }
461
462 sink_active = 0;
463 }
464
btstack_audio_portaudio_source_stop_stream(void)465 static void btstack_audio_portaudio_source_stop_stream(void){
466
467 if (!recording_callback) return;
468 if (!source_active) return;
469
470 // stop timer
471 btstack_run_loop_remove_timer(&driver_timer_source);
472
473 PaError err = Pa_StopStream(stream_source);
474 if (err != paNoError){
475 log_error("PortAudio: error stopping source stream: \"%s\"", Pa_GetErrorText(err));
476 return;
477 }
478
479 source_active = 0;
480 }
481
btstack_audio_portaudio_close_pa_if_not_needed(void)482 static void btstack_audio_portaudio_close_pa_if_not_needed(void){
483 if (source_initialized) return;
484 if (sink_initialized) return;
485 PaError err = Pa_Terminate();
486 if (err != paNoError){
487 log_error("Portudio: Error terminating portaudio: \"%s\"", Pa_GetErrorText(err));
488 return;
489 }
490 portaudio_initialized = 0;
491 }
492
btstack_audio_portaudio_sink_close(void)493 static void btstack_audio_portaudio_sink_close(void){
494
495 if (!playback_callback) return;
496
497 if (sink_active){
498 btstack_audio_portaudio_sink_stop_stream();
499 }
500
501 PaError err = Pa_CloseStream(stream_sink);
502 if (err != paNoError){
503 log_error("PortAudio: error closing sink stream: \"%s\"", Pa_GetErrorText(err));
504 return;
505 }
506
507 sink_initialized = 0;
508 btstack_audio_portaudio_close_pa_if_not_needed();
509 }
510
btstack_audio_portaudio_source_close(void)511 static void btstack_audio_portaudio_source_close(void){
512
513 if (!recording_callback) return;
514
515 if (source_active){
516 btstack_audio_portaudio_source_stop_stream();
517 }
518
519 PaError err = Pa_CloseStream(stream_source);
520 if (err != paNoError){
521 log_error("PortAudio: error closing source stream: \"%s\"", Pa_GetErrorText(err));
522 return;
523 }
524
525 source_initialized = 0;
526 btstack_audio_portaudio_close_pa_if_not_needed();
527 }
528
529 static const btstack_audio_sink_t btstack_audio_portaudio_sink = {
530 /* int (*init)(..);*/ &btstack_audio_portaudio_sink_init,
531 /* uint32_t (*get_samplerate)() */ &btstack_audio_portaudio_sink_get_samplerate,
532 /* void (*set_volume)(uint8_t volume); */ &btstack_audio_portaudio_sink_set_volume,
533 /* void (*start_stream(void));*/ &btstack_audio_portaudio_sink_start_stream,
534 /* void (*stop_stream)(void) */ &btstack_audio_portaudio_sink_stop_stream,
535 /* void (*close)(void); */ &btstack_audio_portaudio_sink_close
536 };
537
538 static const btstack_audio_source_t btstack_audio_portaudio_source = {
539 /* int (*init)(..);*/ &btstack_audio_portaudio_source_init,
540 /* uint32_t (*get_samplerate)() */ &btstack_audio_portaudio_source_get_samplerate,
541 /* void (*set_gain)(uint8_t gain); */ &btstack_audio_portaudio_source_set_gain,
542 /* void (*start_stream(void));*/ &btstack_audio_portaudio_source_start_stream,
543 /* void (*stop_stream)(void) */ &btstack_audio_portaudio_source_stop_stream,
544 /* void (*close)(void); */ &btstack_audio_portaudio_source_close
545 };
546
btstack_audio_portaudio_sink_get_instance(void)547 const btstack_audio_sink_t * btstack_audio_portaudio_sink_get_instance(void){
548 return &btstack_audio_portaudio_sink;
549 }
550
btstack_audio_portaudio_source_get_instance(void)551 const btstack_audio_source_t * btstack_audio_portaudio_source_get_instance(void){
552 return &btstack_audio_portaudio_source;
553 }
554
btstack_audio_portaudio_sink_set_device(const char * device_name)555 void btstack_audio_portaudio_sink_set_device(const char * device_name){
556 sink_device_name = device_name;
557 }
558
btstack_audio_portaudio_source_set_device(const char * device_name)559 void btstack_audio_portaudio_source_set_device(const char * device_name){
560 source_device_name = device_name;
561 }
562
563 #endif
564