1 /*
2  *  Squeezelite - lightweight headless squeezebox emulator
3  *
4  *  (c) Adrian Smith 2012-2015, triode1@btinternet.com
5  *      Ralph Irving 2015-2017, ralph_irving@hotmail.com
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 // Portaudio output
23 
24 #include "squeezelite.h"
25 
26 #if PORTAUDIO
27 
28 #include <portaudio.h>
29 
30 #if WIN
31 #ifndef PA18API
32 #include <pa_win_wasapi.h>
33 #endif
34 #define snprintf _snprintf
35 #endif
36 
37 #if OSX && !defined(OSXPPC)
38 #include <pa_mac_core.h>
39 #endif
40 
41 #if PA18API
42 typedef int PaDeviceIndex;
43 typedef double PaTime;
44 
45 typedef struct PaStreamParameters
46 {
47     PaDeviceIndex device;
48     int channelCount;
49     PaSampleFormat sampleFormat;
50     PaTime suggestedLatency;
51 
52 } PaStreamParameters;
53 
54 static int paContinue=0; /* Signal that the stream should continue invoking the callback and processing audio. */
55 static int paComplete=1; /* Signal that the stream should stop invoking the callback and finish once all output */
56 			 /* samples have played. */
57 
58 static unsigned paFramesPerBuffer = 4096;
59 static unsigned paNumberOfBuffers = 4;
60 #endif /* PA18API */
61 
62 // ouput device
63 static struct {
64 	unsigned rate;
65 	PaStream *stream;
66 } pa;
67 
68 static log_level loglevel;
69 
70 static bool running = true;
71 
72 extern struct outputstate output;
73 extern struct buffer *outputbuf;
74 
75 #define LOCK   mutex_lock(outputbuf->mutex)
76 #define UNLOCK mutex_unlock(outputbuf->mutex)
77 
78 extern u8_t *silencebuf;
79 #if DSD
80 extern u8_t *silencebuf_dsd;
81 #endif
82 
list_devices(void)83 void list_devices(void) {
84 	PaError err;
85 	int i;
86 
87 	if ((err = Pa_Initialize()) != paNoError) {
88 		LOG_WARN("error initialising port audio: %s", Pa_GetErrorText(err));
89 		return;
90 	}
91 
92 	printf("Output devices:\n");
93 #ifndef PA18API
94 	for (i = 0; i < Pa_GetDeviceCount(); ++i) {
95 		if (Pa_GetDeviceInfo(i)->maxOutputChannels > 1) {
96 			printf("  %i - %s [%s]\n", i, Pa_GetDeviceInfo(i)->name, Pa_GetHostApiInfo(Pa_GetDeviceInfo(i)->hostApi)->name);
97 		}
98 #else
99 	for (i = 0; i < Pa_CountDevices(); ++i) {
100 		printf("  %i - %s\n", i, Pa_GetDeviceInfo(i)->name);
101 #endif
102 	}
103 	printf("\n");
104 
105 	if ((err = Pa_Terminate()) != paNoError) {
106 		LOG_WARN("error closing port audio: %s", Pa_GetErrorText(err));
107 	}
108 }
109 
110 void set_volume(unsigned left, unsigned right) {
111 	LOG_DEBUG("setting internal gain left: %u right: %u", left, right);
112 	LOCK;
113 	output.gainL = left;
114 	output.gainR = right;
115 	UNLOCK;
116 }
117 
118 static int pa_device_id(const char *device) {
119 	int len = strlen(device);
120 	int i;
121 
122 	if (!strncmp(device, "default", 7)) {
123 #ifndef PA18API
124 		return Pa_GetDefaultOutputDevice();
125 #else
126 		return Pa_GetDefaultOutputDeviceID();
127 #endif
128 	}
129 	if (len >= 1 && len <= 2 && device[0] >= '0' && device[0] <= '9') {
130 		return atoi(device);
131 	}
132 
133 #ifndef PA18API
134 #define DEVICE_ID_MAXLEN 256
135 	for (i = 0; i < Pa_GetDeviceCount(); ++i) {
136 		char tmp[DEVICE_ID_MAXLEN];
137 		snprintf(tmp, DEVICE_ID_MAXLEN, "%s [%s]", Pa_GetDeviceInfo(i)->name, Pa_GetHostApiInfo(Pa_GetDeviceInfo(i)->hostApi)->name);
138 		if (!strncmp(tmp, device, len)) {
139 #else
140 	for (i = 0; i < Pa_CountDevices(); ++i) {
141 		if (!strncmp(Pa_GetDeviceInfo(i)->name, device, len)) {
142 #endif
143 			return i;
144 		}
145 	}
146 
147 	return -1;
148 }
149 
150 #ifndef PA18API
151 static int pa_callback(const void *pa_input, void *pa_output, unsigned long pa_frames_wanted,
152 					   const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData);
153 
154 #else
155 static int pa_callback(void *pa_input, void *pa_output, unsigned long pa_frames_wanted,
156 			   PaTimestamp outTime, void *userData);
157 #endif
158 bool test_open(const char *device, unsigned rates[], bool userdef_rates) {
159 	PaStreamParameters outputParameters;
160 	PaError err;
161 	unsigned ref[] TEST_RATES;
162 	int device_id, i, ind;
163 #if WIN
164 	PaWasapiStreamInfo wasapiInfo;
165 	const PaDeviceInfo * paDeviceInfo;
166 	const PaHostApiInfo *paHostApiInfo;
167 
168 #endif
169 	if ((device_id = pa_device_id(device)) == -1) {
170 		LOG_INFO("device %s not found", device);
171 		return false;
172 	}
173 
174 	outputParameters.device = device_id;
175 	outputParameters.channelCount = 2;
176 	outputParameters.sampleFormat = paInt32;
177 #ifndef PA18API
178 	outputParameters.suggestedLatency =
179 		output.latency ? (double)output.latency/(double)1000 : Pa_GetDeviceInfo(outputParameters.device)->defaultHighOutputLatency;
180 	outputParameters.hostApiSpecificStreamInfo = NULL;
181 #if WIN
182 	paDeviceInfo = Pa_GetDeviceInfo( outputParameters.device );
183 	paHostApiInfo = Pa_GetHostApiInfo ( paDeviceInfo->hostApi );
184 
185 	if ( paHostApiInfo != NULL )
186 	{
187 		if ( paHostApiInfo->type == paWASAPI )
188 		{
189 			/* Use exclusive mode for WasApi device, default is shared */
190 			if (output.pa_hostapi_option == 1)
191 			{
192 				wasapiInfo.size = sizeof(PaWasapiStreamInfo);
193 				wasapiInfo.hostApiType = paWASAPI;
194 				wasapiInfo.version = 1;
195 				wasapiInfo.flags = paWinWasapiExclusive;
196 				outputParameters.hostApiSpecificStreamInfo = &wasapiInfo;
197 				LOG_INFO("opening WASAPI device in exclusive mode");
198 			}
199 		}
200 	}
201 #endif /* WIN */
202 #endif
203 
204 	// check supported sample rates
205 	// Note use Pa_OpenStream as it appears more reliable than Pa_IsFormatSupported on some windows apis
206 	for (i = 0, ind = 0; ref[i]; ++i) {
207 #ifndef PA18API
208 		err = Pa_OpenStream(&pa.stream, NULL, &outputParameters, (double)ref[i],
209 			paFramesPerBufferUnspecified, paNoFlag, pa_callback, NULL);
210 #else
211 		err = Pa_OpenStream(&pa.stream, paNoDevice, 0, 0, NULL, outputParameters.device,
212 			outputParameters.channelCount, outputParameters.sampleFormat, NULL, (double)ref[i],
213 			paFramesPerBuffer, paNumberOfBuffers, paNoFlag, pa_callback, NULL);
214 #endif
215 		switch (err) {
216 			case paInvalidSampleRate:
217 				continue;
218 #if WIN
219 #ifndef PA18API
220 			/* Ignore these errors for device probe */
221 			case paUnanticipatedHostError:
222 				continue;
223 
224 			case paInvalidDevice:
225 				continue;
226 #endif
227 #endif
228 			case paNoError:
229 				Pa_CloseStream(pa.stream);
230 				if (!userdef_rates) {
231 					rates[ind++] = ref[i];
232 				}
233 				continue;
234 
235 			default:
236 				/* Any other error is a failure */
237 				LOG_WARN("error opening portaudio stream: %s", Pa_GetErrorText(err));
238 				return false;
239 		}
240 	}
241 
242 	if (!rates[0] && !userdef_rates) {
243 		LOG_WARN("no available rate found");
244 		return false;
245 	}
246 
247 	pa.stream = NULL;
248 	return true;
249 }
250 
251 static void pa_stream_finished(void *userdata) {
252 	if (running) {
253 		LOG_INFO("stream finished");
254 		LOCK;
255 		output.pa_reopen = true;
256 		wake_controller();
257 		UNLOCK;
258 	}
259 }
260 
261 static thread_type monitor_thread;
262 bool monitor_thread_running = false;
263 
264 static void *pa_monitor() {
265 	bool output_off;
266 
267 	LOCK;
268 
269 	if (monitor_thread_running) {
270 		LOG_DEBUG("monitor thread already running");
271 		UNLOCK;
272 		return 0;
273 	}
274 
275 	LOG_DEBUG("start monitor thread");
276 
277 	monitor_thread_running = true;
278 	output_off = (output.state == OUTPUT_OFF);
279 
280 	while (monitor_thread_running) {
281 		if (output_off) {
282 			if (output.state != OUTPUT_OFF) {
283 				LOG_INFO("output on");
284 				break;
285 			}
286 		} else {
287 			// this is a hack to partially support hot plugging of devices
288 			// we rely on terminating and reinitalising PA to get an updated list of devices and use name for output.device
289 			LOG_INFO("probing device %s", output.device);
290 			Pa_Terminate();
291 			Pa_Initialize();
292 			pa.stream = NULL;
293 			if (pa_device_id(output.device) != -1) {
294 				LOG_INFO("device reopen");
295 				break;
296 			}
297 		}
298 
299 		UNLOCK;
300 		sleep(output_off ? 1 : 5);
301 		LOCK;
302 	}
303 
304 	LOG_DEBUG("end monitor thread");
305 
306 	monitor_thread_running = false;
307 	pa.stream = NULL;
308 
309 	_pa_open();
310 
311 	UNLOCK;
312 
313 	return 0;
314 }
315 
316 void _pa_open(void) {
317 	PaStreamParameters outputParameters;
318 	PaError err = paNoError;
319 	int device_id;
320 #if WIN
321 	PaWasapiStreamInfo wasapiInfo;
322 	const PaDeviceInfo * paDeviceInfo;
323 	const PaHostApiInfo *paHostApiInfo;
324 
325 #endif
326 	if (pa.stream) {
327 		if ((err = Pa_CloseStream(pa.stream)) != paNoError) {
328 			LOG_WARN("error closing stream: %s", Pa_GetErrorText(err));
329 		}
330 	}
331 
332 	if (output.state == OUTPUT_OFF) {
333 		// we get called when transitioning to OUTPUT_OFF to create the probe thread
334 		// set err to avoid opening device and logging messages
335 		err = 1;
336 
337 	} else if ((device_id = pa_device_id(output.device)) == -1) {
338 		LOG_INFO("device %s not found", output.device);
339 		err = 1;
340 
341 	} else {
342 
343 		outputParameters.device = device_id;
344 		outputParameters.channelCount = 2;
345 		outputParameters.sampleFormat = paInt32;
346 #ifndef PA18API
347 		outputParameters.suggestedLatency =
348 			output.latency ? (double)output.latency/(double)1000 : Pa_GetDeviceInfo(outputParameters.device)->defaultHighOutputLatency;
349 		outputParameters.hostApiSpecificStreamInfo = NULL;
350 
351 #endif
352 #if OSX && !defined(OSXPPC)
353 		/* enable pro mode which aims to avoid resampling if possible */
354 		/* command line controls pa_hostapi_option which is -1 if not specified, 0 or 1 - choose playnice if -1 or 1 */
355 		PaMacCoreStreamInfo macInfo;
356 		unsigned long streamInfoFlags;
357 	 	if (output.pa_hostapi_option) {
358 			LOG_INFO("opening device in PlayNice mode");
359 			streamInfoFlags = paMacCorePlayNice;
360 		} else {
361 			LOG_INFO("opening device in Pro mode");
362 			streamInfoFlags = paMacCorePro;
363 		}
364 		PaMacCore_SetupStreamInfo(&macInfo, streamInfoFlags);
365 		outputParameters.hostApiSpecificStreamInfo = &macInfo;
366 #endif
367 #if WIN
368 		paDeviceInfo = Pa_GetDeviceInfo( outputParameters.device );
369 		paHostApiInfo = Pa_GetHostApiInfo ( paDeviceInfo->hostApi );
370 
371 		if ( paHostApiInfo != NULL )
372 		{
373 			if ( paHostApiInfo->type == paWASAPI )
374 			{
375 				/* Use exclusive mode for WasApi device, default is shared */
376 				if (output.pa_hostapi_option == 1)
377 				{
378 					wasapiInfo.size = sizeof(PaWasapiStreamInfo);
379 					wasapiInfo.hostApiType = paWASAPI;
380 					wasapiInfo.version = 1;
381 					wasapiInfo.flags = paWinWasapiExclusive;
382 					outputParameters.hostApiSpecificStreamInfo = &wasapiInfo;
383 					LOG_INFO("opening WASAPI device in exclusive mode");
384 				}
385 			}
386 		}
387 #endif
388 	}
389 
390 	if (!err &&
391 #ifndef PA18API
392 		(err = Pa_OpenStream(&pa.stream, NULL, &outputParameters, (double)output.current_sample_rate, paFramesPerBufferUnspecified,
393 							 paPrimeOutputBuffersUsingStreamCallback | paDitherOff, pa_callback, NULL)) != paNoError) {
394 		LOG_WARN("error opening device %i - %s [%s] : %s", outputParameters.device, Pa_GetDeviceInfo(outputParameters.device)->name,
395 				  Pa_GetHostApiInfo(Pa_GetDeviceInfo(outputParameters.device)->hostApi)->name, Pa_GetErrorText(err));
396 #else
397 		(err = Pa_OpenStream(&pa.stream, paNoDevice, 0, 0, NULL, outputParameters.device, outputParameters.channelCount,
398 							outputParameters.sampleFormat, NULL, (double)output.current_sample_rate, paFramesPerBuffer,
399 							paNumberOfBuffers, paDitherOff, pa_callback, NULL)) != paNoError) {
400 		LOG_WARN("error opening device %i - %s : %s", outputParameters.device, Pa_GetDeviceInfo(outputParameters.device)->name,
401 				 Pa_GetErrorText(err));
402 #endif
403 	}
404 
405 	if (!err) {
406 #ifndef PA18API
407 		LOG_INFO("opened device %i - %s [%s] at %u latency %u ms", outputParameters.device, Pa_GetDeviceInfo(outputParameters.device)->name,
408 				 Pa_GetHostApiInfo(Pa_GetDeviceInfo(outputParameters.device)->hostApi)->name,
409 				 (unsigned int)Pa_GetStreamInfo(pa.stream)->sampleRate, (unsigned int)(Pa_GetStreamInfo(pa.stream)->outputLatency * 1000));
410 #else
411 		LOG_INFO("opened device %i - %s at %u fpb %u nbf %u", outputParameters.device, Pa_GetDeviceInfo(outputParameters.device)->name,
412 				 (unsigned int)output.current_sample_rate, paFramesPerBuffer, paNumberOfBuffers);
413 
414 #endif
415 		pa.rate = output.current_sample_rate;
416 
417 #ifndef PA18API
418 		if ((err = Pa_SetStreamFinishedCallback(pa.stream, pa_stream_finished)) != paNoError) {
419 			LOG_WARN("error setting finish callback: %s", Pa_GetErrorText(err));
420 		}
421 
422 		UNLOCK; // StartStream can call pa_callback in a sychronised thread on freebsd, remove lock while it is called
423 
424 #endif
425 		if ((err = Pa_StartStream(pa.stream)) != paNoError) {
426 			LOG_WARN("error starting stream: %s", Pa_GetErrorText(err));
427 		}
428 
429 #ifndef PA18API
430 		LOCK;
431 #endif
432 	}
433 
434 	if (err && !monitor_thread_running) {
435 		vis_stop();
436 
437 		// create a thread to check for output state change or device return
438 #if LINUX || OSX || FREEBSD
439 		pthread_create(&monitor_thread, NULL, pa_monitor, NULL);
440 #endif
441 #if WIN
442 		monitor_thread = CreateThread(NULL, OUTPUT_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&pa_monitor, NULL, 0, NULL);
443 #endif
444 	}
445 
446 	output.error_opening = !!err;
447 }
448 
449 static u8_t *optr;
450 
451 static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
452 						 s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) {
453 
454 	if (!silence) {
455 
456 		if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) {
457 			_apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr);
458 		}
459 
460 		if (gainL != FIXED_ONE || gainR!= FIXED_ONE) {
461 			_apply_gain(outputbuf, out_frames, gainL, gainR);
462 		}
463 
464 		IF_DSD(
465 			if (output.outfmt == DOP) {
466 				update_dop((u32_t *) outputbuf->readp, out_frames, output.invert);
467 			} else if (output.outfmt != PCM && output.invert)
468 				dsd_invert((u32_t *) outputbuf->readp, out_frames);
469 		)
470 
471 		memcpy(optr, outputbuf->readp, out_frames * BYTES_PER_FRAME);
472 
473 	} else {
474 
475 		u8_t *buf = silencebuf;
476 
477 		IF_DSD(
478 			if (output.outfmt != PCM) {
479 				buf = silencebuf_dsd;
480 				update_dop((u32_t *) buf, out_frames, false); // don't invert silence
481 			}
482 		)
483 
484 		memcpy(optr, buf, out_frames * BYTES_PER_FRAME);
485 	}
486 
487 	optr += out_frames * BYTES_PER_FRAME;
488 
489 	return (int)out_frames;
490 }
491 
492 #ifndef PA18API
493 static int pa_callback(const void *pa_input, void *pa_output, unsigned long pa_frames_wanted,
494 					   const PaStreamCallbackTimeInfo *time_info, PaStreamCallbackFlags statusFlags, void *userData) {
495 #else
496 static int pa_callback(void *pa_input, void *pa_output, unsigned long pa_frames_wanted,PaTimestamp outTime, void *userData) {
497 #endif
498 	int ret;
499 	frames_t frames;
500 
501 	optr = (u8_t *)pa_output;
502 
503 	LOCK;
504 
505 #ifndef PA18API
506 	if (time_info->outputBufferDacTime > time_info->currentTime) {
507 		// workaround for wdm-ks which can return outputBufferDacTime with a different epoch
508 		output.device_frames = (unsigned)((time_info->outputBufferDacTime - time_info->currentTime) * output.current_sample_rate);
509 	} else {
510 		output.device_frames = 0;
511 	}
512 
513 #else
514 	output.device_frames = 0;
515 #endif
516 	output.updated = gettime_ms();
517 	output.frames_played_dmp = output.frames_played;
518 
519 	do {
520 		frames = _output_frames(pa_frames_wanted);
521 		pa_frames_wanted -= frames;
522 	} while (pa_frames_wanted > 0 && frames != 0);
523 
524 	if (pa_frames_wanted > 0) {
525 		LOG_DEBUG("pad with silence");
526 		memset(optr, 0, pa_frames_wanted * BYTES_PER_FRAME);
527 	}
528 
529 	if (output.state == OUTPUT_OFF) {
530 		LOG_INFO("output off");
531 		ret = paComplete;
532 	} else if (pa.rate != output.current_sample_rate) {
533 		ret = paComplete;
534 	} else {
535 		ret = paContinue;
536 	}
537 
538 	UNLOCK;
539 
540 #ifdef PA18API
541 	if ( ret == paComplete )
542 		pa_stream_finished (userData);
543 #endif
544 	return ret;
545 }
546 
547 void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay,
548 					unsigned idle) {
549 	PaError err;
550 #ifndef PA18API
551 	unsigned latency = 0;
552 	int pa_hostapi_option = -1;
553 
554 #else
555 	unsigned pa_frames = 0;
556 	unsigned pa_nbufs = 0;
557 #endif /* PA18API */
558 #ifndef PA18API
559 	char *l = next_param(params, ':');
560 	char *p = next_param(NULL, ':');
561 
562 	if (l) latency = (unsigned)atoi(l);
563 	if (p) pa_hostapi_option = atoi(p);
564 #else
565 	char *t = next_param(params, ':');
566 	char *c = next_param(NULL, ':');
567 	if (t) pa_frames  = atoi(t);
568 	if (c) pa_nbufs = atoi(c);
569 #endif
570 
571 	loglevel = level;
572 
573 	LOG_INFO("init output");
574 
575 	memset(&output, 0, sizeof(output));
576 
577 #ifndef PA18API
578 	output.latency = latency;
579 	output.pa_hostapi_option = pa_hostapi_option;
580 #else
581 	if ( pa_frames != 0 )
582 		paFramesPerBuffer = pa_frames;
583 	if ( pa_nbufs != 0 )
584 		paNumberOfBuffers = pa_nbufs;
585 #endif /* PA18API */
586 	output.format = 0;
587 	output.start_frames = 0;
588 	output.write_cb = &_write_frames;
589 	output.rate_delay = rate_delay;
590 	pa.stream = NULL;
591 
592 #ifndef PA18API
593 	LOG_INFO("requested latency: %u", output.latency);
594 #endif
595 
596 	if ((err = Pa_Initialize()) != paNoError) {
597 		LOG_WARN("error initialising port audio: %s", Pa_GetErrorText(err));
598 		exit(0);
599 	}
600 
601 	output_init_common(level, device, output_buf_size, rates, idle);
602 
603 	LOCK;
604 
605 	_pa_open();
606 
607 	UNLOCK;
608 }
609 
610 void output_close_pa(void) {
611 	PaError err;
612 
613 	LOG_INFO("close output");
614 
615 	LOCK;
616 
617 	running = false;
618 	monitor_thread_running = false;
619 
620 	if (pa.stream) {
621 		if ((err = Pa_AbortStream(pa.stream)) != paNoError) {
622 			LOG_WARN("error closing stream: %s", Pa_GetErrorText(err));
623 		}
624 	}
625 
626 	if ((err = Pa_Terminate()) != paNoError) {
627 		LOG_WARN("error closing port audio: %s", Pa_GetErrorText(err));
628 	}
629 
630 	UNLOCK;
631 
632 	output_close_common();
633 }
634 
635 #endif // PORTAUDIO
636