1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    Sound Channel Process Functions - PulseAudio
4    Copyright (C) Krupen'ko Nikita <krnekit@gmail.com> 2010
5    Copyright (C) Henrik Andersson <hean01@cendio.com> 2017
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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21 
22 #include "rdesktop.h"
23 #include "rdpsnd.h"
24 #include "rdpsnd_dsp.h"
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <sys/time.h>
30 
31 #include <pulse/version.h>
32 
33 #ifndef PA_CHECK_VERSION
34 #define PA_CHECK_VERSION(major, minor, micro)	(0)
35 #endif
36 
37 #include <pulse/thread-mainloop.h>
38 #if PA_CHECK_VERSION(0,9,11)
39 #include <pulse/proplist.h>
40 #endif
41 #include <pulse/context.h>
42 #include <pulse/sample.h>
43 #include <pulse/stream.h>
44 #include <pulse/error.h>
45 
46 #define DEFAULTDEVICE	NULL
47 
48 /* Messages that may be sent from the PulseAudio thread */
49 enum RDPSND_PULSE_MSG_TYPE
50 {
51 	RDPSND_PULSE_OUT_AVAIL,	// Output space available in output stream
52 	RDPSND_PULSE_IN_AVAIL,	// Input data available in input stream
53 	RDPSND_PULSE_OUT_ERR,	// An error occured in the output stream
54 	RDPSND_PULSE_IN_ERR	// An error occured in the input strem
55 };
56 
57 static pa_threaded_mainloop *mainloop;
58 #if PA_CHECK_VERSION(0,9,11)
59 static pa_proplist *proplist;
60 #endif
61 static pa_context *context;
62 static pa_stream *playback_stream;
63 static pa_stream *capture_stream;
64 static int pulse_ctl[2] = { -1, -1 };	// Pipe for comminicating with main thread
65 
66 /* Streams states for the possibility of the proper reinitialization */
67 static RD_BOOL playback_started = False;
68 static RD_BOOL capture_started = False;
69 
70 /* Device's parameters */
71 static const char *device;
72 static int playback_channels;
73 static int playback_samplerate;
74 static int playback_samplewidth;
75 static int capture_channels;
76 static int capture_samplerate;
77 static int capture_samplewidth;
78 
79 /* Internal audio buffer sizes (latency) */
80 static const int playback_latency_part = 10;	// Playback latency (in part of second)
81 static const int capture_latency_part = 10;	// Capture latency (in part of second)
82 
83 /* Capture buffer */
84 static void *capture_buf = NULL;
85 static size_t capture_buf_size = 0;
86 
87 static RD_BOOL pulse_init(void);
88 static void pulse_deinit(void);
89 static RD_BOOL pulse_context_init(void);
90 static void pulse_context_deinit(void);
91 static RD_BOOL pulse_stream_open(pa_stream ** stream, int channels, int samplerate, int samplewidth,
92 				 pa_stream_flags_t flags);
93 static void pulse_stream_close(pa_stream ** stream);
94 
95 static void pulse_send_msg(int fd, char message);
96 
97 static RD_BOOL pulse_playback_start(void);
98 static RD_BOOL pulse_playback_stop(void);
99 static RD_BOOL pulse_playback_set_audio(int channels, int samplerate, int samplewidth);
100 static RD_BOOL pulse_play(void);
101 
102 static RD_BOOL pulse_capture_start(void);
103 static RD_BOOL pulse_capture_stop(void);
104 static RD_BOOL pulse_capture_set_audio(int channels, int samplerate, int samplewidth);
105 static RD_BOOL pulse_record(void);
106 
107 static RD_BOOL pulse_recover(pa_stream ** stream);
108 
109 /* Callbacks for the PulseAudio events */
110 static void pulse_context_state_cb(pa_context * c, void *userdata);
111 static void pulse_stream_state_cb(pa_stream * p, void *userdata);
112 static void pulse_write_cb(pa_stream *, size_t nbytes, void *userdata);
113 static void pulse_read_cb(pa_stream * p, size_t nbytes, void *userdata);
114 
115 static void pulse_cork_cb(pa_stream * p, int success, void *userdata);
116 static void pulse_flush_cb(pa_stream * p, int success, void *userdata);
117 static void pulse_update_timing_cb(pa_stream * p, int success, void *userdata);
118 
119 static RD_BOOL
pulse_init(void)120 pulse_init(void)
121 {
122 	RD_BOOL ret = False;
123 
124 
125 	do
126 	{
127 		/* PulsaAudio mainloop thread initialization */
128 		mainloop = pa_threaded_mainloop_new();
129 		if (mainloop == NULL)
130 		{
131 			logger(Sound, Error,
132 			       "pulse_init(), Error creating PulseAudio threaded mainloop");
133 			break;
134 		}
135 		if (pa_threaded_mainloop_start(mainloop) != 0)
136 		{
137 			logger(Sound, Error,
138 			       "pulse_init(), Error starting PulseAudio threaded mainloop");
139 			break;
140 		}
141 #if PA_CHECK_VERSION(0,9,11)
142 		/* PulseAudio proplist initialization */
143 		proplist = pa_proplist_new();
144 		if (proplist == NULL)
145 		{
146 			logger(Sound, Error, "pulse_init(), Error creating PulseAudio proplist");
147 			break;
148 		}
149 		if (pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "rdesktop") != 0)
150 		{
151 			logger(Sound, Error,
152 			       "pulse_init(), Error setting option to PulseAudio proplist");
153 			break;
154 		}
155 #endif
156 
157 		if (pulse_context_init() != True)
158 			break;
159 
160 		ret = True;
161 	}
162 	while (0);
163 
164 	if (ret != True)
165 		pulse_deinit();
166 
167 	return ret;
168 }
169 
170 static void
pulse_deinit(void)171 pulse_deinit(void)
172 {
173 	pulse_stream_close(&capture_stream);
174 	pulse_stream_close(&playback_stream);
175 	pulse_context_deinit();
176 #if PA_CHECK_VERSION(0,9,11)
177 	if (proplist != NULL)
178 	{
179 		pa_proplist_free(proplist);
180 		proplist = NULL;
181 	}
182 #endif
183 	if (mainloop != NULL)
184 	{
185 		pa_threaded_mainloop_stop(mainloop);
186 		pa_threaded_mainloop_free(mainloop);
187 		mainloop = NULL;
188 	}
189 }
190 
191 static RD_BOOL
pulse_context_init(void)192 pulse_context_init(void)
193 {
194 	pa_context_flags_t flags;
195 	pa_context_state_t context_state;
196 	int err;
197 	RD_BOOL ret = False;
198 
199 
200 	pa_threaded_mainloop_lock(mainloop);
201 
202 	do
203 	{
204 		/* Pipe for the control information from the audio thread */
205 		if (pipe(pulse_ctl) != 0)
206 		{
207 			logger(Sound, Error, "pulse_context_init(), pipe: %s", strerror(errno));
208 			pulse_ctl[0] = pulse_ctl[1] = -1;
209 			break;
210 		}
211 		if (fcntl(pulse_ctl[0], F_SETFL, O_NONBLOCK) == -1)
212 		{
213 			logger(Sound, Error, "pulse_context_init(), fcntl: %s", strerror(errno));
214 			break;
215 		}
216 #if PA_CHECK_VERSION(0,9,11)
217 		context =
218 			pa_context_new_with_proplist(pa_threaded_mainloop_get_api(mainloop), NULL,
219 						     proplist);
220 #else
221 		context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "rdesktop");
222 #endif
223 		if (context == NULL)
224 		{
225 			logger(Sound, Error,
226 			       "pulse_context_init(), error creating PulseAudio context");
227 			break;
228 		}
229 		pa_context_set_state_callback(context, pulse_context_state_cb, mainloop);
230 		/* PulseAudio context connection */
231 #if PA_CHECK_VERSION(0,9,15)
232 		flags = PA_CONTEXT_NOFAIL;
233 #else
234 		flags = 0;
235 #endif
236 		if (pa_context_connect(context, NULL, flags, NULL) != 0)
237 		{
238 			err = pa_context_errno(context);
239 			logger(Sound, Error, "pulse_context_init(), %s", pa_strerror(err));
240 			break;
241 		}
242 		do
243 		{
244 			context_state = pa_context_get_state(context);
245 			if (context_state == PA_CONTEXT_READY || context_state == PA_CONTEXT_FAILED)
246 				break;
247 			else
248 				pa_threaded_mainloop_wait(mainloop);
249 		}
250 		while (1);
251 		if (context_state != PA_CONTEXT_READY)
252 		{
253 			err = pa_context_errno(context);
254 			logger(Sound, Error, "pulse_context_init(), %s", pa_strerror(err));
255 			break;
256 		}
257 
258 		ret = True;
259 	}
260 	while (0);
261 
262 	pa_threaded_mainloop_unlock(mainloop);
263 
264 	return ret;
265 }
266 
267 static void
pulse_context_deinit(void)268 pulse_context_deinit(void)
269 {
270 	int err;
271 
272 
273 	if (context != NULL)
274 	{
275 		pa_threaded_mainloop_lock(mainloop);
276 
277 		pa_context_disconnect(context);
278 		pa_context_unref(context);
279 		context = NULL;
280 
281 		pa_threaded_mainloop_unlock(mainloop);
282 	}
283 
284 	if (pulse_ctl[0] != -1)
285 	{
286 		do
287 			err = close(pulse_ctl[0]);
288 		while (err == -1 && errno == EINTR);
289 		if (err == -1)
290 			logger(Sound, Error, "pulse_context_deinit(), close: %s", strerror(errno));
291 		pulse_ctl[0] = -1;
292 	}
293 	if (pulse_ctl[1] != -1)
294 	{
295 		do
296 			err = close(pulse_ctl[1]);
297 		while (err == -1 && errno == EINTR);
298 		if (err == -1)
299 			logger(Sound, Error, "pulse_context_deinit(), close: %s", strerror(errno));
300 		pulse_ctl[1] = 0;
301 	}
302 }
303 
304 static RD_BOOL
pulse_stream_open(pa_stream ** stream,int channels,int samplerate,int samplewidth,pa_stream_flags_t flags)305 pulse_stream_open(pa_stream ** stream, int channels, int samplerate, int samplewidth,
306 		  pa_stream_flags_t flags)
307 {
308 	pa_sample_spec samples;
309 	pa_buffer_attr buffer_attr;
310 	pa_stream_state_t state;
311 	int err;
312 	RD_BOOL ret = False;
313 
314 
315 	assert(stream != NULL);
316 	assert(stream == &playback_stream || stream == &capture_stream);
317 
318 	logger(Sound, Debug, "pulse_stream_open(), channels=%d, samplerate=%d, samplewidth=%d",
319 	       channels, samplerate, samplewidth);
320 
321 	pa_threaded_mainloop_lock(mainloop);
322 
323 	do
324 	{
325 		/* PulseAudio sample format initialization */
326 #if PA_CHECK_VERSION(0,9,13)
327 		if (pa_sample_spec_init(&samples) == NULL)
328 		{
329 			logger(Sound, Error,
330 			       "pulse_stream_open(), error initializing PulseAudio sample format");
331 			break;
332 		}
333 #endif
334 		if (samplewidth == 2)
335 			samples.format = PA_SAMPLE_S16LE;
336 		else if (samplewidth == 1)
337 			samples.format = PA_SAMPLE_U8;
338 		else
339 		{
340 			logger(Sound, Error,
341 			       "pulse_stream_open(), wrong samplewidth for the PulseAudio stream: %d",
342 			       samplewidth);
343 			break;
344 		}
345 		samples.rate = samplerate;
346 		samples.channels = channels;
347 		if (!pa_sample_spec_valid(&samples))
348 		{
349 			logger(Sound, Error,
350 			       "pulse_stream_open(), Invalid PulseAudio sample format");
351 			break;
352 		}
353 		/* PulseAudio stream creation */
354 #if PA_CHECK_VERSION(0,9,11)
355 		if (stream == &playback_stream)
356 			*stream =
357 				pa_stream_new_with_proplist(context, "Playback Stream", &samples,
358 							    NULL, proplist);
359 		else
360 			*stream =
361 				pa_stream_new_with_proplist(context, "Capture Stream", &samples,
362 							    NULL, proplist);
363 #else
364 		if (stream == &playback_stream)
365 			*stream = pa_stream_new(context, "Playback Stream", &samples, NULL);
366 		else
367 			*stream = pa_stream_new(context, "Capture Stream", &samples, NULL);
368 #endif
369 		if (*stream == NULL)
370 		{
371 			err = pa_context_errno(context);
372 			logger(Sound, Error, "pulse_stream_open(), pa_stream_new: %s",
373 			       pa_strerror(err));
374 			break;
375 		}
376 		pa_stream_set_state_callback(*stream, pulse_stream_state_cb, mainloop);
377 
378 		buffer_attr.maxlength = (uint32_t) - 1;
379 		buffer_attr.minreq = (uint32_t) - 1;
380 		buffer_attr.prebuf = (uint32_t) - 1;
381 		buffer_attr.tlength = (uint32_t) - 1;
382 		buffer_attr.fragsize = (uint32_t) - 1;
383 
384 		/* PulseAudio stream connection */
385 		if (stream == &playback_stream)
386 		{
387 #if PA_CHECK_VERSION(0,9,0)
388 			buffer_attr.tlength =
389 				pa_usec_to_bytes(1000000 / playback_latency_part, &samples);
390 #else
391 			buffer_attr.tlength =
392 				(samples.rate / playback_latency_part) * samples.channels *
393 				(samples.format == PA_SAMPLE_S16LE ? 2 : 1);
394 #endif
395 			buffer_attr.prebuf = 0;
396 			buffer_attr.maxlength = buffer_attr.tlength;
397 		}
398 		else
399 		{
400 #if PA_CHECK_VERSION(0,9,0)
401 			buffer_attr.fragsize =
402 				pa_usec_to_bytes(1000000 / capture_latency_part, &samples);
403 #else
404 			buffer_attr.fragsize =
405 				(samples.rate / capture_latency_part) * samples.channels *
406 				(samples.format == PA_SAMPLE_S16LE ? 2 : 1);
407 #endif
408 			buffer_attr.maxlength = buffer_attr.fragsize;
409 		}
410 
411 #if !PA_CHECK_VERSION(0,9,16)
412 		buffer_attr.minreq = (samples.rate / 50) * samples.channels * (samples.format == PA_SAMPLE_S16LE ? 2 : 1);	// 20 ms
413 #endif
414 
415 		if (stream == &playback_stream)
416 			err = pa_stream_connect_playback(*stream, device, &buffer_attr, flags, NULL,
417 							 NULL);
418 		else
419 			err = pa_stream_connect_record(*stream, device, &buffer_attr, flags);
420 		if (err)
421 		{
422 			err = pa_context_errno(context);
423 			logger(Sound, Error,
424 			       "pulse_stream_open(), error connecting PulseAudio stream: %s",
425 			       pa_strerror(err));
426 			break;
427 		}
428 		do
429 		{
430 			state = pa_stream_get_state(*stream);
431 			if (state == PA_STREAM_READY || state == PA_STREAM_FAILED)
432 				break;
433 			else
434 				pa_threaded_mainloop_wait(mainloop);
435 		}
436 		while (1);
437 		if (state != PA_STREAM_READY)
438 		{
439 			err = pa_context_errno(context);
440 			logger(Sound, Error,
441 			       "pulse_stream_open(), error connecting PulseAudio stream: %s",
442 			       pa_strerror(err));
443 			break;
444 		}
445 
446 #if PA_CHECK_VERSION(0,9,8)
447 		logger(Sound, Debug, "pulse_stream_open(), opened PulseAudio stream on device %s",
448 		       pa_stream_get_device_name(*stream));
449 #endif
450 #if PA_CHECK_VERSION(0,9,0)
451 		const pa_buffer_attr *res_ba;
452 		res_ba = pa_stream_get_buffer_attr(*stream);
453 		logger(Sound, Debug,
454 		       "pulse_stream_open(), PulseAudio stream buffer metrics: maxlength %u, minreq %u, prebuf %u, tlength %u, fragsize %u",
455 		       res_ba->maxlength, res_ba->minreq, res_ba->prebuf, res_ba->tlength,
456 		       res_ba->fragsize);
457 #endif
458 
459 		/* Set the data callbacks for the PulseAudio stream */
460 		if (stream == &playback_stream)
461 			pa_stream_set_write_callback(*stream, pulse_write_cb, mainloop);
462 		else
463 			pa_stream_set_read_callback(*stream, pulse_read_cb, mainloop);
464 
465 		ret = True;
466 	}
467 	while (0);
468 
469 	pa_threaded_mainloop_unlock(mainloop);
470 
471 	return ret;
472 }
473 
474 static void
pulse_stream_close(pa_stream ** stream)475 pulse_stream_close(pa_stream ** stream)
476 {
477 	pa_stream_state_t state;
478 	int err;
479 
480 
481 	assert(stream != NULL);
482 
483 	if (*stream != NULL)
484 	{
485 		pa_threaded_mainloop_lock(mainloop);
486 
487 		state = pa_stream_get_state(*stream);
488 		if (state == PA_STREAM_READY)
489 		{
490 			if (pa_stream_disconnect(*stream) != 0)
491 			{
492 				err = pa_context_errno(context);
493 				logger(Sound, Error,
494 				       "pulse_stream_close(), pa_stream_disconnect: %s\n",
495 				       pa_strerror(err));
496 			}
497 		}
498 		pa_stream_unref(*stream);
499 		*stream = NULL;
500 
501 		pa_threaded_mainloop_unlock(mainloop);
502 	}
503 }
504 
505 static void
pulse_send_msg(int fd,char message)506 pulse_send_msg(int fd, char message)
507 {
508 	int ret;
509 
510 
511 	do
512 		ret = write(fd, &message, sizeof message);
513 	while (ret == -1 && errno == EINTR);
514 	if (ret == -1)
515 		logger(Sound, Error, "pulse_send_msg(), error writing message to the pipe: %s\n",
516 		       strerror(errno));
517 }
518 
519 static RD_BOOL
pulse_playback_start(void)520 pulse_playback_start(void)
521 {
522 	RD_BOOL result = False;
523 	int ret;
524 	int err;
525 	pa_operation *po;
526 
527 
528 	if (playback_stream == NULL)
529 	{
530 		logger(Sound, Warning,
531 		       "pulse_playback_start(), trying to start PulseAudio stream while it's not exists");
532 		return True;
533 	}
534 
535 	pa_threaded_mainloop_lock(mainloop);
536 
537 	do
538 	{
539 		if (pa_stream_get_state(playback_stream) != PA_STREAM_READY)
540 		{
541 			logger(Sound, Warning,
542 			       "pulse_playback_start(), trying to start PulseAudio stream while it's not ready");
543 			break;
544 		}
545 #if PA_CHECK_VERSION(0,9,11)
546 		ret = pa_stream_is_corked(playback_stream);
547 #else
548 		ret = 1;
549 #endif
550 		if (ret < 0)
551 		{
552 			err = pa_context_errno(context);
553 			logger(Sound, Error, "pulse_playback_start(), pa_stream_is_corked: %s",
554 			       pa_strerror(err));
555 			break;
556 		}
557 		else if (ret != 0)
558 		{
559 			po = pa_stream_cork(playback_stream, 0, pulse_cork_cb, mainloop);
560 			if (po == NULL)
561 			{
562 				err = pa_context_errno(context);
563 				logger(Sound, Error, "pulse_playback_start(), pa_stream_corked: %s",
564 				       pa_strerror(err));
565 				break;
566 			}
567 			while (pa_operation_get_state(po) == PA_OPERATION_RUNNING)
568 				pa_threaded_mainloop_wait(mainloop);
569 			pa_operation_unref(po);
570 		}
571 
572 		result = True;
573 	}
574 	while (0);
575 
576 	pa_threaded_mainloop_unlock(mainloop);
577 
578 	return result;
579 }
580 
581 static RD_BOOL
pulse_playback_stop(void)582 pulse_playback_stop(void)
583 {
584 	RD_BOOL result = False;
585 	int ret;
586 	int err;
587 	pa_operation *po;
588 
589 
590 	if (playback_stream == NULL)
591 	{
592 		logger(Sound, Debug,
593 		       "pulse_playback_stop(), trying to stop PulseAudio stream while it's not exists");
594 		return True;
595 	}
596 
597 	pa_threaded_mainloop_lock(mainloop);
598 
599 	do
600 	{
601 		if (pa_stream_get_state(playback_stream) != PA_STREAM_READY)
602 		{
603 			logger(Sound, Error,
604 			       "pulse_playback_stop(), trying to stop PulseAudio stream while it's not ready");
605 			break;
606 		}
607 #if PA_CHECK_VERSION(0,9,11)
608 		ret = pa_stream_is_corked(playback_stream);
609 #else
610 		ret = 0;
611 #endif
612 		if (ret < 0)
613 		{
614 			err = pa_context_errno(context);
615 			logger(Sound, Error, "pulse_playback_stop(), pa_stream_is_corked: %s",
616 			       pa_strerror(err));
617 			break;
618 		}
619 		else if (ret == 0)
620 		{
621 			po = pa_stream_cork(playback_stream, 1, pulse_cork_cb, mainloop);
622 			if (po == NULL)
623 			{
624 				err = pa_context_errno(context);
625 				logger(Sound, Error, "pulse_playback_stop(), pa_stream_cork: %s",
626 				       pa_strerror(err));
627 				break;
628 			}
629 			while (pa_operation_get_state(po) == PA_OPERATION_RUNNING)
630 				pa_threaded_mainloop_wait(mainloop);
631 			pa_operation_unref(po);
632 		}
633 		po = pa_stream_flush(playback_stream, pulse_flush_cb, mainloop);
634 		if (po == NULL)
635 		{
636 			err = pa_context_errno(context);
637 			logger(Sound, Error, "pulse_playback_stop(), pa_stream_flush: %s",
638 			       pa_strerror(err));
639 			break;
640 		}
641 		while (pa_operation_get_state(po) == PA_OPERATION_RUNNING)
642 			pa_threaded_mainloop_wait(mainloop);
643 		pa_operation_unref(po);
644 
645 		result = True;
646 	}
647 	while (0);
648 
649 	pa_threaded_mainloop_unlock(mainloop);
650 
651 	return result;
652 }
653 
654 static RD_BOOL
pulse_playback_set_audio(int channels,int samplerate,int samplewidth)655 pulse_playback_set_audio(int channels, int samplerate, int samplewidth)
656 {
657 	pa_stream_flags_t flags;
658 
659 
660 	pulse_stream_close(&playback_stream);
661 
662 	flags = PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
663 		PA_STREAM_AUTO_TIMING_UPDATE;
664 #if PA_CHECK_VERSION(0,9,11)
665 	flags |= PA_STREAM_ADJUST_LATENCY;
666 #endif
667 	if (pulse_stream_open(&playback_stream, channels, samplerate, samplewidth, flags) != True)
668 		return False;
669 
670 	return True;
671 }
672 
673 static RD_BOOL
pulse_capture_start(void)674 pulse_capture_start(void)
675 {
676 	RD_BOOL result = False;
677 	int ret;
678 	int err;
679 	pa_operation *po;
680 
681 
682 	if (capture_stream == NULL)
683 	{
684 		logger(Sound, Warning,
685 		       "pulse_capture_start(), trying to start PulseAudio stream while it's not exists");
686 		return True;
687 	}
688 
689 	pa_threaded_mainloop_lock(mainloop);
690 
691 	do
692 	{
693 		if (pa_stream_get_state(capture_stream) != PA_STREAM_READY)
694 		{
695 			logger(Sound, Error,
696 			       "pulse_capture_start(), trying to start PulseAudio stream while it's not exists");
697 			break;
698 		}
699 #if PA_CHECK_VERSION(0,9,11)
700 		ret = pa_stream_is_corked(capture_stream);
701 #else
702 		ret = 1;
703 #endif
704 		if (ret < 0)
705 		{
706 			err = pa_context_errno(context);
707 			logger(Sound, Error, "pulse_capture_start(), pa_stream_is_corked: %s",
708 			       pa_strerror(err));
709 			break;
710 		}
711 		else if (ret != 0)
712 		{
713 			po = pa_stream_cork(capture_stream, 0, pulse_cork_cb, mainloop);
714 			if (po == NULL)
715 			{
716 				err = pa_context_errno(context);
717 				logger(Sound, Error, "pulse_capture_start(), pa_stream_cork: %s\n",
718 				       pa_strerror(err));
719 				break;
720 			}
721 			while (pa_operation_get_state(po) == PA_OPERATION_RUNNING)
722 				pa_threaded_mainloop_wait(mainloop);
723 			pa_operation_unref(po);
724 		}
725 
726 		result = True;
727 	}
728 	while (0);
729 
730 	pa_threaded_mainloop_unlock(mainloop);
731 
732 	return result;
733 }
734 
735 static RD_BOOL
pulse_capture_stop(void)736 pulse_capture_stop(void)
737 {
738 	RD_BOOL result = False;
739 	int ret;
740 	int err;
741 	pa_operation *po;
742 
743 
744 	if (capture_stream == NULL)
745 	{
746 		logger(Sound, Debug,
747 		       "pulse_capture_stop(), trying to stop PulseAudio stream while it's not exists");
748 		return True;
749 	}
750 
751 	pa_threaded_mainloop_lock(mainloop);
752 
753 	do
754 	{
755 		if (pa_stream_get_state(capture_stream) != PA_STREAM_READY)
756 		{
757 			logger(Sound, Error,
758 			       "pulse_capture_stop(), trying to stop PulseAudio stream while it's not exists");
759 			break;
760 		}
761 #if PA_CHECK_VERSION(0,9,11)
762 		ret = pa_stream_is_corked(capture_stream);
763 #else
764 		ret = 0;
765 #endif
766 		if (ret < 0)
767 		{
768 			err = pa_context_errno(context);
769 			logger(Sound, Error, "pulse_capture_stop(), pa_stream_is_corked: %s\n",
770 			       pa_strerror(err));
771 			break;
772 		}
773 		else if (ret == 0)
774 		{
775 			po = pa_stream_cork(capture_stream, 1, pulse_cork_cb, mainloop);
776 			if (po == NULL)
777 			{
778 				err = pa_context_errno(context);
779 				logger(Sound, Error, "pulse_capture_stop(), pa_stream_cork: %s\n",
780 				       pa_strerror(err));
781 				break;
782 			}
783 			while (pa_operation_get_state(po) == PA_OPERATION_RUNNING)
784 				pa_threaded_mainloop_wait(mainloop);
785 			pa_operation_unref(po);
786 		}
787 
788 		result = True;
789 	}
790 	while (0);
791 
792 	pa_threaded_mainloop_unlock(mainloop);
793 
794 	return result;
795 }
796 
797 static RD_BOOL
pulse_capture_set_audio(int channels,int samplerate,int samplewidth)798 pulse_capture_set_audio(int channels, int samplerate, int samplewidth)
799 {
800 	pa_stream_flags_t flags;
801 	pa_stream_state_t state;
802 	int ret;
803 	int err;
804 
805 
806 	flags = PA_STREAM_START_CORKED;
807 #if PA_CHECK_VERSION(0,9,11)
808 	flags |= PA_STREAM_ADJUST_LATENCY;
809 #endif
810 
811 	if (capture_stream != NULL)
812 	{
813 		pa_threaded_mainloop_lock(mainloop);
814 		state = pa_stream_get_state(capture_stream);
815 		if (state == PA_STREAM_READY)
816 		{
817 #if PA_CHECK_VERSION(0,9,11)
818 			ret = pa_stream_is_corked(capture_stream);
819 #else
820 			ret = (capture_started == False);
821 #endif
822 			if (ret == 0)
823 				flags &= ~PA_STREAM_START_CORKED;
824 			else if (ret < 0)
825 			{
826 				err = pa_context_errno(context);
827 				pa_threaded_mainloop_unlock(mainloop);
828 				logger(Sound, Error,
829 				       "pulse_capture_set_audio(), pa_stream_is_corked: %s\n",
830 				       pa_strerror(err));
831 				return False;
832 			}
833 		}
834 		pa_threaded_mainloop_unlock(mainloop);
835 	}
836 
837 	pulse_stream_close(&capture_stream);
838 
839 	if (pulse_stream_open(&capture_stream, channels, samplerate, samplewidth, flags) != True)
840 		return False;
841 
842 	return True;
843 }
844 
845 static void
pulse_context_state_cb(pa_context * c,void * userdata)846 pulse_context_state_cb(pa_context * c, void *userdata)
847 {
848 	pa_context_state_t state;
849 
850 
851 	assert(userdata != NULL);
852 
853 	state = pa_context_get_state(c);
854 	if (state == PA_CONTEXT_READY || state == PA_CONTEXT_FAILED)
855 		pa_threaded_mainloop_signal((pa_threaded_mainloop *) userdata, 0);
856 }
857 
858 static void
pulse_stream_state_cb(pa_stream * p,void * userdata)859 pulse_stream_state_cb(pa_stream * p, void *userdata)
860 {
861 	pa_stream_state_t state;
862 
863 
864 	assert(userdata != NULL);
865 
866 	state = pa_stream_get_state(p);
867 	if (state == PA_STREAM_FAILED)
868 	{
869 		if (p == playback_stream)
870 		{
871 			logger(Sound, Debug,
872 			       "pulse_stream_state_cb(), PulseAudio playback stream is in a fail state");
873 			pulse_send_msg(pulse_ctl[1], RDPSND_PULSE_OUT_ERR);
874 		}
875 		else
876 		{
877 			logger(Sound, Debug,
878 			       "pulse_stream_state_cb(), PulseAudio capture stream is in a fail state");
879 			pulse_send_msg(pulse_ctl[1], RDPSND_PULSE_IN_ERR);
880 		}
881 	}
882 	if (state == PA_STREAM_READY || state == PA_STREAM_FAILED)
883 		pa_threaded_mainloop_signal((pa_threaded_mainloop *) userdata, 0);
884 }
885 
886 static void
pulse_read_cb(pa_stream * p,size_t nbytes,void * userdata)887 pulse_read_cb(pa_stream * p, size_t nbytes, void *userdata)
888 {
889 	assert(userdata != NULL);
890 
891 	pulse_send_msg(pulse_ctl[1], RDPSND_PULSE_IN_AVAIL);
892 }
893 
894 static void
pulse_write_cb(pa_stream * p,size_t nbytes,void * userdata)895 pulse_write_cb(pa_stream * p, size_t nbytes, void *userdata)
896 {
897 	assert(userdata != NULL);
898 
899 	pulse_send_msg(pulse_ctl[1], RDPSND_PULSE_OUT_AVAIL);
900 }
901 
902 static void
pulse_cork_cb(pa_stream * p,int success,void * userdata)903 pulse_cork_cb(pa_stream * p, int success, void *userdata)
904 {
905 	assert(userdata != NULL);
906 
907 	if (!success)
908 	{
909 		if (p == playback_stream)
910 		{
911 			logger(Sound, Warning,
912 			       "pulse_cork_cb(), fail to cork/uncork the PulseAudio playback stream: %s",
913 			       pa_strerror(pa_context_errno(context)));
914 			pulse_send_msg(pulse_ctl[1], RDPSND_PULSE_OUT_ERR);
915 		}
916 		else
917 		{
918 			logger(Sound, Warning,
919 			       "pulse_cork_cb(), fail to cork/uncork the PulseAudio capture stream: %s",
920 			       pa_strerror(pa_context_errno(context)));
921 			pulse_send_msg(pulse_ctl[1], RDPSND_PULSE_IN_ERR);
922 		}
923 	}
924 
925 	pa_threaded_mainloop_signal((pa_threaded_mainloop *) userdata, 0);
926 }
927 
928 static void
pulse_flush_cb(pa_stream * p,int success,void * userdata)929 pulse_flush_cb(pa_stream * p, int success, void *userdata)
930 {
931 	assert(userdata != NULL);
932 
933 	if (!success)
934 	{
935 		logger(Sound, Warning, "pulse_flush_cb(), Fail to flush the PulseAudio stream: %s",
936 		       pa_strerror(pa_context_errno(context)));
937 		pulse_send_msg(pulse_ctl[1], RDPSND_PULSE_OUT_ERR);
938 	}
939 
940 	pa_threaded_mainloop_signal((pa_threaded_mainloop *) userdata, 0);
941 }
942 
943 static void
pulse_update_timing_cb(pa_stream * p,int success,void * userdata)944 pulse_update_timing_cb(pa_stream * p, int success, void *userdata)
945 {
946 	assert(userdata != NULL);
947 
948 	if (!success)
949 	{
950 		logger(Sound, Warning,
951 		       "pulse_update_timing_cb(), fail to update timing info of the PulseAudio stream: %s",
952 		       pa_strerror(pa_context_errno(context)));
953 		pulse_send_msg(pulse_ctl[1], RDPSND_PULSE_OUT_ERR);
954 	}
955 
956 	pa_threaded_mainloop_signal((pa_threaded_mainloop *) userdata, 0);
957 }
958 
959 void
pulse_add_fds(int * n,fd_set * rfds,fd_set * wfds,struct timeval * tv)960 pulse_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv)
961 {
962 	if (pulse_ctl[0] != -1)
963 	{
964 		if (pulse_ctl[0] > *n)
965 			*n = pulse_ctl[0];
966 
967 		FD_SET(pulse_ctl[0], rfds);
968 	}
969 }
970 
971 void
pulse_check_fds(fd_set * rfds,fd_set * wfds)972 pulse_check_fds(fd_set * rfds, fd_set * wfds)
973 {
974 	char audio_cmd;
975 	int n;
976 
977 
978 	if (pulse_ctl[0] == -1)
979 		return;
980 
981 	if (FD_ISSET(pulse_ctl[0], rfds))
982 	{
983 		do
984 		{
985 			n = read(pulse_ctl[0], &audio_cmd, sizeof audio_cmd);
986 			if (n == -1)
987 			{
988 				if (errno == EINTR)
989 					continue;
990 				else if (errno == EAGAIN || errno == EWOULDBLOCK)
991 					break;
992 				else
993 				{
994 					logger(Sound, Error, "pulse_check_fds(), read: %s\n",
995 					       strerror(errno));
996 					return;
997 				}
998 			}
999 			else if (n == 0)
1000 			{
1001 				logger(Sound, Warning,
1002 				       "pulse_check_fds(), audio control pipe was closed");
1003 				break;
1004 			}
1005 			else
1006 				switch (audio_cmd)
1007 				{
1008 					case RDPSND_PULSE_OUT_AVAIL:
1009 						if (pulse_play() != True)
1010 							if (pulse_recover(&playback_stream) != True)
1011 							{
1012 								logger(Sound, Error,
1013 								       "pulse_check_fds(), PulseAudio playback error");
1014 								return;
1015 							}
1016 						break;
1017 					case RDPSND_PULSE_IN_AVAIL:
1018 						if (pulse_record() != True)
1019 							if (pulse_recover(&capture_stream) != True)
1020 							{
1021 								logger(Sound, Error,
1022 								       "pulse_check_fds(), PulseAudio capture error");
1023 								return;
1024 							}
1025 						break;
1026 					case RDPSND_PULSE_OUT_ERR:
1027 						if (pulse_recover(&playback_stream) != True)
1028 						{
1029 							logger(Sound, Error,
1030 							       "pulse_check_fds(), an error occured in audio thread with PulseAudio playback stream");
1031 							return;
1032 						}
1033 						break;
1034 					case RDPSND_PULSE_IN_ERR:
1035 						if (pulse_recover(&capture_stream) != True)
1036 						{
1037 							logger(Sound, Error,
1038 							       "pulse_check_fds(), an error occured in audio thread with PulseAudio capture stream");
1039 							return;
1040 						}
1041 						break;
1042 					default:
1043 						logger(Sound, Error,
1044 						       "pulse_check_fds(), wrong command from the audio thread: %d",
1045 						       audio_cmd);
1046 						break;
1047 				}
1048 		}
1049 		while (1);
1050 	}
1051 
1052 	return;
1053 }
1054 
1055 RD_BOOL
pulse_open_out(void)1056 pulse_open_out(void)
1057 {
1058 	if (context == NULL || mainloop == NULL)
1059 		if (pulse_init() != True)
1060 			return False;
1061 
1062 	return True;
1063 }
1064 
1065 void
pulse_close_out(void)1066 pulse_close_out(void)
1067 {
1068 	/* Ack all remaining packets */
1069 	while (!rdpsnd_queue_empty())
1070 		rdpsnd_queue_next(0);
1071 
1072 	playback_started = False;
1073 
1074 	if (playback_stream && pulse_playback_stop() != True)
1075 		if (pulse_recover(&playback_stream) != True)
1076 		{
1077 			logger(Sound, Error,
1078 			       "pulse_close_out(), fail to close the PulseAudio playback stream");
1079 			return;
1080 		}
1081 }
1082 
1083 RD_BOOL
pulse_format_supported(RD_WAVEFORMATEX * pwfx)1084 pulse_format_supported(RD_WAVEFORMATEX * pwfx)
1085 {
1086 	if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
1087 		return False;
1088 	if ((pwfx->nChannels != 1) && (pwfx->nChannels != 2))
1089 		return False;
1090 	if ((pwfx->wBitsPerSample != 8) && (pwfx->wBitsPerSample != 16))
1091 		return False;
1092 
1093 	return True;
1094 }
1095 
1096 RD_BOOL
pulse_set_format_out(RD_WAVEFORMATEX * pwfx)1097 pulse_set_format_out(RD_WAVEFORMATEX * pwfx)
1098 {
1099 	if (playback_stream == NULL
1100 	    || playback_channels != pwfx->nChannels
1101 	    || playback_samplerate != pwfx->nSamplesPerSec
1102 	    || playback_samplewidth != pwfx->wBitsPerSample / 8)
1103 	{
1104 		playback_channels = pwfx->nChannels;
1105 		playback_samplerate = pwfx->nSamplesPerSec;
1106 		playback_samplewidth = pwfx->wBitsPerSample / 8;
1107 
1108 		if (pulse_playback_set_audio
1109 		    (pwfx->nChannels, pwfx->nSamplesPerSec, pwfx->wBitsPerSample / 8) != True)
1110 			if (pulse_recover(&playback_stream) != True)
1111 			{
1112 				logger(Sound, Error,
1113 				       "pulse_set_format_out(), fail to open the PulseAudio playback stream");
1114 				return False;
1115 			}
1116 	}
1117 
1118 	playback_started = True;
1119 
1120 	if (pulse_playback_start() != True)
1121 		if (pulse_recover(&playback_stream) != True)
1122 		{
1123 			logger(Sound, Error,
1124 			       "pulse_set_format_out(), fail to start the PulseAudio playback stream");
1125 			return False;
1126 		}
1127 
1128 	return True;
1129 }
1130 
1131 RD_BOOL
pulse_play(void)1132 pulse_play(void)
1133 {
1134 	struct audio_packet *packet;
1135 	STREAM out;
1136 	const pa_timing_info *ti;
1137 	pa_operation *po;
1138 	pa_seek_mode_t playback_seek;
1139 	size_t avail_space, audio_size;
1140 	pa_usec_t delay = 0;
1141 	int ret;
1142 	int err;
1143 	RD_BOOL result = False;
1144 
1145 
1146 	if (rdpsnd_queue_empty())
1147 		return True;
1148 
1149 	if (playback_stream == NULL)
1150 		return False;
1151 
1152 	pa_threaded_mainloop_lock(mainloop);
1153 
1154 	do
1155 	{
1156 		packet = rdpsnd_queue_current_packet();
1157 		out = packet->s;
1158 
1159 		ti = pa_stream_get_timing_info(playback_stream);
1160 		if (ti == NULL)
1161 		{
1162 			err = pa_context_errno(context);
1163 			logger(Sound, Error, "pulse_play(), pa_stream_get_timing_info: %s",
1164 			       pa_strerror(err));
1165 			break;
1166 		}
1167 
1168 		if (ti->read_index_corrupt || ti->write_index_corrupt)
1169 		{
1170 			po = pa_stream_update_timing_info(playback_stream, pulse_update_timing_cb,
1171 							  mainloop);
1172 			if (po == NULL)
1173 			{
1174 				err = pa_context_errno(context);
1175 				logger(Sound, Error,
1176 				       "pulse_play(), pa_stream_update_timing_info: %s",
1177 				       pa_strerror(err));
1178 				break;
1179 			}
1180 			while (pa_operation_get_state(po) == PA_OPERATION_RUNNING)
1181 				pa_threaded_mainloop_wait(mainloop);
1182 			pa_operation_unref(po);
1183 		}
1184 
1185 		if (ti->read_index > ti->write_index)
1186 		{
1187 			logger(Sound, Debug, "pulse_play(), PulseAudio stream underflow %ld bytes",
1188 			       (long) (ti->read_index - ti->write_index));
1189 			playback_seek = PA_SEEK_RELATIVE_ON_READ;
1190 		}
1191 		else
1192 			playback_seek = PA_SEEK_RELATIVE;
1193 
1194 		avail_space = pa_stream_writable_size(playback_stream);
1195 		audio_size = MIN(s_remaining(out), avail_space);
1196 		if (audio_size)
1197 		{
1198 			unsigned char *data;
1199 
1200 			in_uint8p(out, data, audio_size);
1201 			if (pa_stream_write
1202 			    (playback_stream, data, audio_size, NULL, 0, playback_seek) != 0)
1203 			{
1204 				err = pa_context_errno(context);
1205 				logger(Sound, Error, "pulse_play(), pa_stream_write: %s",
1206 				       pa_strerror(err));
1207 				break;
1208 			}
1209 			else if (playback_seek == PA_SEEK_RELATIVE_ON_READ)
1210 				playback_seek = PA_SEEK_RELATIVE;
1211 		}
1212 
1213 		if (s_check_end(out))
1214 		{
1215 			ret = pa_stream_get_latency(playback_stream, &delay, NULL);
1216 			if (ret != 0 && (err = pa_context_errno(context)) == PA_ERR_NODATA)
1217 			{
1218 				po = pa_stream_update_timing_info(playback_stream,
1219 								  pulse_update_timing_cb, mainloop);
1220 				if (po == NULL)
1221 				{
1222 					delay = 0;
1223 					err = pa_context_errno(context);
1224 					logger(Sound, Error,
1225 					       "pulse_play(), pa_stream_update_timing_info: %s",
1226 					       pa_strerror(err));
1227 					break;
1228 				}
1229 				while (pa_operation_get_state(po) == PA_OPERATION_RUNNING)
1230 					pa_threaded_mainloop_wait(mainloop);
1231 				pa_operation_unref(po);
1232 
1233 				ret = pa_stream_get_latency(playback_stream, &delay, NULL);
1234 			}
1235 			if (ret != 0)
1236 			{
1237 				delay = 0;
1238 				err = pa_context_errno(context);
1239 				logger(Sound, Error, "pulse_play(), pa_stream_get_latency: %s",
1240 				       pa_strerror(err));
1241 				break;
1242 			}
1243 
1244 			logger(Sound, Debug,
1245 			       "pulse_play(), PulseAudio playback stream latency %lu usec",
1246 			       (long) delay);
1247 		}
1248 
1249 		result = True;
1250 	}
1251 	while (0);
1252 
1253 	pa_threaded_mainloop_unlock(mainloop);
1254 
1255 	if (s_check_end(out))
1256 		rdpsnd_queue_next(delay);
1257 
1258 	return result;
1259 }
1260 
1261 RD_BOOL
pulse_open_in(void)1262 pulse_open_in(void)
1263 {
1264 	if (context == NULL || mainloop == NULL)
1265 		if (pulse_init() != True)
1266 			return False;
1267 
1268 	return True;
1269 }
1270 
1271 void
pulse_close_in(void)1272 pulse_close_in(void)
1273 {
1274 	capture_started = False;
1275 
1276 	if (capture_stream && pulse_capture_stop() != True)
1277 		if (pulse_recover(&capture_stream) != True)
1278 		{
1279 			logger(Sound, Error,
1280 			       "pulse_close_in(), fail to close the PulseAudio capture stream");
1281 			return;
1282 		}
1283 }
1284 
1285 RD_BOOL
pulse_set_format_in(RD_WAVEFORMATEX * pwfx)1286 pulse_set_format_in(RD_WAVEFORMATEX * pwfx)
1287 {
1288 	if (capture_stream == NULL
1289 	    || capture_channels != pwfx->nChannels
1290 	    || capture_samplerate != pwfx->nSamplesPerSec
1291 	    || capture_samplewidth != pwfx->wBitsPerSample / 8)
1292 	{
1293 		capture_channels = pwfx->nChannels;
1294 		capture_samplerate = pwfx->nSamplesPerSec;
1295 		capture_samplewidth = pwfx->wBitsPerSample / 8;
1296 
1297 		if (pulse_capture_set_audio
1298 		    (pwfx->nChannels, pwfx->nSamplesPerSec, pwfx->wBitsPerSample / 8) != True)
1299 			if (pulse_recover(&capture_stream) != True)
1300 			{
1301 				logger(Sound, Error,
1302 				       "pulse_set_format_in(), fail to open the PulseAudio capture stream");
1303 				return False;
1304 			}
1305 	}
1306 
1307 	capture_started = True;
1308 
1309 	if (pulse_capture_start() != True)
1310 		if (pulse_recover(&capture_stream) != True)
1311 		{
1312 			logger(Sound, Error,
1313 			       "pulse_set_format_in(), fail to start the PulseAudio capture stream");
1314 			return False;
1315 		}
1316 
1317 	return True;
1318 }
1319 
1320 RD_BOOL
pulse_record(void)1321 pulse_record(void)
1322 {
1323 	const void *pulse_buf;
1324 	size_t audio_size;
1325 	RD_BOOL result = False;
1326 
1327 
1328 	if (capture_stream == NULL)
1329 		return False;
1330 
1331 	pa_threaded_mainloop_lock(mainloop);
1332 
1333 	do
1334 	{
1335 		if (pa_stream_peek(capture_stream, &pulse_buf, &audio_size) != 0)
1336 		{
1337 			logger(Sound, Error, "pulse_record(), pa_stream_peek: %s",
1338 			       pa_strerror(pa_context_errno(context)));
1339 			break;
1340 		}
1341 
1342 		/* Stretch the buffer, if needed */
1343 		if (capture_buf_size < audio_size)
1344 		{
1345 			capture_buf_size = audio_size;
1346 			if (capture_buf != NULL)
1347 				free(capture_buf);
1348 			capture_buf = malloc(capture_buf_size);
1349 			if (capture_buf == NULL)
1350 			{
1351 				logger(Sound, Error, "pulse_record(), malloc error");
1352 				capture_buf_size = 0;
1353 				break;
1354 			}
1355 		}
1356 
1357 		memcpy(capture_buf, pulse_buf, audio_size);
1358 
1359 		if (pa_stream_drop(capture_stream) != 0)
1360 		{
1361 			logger(Sound, Error, "pulse_record(), pa_stream_drop: %s",
1362 			       pa_strerror(pa_context_errno(context)));
1363 			break;
1364 		}
1365 
1366 		result = True;
1367 	}
1368 	while (0);
1369 
1370 	pa_threaded_mainloop_unlock(mainloop);
1371 
1372 	if (result == True)
1373 		rdpsnd_record(capture_buf, audio_size);
1374 
1375 	return result;
1376 }
1377 
1378 static RD_BOOL
pulse_recover(pa_stream ** stream)1379 pulse_recover(pa_stream ** stream)
1380 {
1381 	RD_BOOL playback, capture;
1382 
1383 
1384 	playback = capture = False;
1385 
1386 	if (playback_stream != NULL)
1387 		playback = True;
1388 	if (capture_stream != NULL)
1389 		capture = True;
1390 
1391 	if (stream == &playback_stream)
1392 	{
1393 		if (pulse_playback_set_audio
1394 		    (playback_channels, playback_samplerate, playback_samplewidth) == True)
1395 			if (playback_started != True || pulse_playback_start() == True)
1396 				return True;
1397 	}
1398 	else if (stream == &capture_stream)
1399 	{
1400 		if (pulse_capture_set_audio
1401 		    (capture_channels, capture_samplerate, capture_samplewidth) == True)
1402 			if (capture_started != True || pulse_capture_start() == True)
1403 				return True;
1404 	}
1405 
1406 	pulse_deinit();
1407 
1408 	if (pulse_init() != True)
1409 		return False;
1410 
1411 	do
1412 	{
1413 		if (playback == True)
1414 		{
1415 			if (pulse_playback_set_audio
1416 			    (playback_channels, playback_samplerate, playback_samplewidth) != True
1417 			    || (playback_started == True && pulse_playback_start() != True))
1418 				break;
1419 		}
1420 		if (capture == True)
1421 		{
1422 			if (pulse_capture_set_audio
1423 			    (capture_channels, capture_samplerate, capture_samplewidth) != True
1424 			    || (capture_started == True && pulse_capture_start() != True))
1425 				break;
1426 		}
1427 
1428 		return True;
1429 	}
1430 	while (0);
1431 
1432 	pulse_deinit();
1433 
1434 	return False;
1435 }
1436 
1437 struct audio_driver *
pulse_register(char * options)1438 pulse_register(char *options)
1439 {
1440 	static struct audio_driver pulse_driver;
1441 
1442 	memset(&pulse_driver, 0, sizeof(pulse_driver));
1443 
1444 	pulse_driver.name = "pulse";
1445 	pulse_driver.description = "PulseAudio output driver, default device: system dependent";
1446 
1447 	pulse_driver.add_fds = pulse_add_fds;
1448 	pulse_driver.check_fds = pulse_check_fds;
1449 
1450 	pulse_driver.wave_out_open = pulse_open_out;
1451 	pulse_driver.wave_out_close = pulse_close_out;
1452 	pulse_driver.wave_out_format_supported = pulse_format_supported;
1453 	pulse_driver.wave_out_set_format = pulse_set_format_out;
1454 	pulse_driver.wave_out_volume = rdpsnd_dsp_softvol_set;
1455 
1456 	pulse_driver.wave_in_open = pulse_open_in;
1457 	pulse_driver.wave_in_close = pulse_close_in;
1458 	pulse_driver.wave_in_format_supported = pulse_format_supported;
1459 	pulse_driver.wave_in_set_format = pulse_set_format_in;
1460 	pulse_driver.wave_in_volume = NULL;	/* FIXME */
1461 
1462 	pulse_driver.need_byteswap_on_be = 0;
1463 	pulse_driver.need_resampling = 0;
1464 
1465 	if (options != NULL)
1466 	{
1467 		device = xstrdup(options);
1468 	}
1469 	else
1470 	{
1471 		device = DEFAULTDEVICE;
1472 	}
1473 
1474 	return &pulse_driver;
1475 }
1476