1 /* @(#)sndconfig.c	1.45 19/09/01 Copyright 1998-2004,2015 Heiko Eissfeldt, Copyright 2004-2019 J. Schilling */
2 #include "config.h"
3 #ifndef lint
4 static	UConst char sccsid[] =
5 "@(#)sndconfig.c	1.45 19/09/01 Copyright 1998-2004,2015 Heiko Eissfeldt, Copyright 2004-2019 J. Schilling";
6 #endif
7 
8 /*
9  * os dependent functions
10  */
11 /*
12  * The contents of this file are subject to the terms of the
13  * Common Development and Distribution License, Version 1.0 only
14  * (the "License").  You may not use this file except in compliance
15  * with the License.
16  *
17  * See the file CDDL.Schily.txt in this distribution for details.
18  * A copy of the CDDL is also available via the Internet at
19  * http://www.opensource.org/licenses/cddl1.txt
20  *
21  * When distributing Covered Code, include this CDDL HEADER in each
22  * file and include the License file CDDL.Schily.txt from this distribution.
23  */
24 
25 #include "config.h"
26 #include <schily/stdio.h>
27 #include <schily/stdlib.h>
28 #include <schily/string.h>
29 #include <schily/fcntl.h>
30 #include <schily/unistd.h>
31 #include <schily/ioctl.h>
32 #include <schily/select.h>
33 #include <schily/schily.h>
34 #include <schily/nlsdefs.h>
35 
36 
37 #if	defined HAVE_SOUNDCARD_H || defined HAVE_SYS_SOUNDCARD_H || \
38 	defined HAVE_LINUX_SOUNDCARD_H || defined HAVE_MACHINE_SOUNDCARD_H
39 #define	HAVE_OSS	1
40 #endif
41 
42 #if	defined HAVE_ALSA_ASOUNDLIB_H || defined HAVE_SYS_ASOUNDLIB_H
43 #define	HAVE_ALSA	1
44 #undef	HAVE_OSS
45 #endif
46 
47 /*
48  * OpenSolaris switched to OSS in 2008, so is it really wise to
49  * prefer the old sound system?
50  */
51 #if	defined HAVE_SYS_AUDIOIO_H || defined HAVE_SUN_AUDIOIO_H
52 #define	HAVE_SUNSOUND	1
53 #undef	HAVE_ALSA
54 #undef	HAVE_OSS
55 #endif
56 
57 #if	defined HAVE_WINDOWS_H && defined HAVE_MMSYSTEM_H
58 #undef	HAVE_WINSOUND
59 #endif
60 
61 #if	defined HAVE_OS2_H && defined HAVE_OS2ME_H
62 #define	HAVE_OS2SOUND	1
63 #undef	HAVE_OSS
64 #endif
65 
66 /* soundcard setup */
67 #if !defined HAVE_SUNSOUND
68 # if defined(HAVE_SOUNDCARD_H) || defined(HAVE_LINUX_SOUNDCARD_H) || \
69 	defined(HAVE_SYS_SOUNDCARD_H) || defined(HAVE_MACHINE_SOUNDCARD_H)
70 #  if defined(HAVE_SOUNDCARD_H)
71 #   include <soundcard.h>
72 #  else
73 #   if defined(HAVE_MACHINE_SOUNDCARD_H)
74 #    include <machine/soundcard.h>
75 #   else
76 #    if defined(HAVE_SYS_SOUNDCARD_H)
77 #	include <sys/soundcard.h>
78 #    else
79 #	if defined(HAVE_LINUX_SOUNDCARD_H)
80 #		include <linux/soundcard.h>
81 #	endif
82 #    endif
83 #   endif
84 #  endif
85 # endif
86 #endif
87 
88 #if defined HAVE_SNDIO_H
89 # include <sndio.h>
90 #undef	SOUND_DEV
91 #define	SOUND_DEV SIO_DEVANY
92 #endif
93 
94 #include "mytype.h"
95 #include "byteorder.h"
96 #include "lowlevel.h"
97 #include "global.h"
98 #include "sndconfig.h"
99 
100 #ifdef	ECHO_TO_SOUNDCARD
101 #   if defined(HAVE_WINSOUND)
102 #	include <schily/windows.h>
103 #	include "mmsystem.h"
104 #   endif
105 
106 #   if	defined(HAVE_OS2SOUND)
107 #	define	INCL_DOS
108 #	define	INCL_OS2MM
109 #	include	<os2.h>
110 #	define	PPFN	_PPFN
111 #	include	<os2me.h>
112 #	undef	PPFN
113 static unsigned long	DeviceID;
114 
115 #	define	FRAGMENTS	2
116 /* playlist-structure */
117 typedef struct {
118 	ULONG	ulCommand;
119 	ULONG	ulOperand1;
120 	ULONG	ulOperand2;
121 	ULONG	ulOperand3;
122 } PLAYLISTSTRUCTURE;
123 
124 static PLAYLISTSTRUCTURE PlayList[FRAGMENTS + 1];
125 static unsigned BufferInd;
126 #   endif /* defined __EMX__ */
127 
128 static char snd_device[200] = SOUND_DEV;
129 
130 int
set_snd_device(devicename)131 set_snd_device(devicename)
132 	const char	*devicename;
133 {
134 	strncpy(snd_device, devicename, sizeof (snd_device));
135 	return (0);
136 }
137 
138 #   if	defined(HAVE_WINSOUND)
139 static HWAVEOUT	DeviceID;
140 #	define	WAVEHDRS	3
141 static WAVEHDR	wavehdr[WAVEHDRS];
142 static unsigned	lastwav = 0;
143 static unsigned	wavehdrinuse = 0;
144 static HANDLE	waveOutEvent;
145 
146 static int check_winsound_caps __PR((int bits, double rate, int channels));
147 
148 static int
check_winsound_caps(bits,rate,channels)149 check_winsound_caps(bits, rate, channels)
150 	int	bits;
151 	double	rate;
152 	int	channels;
153 {
154 	int		result = 0;
155 	WAVEOUTCAPS	caps;
156 
157 	/*
158 	 * get caps
159 	 */
160 	if (waveOutGetDevCaps(0, &caps, sizeof (caps))) {
161 		errmsgno(EX_BAD, _("Cannot get soundcard capabilities!\n"));
162 		return (1);
163 	}
164 
165 	/*
166 	 * check caps
167 	 */
168 	if ((bits == 8 && !(caps.dwFormats & 0x333)) ||
169 	    (bits == 16 && !(caps.dwFormats & 0xccc))) {
170 		errmsgno(EX_BAD, _("%d bits sound are not supported.\n"), bits);
171 		result = 2;
172 	}
173 
174 	if ((channels == 1 && !(caps.dwFormats & 0x555)) ||
175 	    (channels == 2 && !(caps.dwFormats & 0xaaa))) {
176 		errmsgno(EX_BAD,
177 			_("%d sound channels are not supported.\n"), channels);
178 		result = 3;
179 	}
180 
181 	if ((rate == 44100.0 && !(caps.dwFormats & 0xf00)) ||
182 	    (rate == 22050.0 && !(caps.dwFormats & 0xf0)) ||
183 	    (rate == 11025.0 && !(caps.dwFormats & 0xf))) {
184 		errmsgno(EX_BAD,
185 			_("%d sample rate is not supported.\n"), (int)rate);
186 		result = 4;
187 	}
188 
189 	return (result);
190 }
191 
192 static void CALLBACK waveOutProc __PR((HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2));
193 
waveOutProc(hwo,uMsg,dwInstance,dwParam1,dwParam2)194 static void CALLBACK waveOutProc(hwo, uMsg, dwInstance, dwParam1, dwParam2)
195 	HWAVEOUT hwo;
196 	UINT	uMsg;
197 	DWORD	dwInstance;
198 	DWORD	dwParam1;
199 	DWORD	dwParam2;
200 {
201 	if (uMsg == WOM_DONE) {
202 		if (wavehdrinuse) {
203 			wavehdrinuse--;
204 			SetEvent(waveOutEvent);
205 		}
206 	}
207 }
208 
209 #   endif /* defined HAVE_WINSOUND */
210 #endif /* defined ECHO_TO_SOUNDCARD */
211 
212 #ifdef	HAVE_SUN_AUDIOIO_H
213 # include <sun/audioio.h>
214 #endif
215 #ifdef	HAVE_SYS_AUDIOIO_H
216 # include <sys/audioio.h>
217 #endif
218 
219 #ifdef	HAVE_ALSA
220 #ifdef	HAVE_SYS_ASOUNDLIB_H
221 # include <sys/asoundlib.h>
222 #endif
223 #ifdef	HAVE_ALSA_ASOUNDLIB_H
224 # include <alsa/asoundlib.h>
225 #endif
226 snd_pcm_t	*pcm_handle;
227 unsigned frame_factor;
228 #endif
229 
230 #if	defined	HAVE_SNDIO_H
231 struct	sio_hdl *hdl;
232 #endif
233 
234 #if	defined	HAVE_OSS && defined SNDCTL_DSP_GETOSPACE
235 audio_buf_info abinfo;
236 #endif
237 
238 #if	defined HAVE_PULSE_PULSEAUDIO_H
239 # include	<pulse/pulseaudio.h>
240 
241 #define	PULSEAUDIO_SIMPLE_API	1
242 /*#undef	PULSEAUDIO_SIMPLE_API*/
243 
244 #ifdef	PULSEAUDIO_SIMPLE_API
245 # include	<pulse/simple.h>
246 pa_simple	*ptr_pa_simple = NULL;
247 #else
248 pa_mainloop	*ptr_pa_mainloop = NULL;
249 pa_mainloop_api	*ptr_pa_mainloop_api = NULL;
250 pa_context	*ptr_pa_context = NULL;
251 pa_stream	*ptr_pa_stream = NULL;
252 #endif
253 
254 struct pa_sample_spec	_pa_sample_spec;
255 
256 #endif
257 
258 
259 
260 int
init_soundcard(rate,bits)261 init_soundcard(rate, bits)
262 	double	rate;
263 	int	bits;
264 {
265 #ifdef	ECHO_TO_SOUNDCARD
266 	if (global.echo) {
267 #if	defined HAVE_PULSE_PULSEAUDIO_H
268 		int ret = 1;
269 		if (bits != 16 && bits != 8) {
270 			error("Cannot use pulseaudio sound device with %d bits per sample\n",
271 				bits);
272 			return (1);
273 		}
274 		/* setup format */
275 		_pa_sample_spec.format = bits == 16 ? PA_SAMPLE_S16LE
276 				: PA_SAMPLE_U8;
277 		_pa_sample_spec.channels = 2;
278 		_pa_sample_spec.rate = rate;
279 
280 #ifdef	PULSEAUDIO_SIMPLE_API
281 		ptr_pa_simple = pa_simple_new(
282 			NULL,				/* default server */
283 			pa_locale_to_utf8("Cdda2wav"),	/* application name */
284 			PA_STREAM_PLAYBACK,		/* sound transfer direction */
285 			NULL,				/* default device */
286 			pa_locale_to_utf8("Music"),	/* stream description */
287 			&_pa_sample_spec,		/* sample format */
288 			NULL,				/* default channel map */
289 			NULL,				/* default buffering attributes */
290 			&ret);				/* error code */
291 
292 		if (ptr_pa_simple == NULL) {
293 #ifdef	SND_DEBUG
294 			error("Cannot use pulseaudio sound daemon (no connected object): %d. Trying native sound device...\n",
295 				ret);
296 #endif
297 			goto pa_outsimple;
298 		}
299 #else
300 		/* get a mainloop */
301 		ptr_pa_mainloop = pa_mainloop_new();
302 		if (ptr_pa_mainloop == NULL) {
303 			error("Cannot use pulseaudio sound daemon (no mainloop). Trying native sound device...\n");
304 			goto pa_out0;
305 		}
306 
307 		ptr_pa_mainloop_api = pa_mainloop_get_api(ptr_pa_mainloop);
308 
309 
310 		/* get a context */
311 		ptr_pa_context = pa_context_new(
312 			ptr_pa_mainloop_api,
313 			pa_locale_to_utf8("Cdda2wav_ctx"));
314 
315 		if (ptr_pa_context == NULL) {
316 			error("Cannot use pulseaudio sound daemon (no context). Trying native sound device...\n");
317 			goto pa_out1;
318 		}
319 
320 		/* connect a context */
321 		if (pa_context_connect(
322 		    ptr_pa_context,
323 		    NULL,		/* default server */
324 		    0,			/* flags */
325 		    NULL) < 0 {		/* spawn API */
326 #ifdef	SND_DEBUG
327 			error("Cannot open pulseaudio sound device (no context connect). Trying native sound device...\n");
328 #endif
329 			goto pa_out2;
330 		}
331 
332 		int state = 0;
333 		do {
334 			if (pa_mainloop_iterate(ptr_pa_mainloop, 1, NULL) < 0) {
335 				error(
336 				"Cannot open pulseaudio sound device (mainloop_iterate failed). Trying native sound device...\n");
337 				goto pa_out2;
338 			}
339 			state = pa_context_get_state(ptr_pa_context);
340 			if (state != PA_CONTEXT_CONNECTING &&
341 			    state != PA_CONTEXT_AUTHORIZING &&
342 			    state != PA_CONTEXT_SETTING_NAME &&
343 			    state != PA_CONTEXT_READY) {
344 				error(
345 				"Cannot open pulseaudio sound device (bad context state %d). Trying native sound device...\n",
346 				state);
347 				goto pa_out2;
348 			}
349 		} while (state != PA_CONTEXT_READY);
350 
351 		pa_channel_map pacmap;
352 		pa_channel_map_init_auto(&pacmap, _pa_sample_spec.channels,
353 					PA_CHANNEL_MAP_WAVEEX);
354 
355 		ptr_pa_stream = pa_stream_new(
356 			ptr_pa_context,
357 			pa_locale_to_utf8("Cdda2wav"),
358 			&_pa_sample_spec,
359 			&pacmap);
360 
361 		if (ptr_pa_stream == NULL) {
362 			error("Cannot use pulseaudio sound daemon (no stream). Trying native sound device...\n");
363 			goto pa_out2;
364 		}
365 
366 		if (pa_stream_connect_playback(
367 		    ptr_pa_stream,
368 		    NULL,
369 		    NULL,
370 		    0,
371 		    NULL,
372 		    NULL) < 0) {
373 			error("Cannot use pulseaudio sound daemon (no connect). Trying native sound device...\n");
374 			goto pa_out2;
375 		}
376 
377 		state = 0;
378 		do {
379 			if (pa_mainloop_iterate(ptr_pa_mainloop, 1, NULL) < 0) {
380 				error(
381 				"Cannot open pulseaudio sound device (mainloop_iterate failed). Trying native sound device...\n");
382 				goto pa_out2;
383 			}
384 			state = pa_stream_get_state(ptr_pa_stream);
385 			if (state != PA_STREAM_CREATING &&
386 			    state != PA_STREAM_READY) {
387 				error(
388 				"Cannot open pulseaudio sound device (bad stream state %d). Trying native sound device...\n",
389 				state);
390 				goto pa_out2;
391 			}
392 		} while (state != PA_STREAM_READY);
393 
394 		return (0);
395 		/* if unsuccessful, fallback to native sound API */
396 
397 		/* clean up */
398 pa_out2:
399 		if (ptr_pa_context)
400 			pa_context_unref(ptr_pa_context);
401 pa_out1:
402 		if (ptr_pa_mainloop)
403 			pa_mainloop_free(ptr_pa_mainloop);
404 pa_out0:
405 		ptr_pa_stream = NULL;
406 		ptr_pa_context = NULL;
407 		ptr_pa_mainloop = NULL;
408 		ptr_pa_mainloop_api = NULL;
409 #endif
410 pa_outsimple:
411 		;
412 #endif
413 # if	defined(HAVE_SNDIO_H)
414 		struct	sio_par par;
415 		hdl = sio_open(snd_device, SIO_PLAY, 0);
416 		if (hdl == NULL) {
417 			errmsg("Cannot open sndio sound device '%s'.\n", snd_device);
418 			return (1);
419 		}
420 		sio_initpar(&par);
421 		par.bits = bits;
422 		par.sig = 1;
423 		par.le = SIO_LE_NATIVE;
424 		par.pchan = 2;
425 		par.rate = rate;
426 		par.appbufsz = 44100 / 4; /* 61152 */
427 		if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par)) {
428 			errmsg("Cannot set sound parameters for '%s'.\n", snd_device);
429 			sio_close(hdl);
430 			hdl = NULL;
431 			return (1);
432 		}
433 		if (par.bits != bits || par.sig != 1 || par.le != SIO_LE_NATIVE ||
434 		    par.pchan != 2 || par.rate != (int)rate) {
435 			errmsg("Unsupported sound parameters for '%s'.\n", snd_device);
436 			sio_close(hdl);
437 			hdl = NULL;
438 			return (1);
439 		}
440 		if (!sio_start(hdl)) {
441 			errmsg("Couldn't start sound device '%s'.\n", snd_device);
442 			sio_close(hdl);
443 			hdl = NULL;
444 			return (1);
445 		}
446 # else
447 #  if	defined(HAVE_OSS) && HAVE_OSS == 1
448 		if (open_snd_device() != 0) {
449 			errmsg(_("Cannot open oss sound device '%s'.\n"), snd_device);
450 			global.echo = 0;
451 		} else {
452 			/*
453 			 * This the sound device initialisation for 4front Open sound drivers
454 			 */
455 			int	dummy;
456 			int	garbled_rate = rate;
457 			int	stereo = (global.channels == 2);
458 			int	myformat = bits == 8 ? AFMT_U8 :
459 					(MY_LITTLE_ENDIAN ?
460 					AFMT_S16_LE : AFMT_S16_BE);
461 			int	mask;
462 
463 			if (ioctl(global.soundcard_fd,
464 			    SNDCTL_DSP_GETBLKSIZE, &dummy) == -1) {
465 				errmsg(_("Cannot get blocksize for %s.\n"),
466 					snd_device);
467 				global.echo = 0;
468 			}
469 			if (ioctl(global.soundcard_fd,
470 			    SNDCTL_DSP_SYNC, NULL) == -1) {
471 				errmsg(_("Cannot sync for %s.\n"),
472 					snd_device);
473 				global.echo = 0;
474 			}
475 
476 #if	defined SNDCTL_DSP_GETOSPACE
477 			if (ioctl(global.soundcard_fd,
478 			    SNDCTL_DSP_GETOSPACE, &abinfo) == -1) {
479 				errmsg(_("Cannot get input buffersize for %s.\n"),
480 					snd_device);
481 				abinfo.fragments  = 0;
482 			}
483 #endif
484 
485 			/*
486 			 * check, if the sound device can do the
487 			 * requested format
488 			 */
489 			if (ioctl(global.soundcard_fd,
490 			    SNDCTL_DSP_GETFMTS, &mask) == -1) {
491 				errmsg(_("Fatal error in ioctl(SNDCTL_DSP_GETFMTS).\n"));
492 				return (-1);
493 			}
494 			if ((mask & myformat) == 0) {
495 				errmsgno(EX_BAD,
496 				_("Sound format (%d bits signed) is not available.\n"),
497 				bits);
498 				if ((mask & AFMT_U8) != 0) {
499 					bits = 8;
500 					myformat = AFMT_U8;
501 				}
502 			}
503 			if (ioctl(global.soundcard_fd,
504 			    SNDCTL_DSP_SETFMT, &myformat) == -1) {
505 				errmsg(_("Cannot set %d bits/sample for %s.\n"),
506 					bits, snd_device);
507 			    global.echo = 0;
508 			}
509 
510 			/*
511 			 * limited sound devices may not support stereo
512 			 */
513 			if (stereo &&
514 			    ioctl(global.soundcard_fd,
515 			    SNDCTL_DSP_STEREO, &stereo) == -1) {
516 				errmsg(_("Cannot set stereo mode for %s.\n"),
517 					snd_device);
518 				stereo = 0;
519 			}
520 			if (!stereo &&
521 			    ioctl(global.soundcard_fd,
522 			    SNDCTL_DSP_STEREO, &stereo) == -1) {
523 				errmsg(_("Cannot set mono mode for %s.\n"),
524 					snd_device);
525 				global.echo = 0;
526 			}
527 
528 			/*
529 			 * set the sample rate
530 			 */
531 			if (ioctl(global.soundcard_fd,
532 			    SNDCTL_DSP_SPEED, &garbled_rate) == -1) {
533 				errmsg(_("Cannot set rate %d.%2d Hz for %s.\n"),
534 					(int)rate, (int)(rate*100)%100,
535 					snd_device);
536 				global.echo = 0;
537 			}
538 			if (abs((long)rate - garbled_rate) > rate / 20) {
539 				errmsgno(EX_BAD,
540 				_("Sound device: next best sample rate is %d.\n"),
541 				garbled_rate);
542 			}
543 		}
544 
545 #  else /* HAVE_OSS */
546 
547 #   if defined	HAVE_SYS_AUDIOIO_H || defined HAVE_SUN_AUDIOIO_H
548 		/*
549 		 * This is the SunOS / Solaris / NetBSD
550 		 * sound initialisation
551 		 */
552 		if ((global.soundcard_fd = open(snd_device, O_WRONLY, 0)) ==
553 		    EOF) {
554 			errmsg(_("Cannot open '%s'.\n"), snd_device);
555 			global.echo = 0;
556 		} else {
557 #    if	defined(AUDIO_INITINFO) && defined(AUDIO_ENCODING_LINEAR)
558 			audio_info_t	info;
559 
560 			AUDIO_INITINFO(&info);
561 			info.play.sample_rate = rate;
562 			info.play.channels = global.channels;
563 			info.play.precision = bits;
564 			info.play.encoding = AUDIO_ENCODING_LINEAR;
565 			info.play.pause = 0;
566 			info.record.pause = 0;
567 			info.monitor_gain = 0;
568 			if (ioctl(global.soundcard_fd, AUDIO_SETINFO, &info) <
569 			    0) {
570 				errmsg(_("Cannot init %s (sun).\n"),
571 					snd_device);
572 				global.echo = 0;
573 			}
574 #    else
575 			errmsgno(EX_BAD,
576 			_("Cannot init sound device with %u.%u kHz sample rate on %s (sun compatible).\n"),
577 			rate / 1000, (rate % 1000) / 100,
578 			snd_device);
579 			global.echo = 0;
580 #    endif
581 		}
582 #   else /* SUN audio */
583 #    if defined(HAVE_WINSOUND)
584 		/*
585 		 * Windows sound info
586 		 */
587 		MMRESULT	mmres;
588 		WAVEFORMATEX	wavform;
589 
590 		if (waveOutGetNumDevs() < 1) {
591 			errmsgno(EX_BAD, _("No sound devices available!\n"));
592 			global.echo = 0;
593 			return (1);
594 		}
595 
596 		/*
597 		 * check capabilities
598 		 */
599 		if (check_winsound_caps(bits, rate, global.channels) != 0) {
600 			errmsgno(EX_BAD,
601 			_("Soundcard capabilities are not sufficient!\n"));
602 			global.echo = 0;
603 			return (1);
604 		}
605 
606 		wavform.wFormatTag = WAVE_FORMAT_PCM;
607 		wavform.nChannels = global.channels;
608 		wavform.nSamplesPerSec = (int)rate;
609 		wavform.wBitsPerSample = bits;
610 		wavform.cbSize = sizeof (wavform);
611 		wavform.nAvgBytesPerSec = (int)rate * global.channels *
612 						(wavform.wBitsPerSample / 8);
613 		wavform.nBlockAlign = global.channels * (wavform.wBitsPerSample / 8);
614 
615 		waveOutEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
616 		DeviceID = 0;
617 		mmres = waveOutOpen(&DeviceID, WAVE_MAPPER, &wavform,
618 			(UInt32_t)waveOutProc, 0, CALLBACK_FUNCTION);
619 		if (mmres) {
620 			char	erstr[329];
621 
622 			waveOutGetErrorText(mmres, erstr, sizeof (erstr));
623 			errmsgno(EX_BAD,
624 				_("Soundcard open error: %s!\n"), erstr);
625 			global.echo = 0;
626 			return (1);
627 		}
628 
629 		global.soundcard_fd = 0;
630 
631 		/*
632 		 * init all wavehdrs
633 		 */
634 		{ int	i;
635 
636 			for (i = 0; i < WAVEHDRS; i++) {
637 				wavehdr[i].dwBufferLength = (global.channels*(bits/ 8)*(int)rate*
638 							global.nsectors)/75;
639 				wavehdr[i].lpData = malloc(wavehdr[i].dwBufferLength);
640 				if (wavehdr[i].lpData == NULL) {
641 					errmsg(
642 					_("No memory for sound buffers available.\n"));
643 					waveOutReset(0);
644 					CloseHandle(waveOutEvent);
645 					waveOutClose(DeviceID);
646 					return (1);
647 				}
648 				mmres = waveOutPrepareHeader(DeviceID,
649 						&wavehdr[i], sizeof (WAVEHDR));
650 				if (mmres) {
651 					char	erstr[129];
652 
653 					waveOutGetErrorText(mmres, erstr,
654 							sizeof (erstr));
655 					errmsgno(EX_BAD,
656 					_("soundcard prepare error: %s!\n"),
657 						erstr);
658 					return (1);
659 				}
660 
661 				wavehdr[i].dwLoops = 0;
662 				wavehdr[i].dwFlags = WHDR_DONE;
663 				wavehdr[i].dwBufferLength = 0;
664 			}
665 		}
666 
667 #    else
668 #	if defined(HAVE_OS2SOUND)
669 #	if defined(HAVE_MMPM)
670 		/*
671 		 * OS/2 MMPM/2 MCI sound info
672 		 */
673 		MCI_OPEN_PARMS	mciOpenParms;
674 		int		i;
675 
676 		/*
677 		 * create playlist
678 		 */
679 		for (i = 0; i < FRAGMENTS; i++) {
680 			PlayList[i].ulCommand = DATA_OPERATION;	/* play data */
681 			PlayList[i].ulOperand1 = 0;		/* address */
682 			PlayList[i].ulOperand2 = 0;		/* size */
683 			PlayList[i].ulOperand3 = 0;		/* offset */
684 		}
685 		PlayList[FRAGMENTS].ulCommand = BRANCH_OPERATION; /* jump */
686 		PlayList[FRAGMENTS].ulOperand1 = 0;
687 		PlayList[FRAGMENTS].ulOperand2 = 0;		/* destination */
688 		PlayList[FRAGMENTS].ulOperand3 = 0;
689 
690 		memset(&mciOpenParms, 0, sizeof (mciOpenParms));
691 		mciOpenParms.pszDeviceType = (PSZ) (((unsigned long) MCI_DEVTYPE_WAVEFORM_AUDIO << 16) | \
692 						(unsigned short) DeviceIndex);
693 		mciOpenParms.pszElementName = (PSZ) & PlayList;
694 
695 		/*
696 		 * try to open the sound device
697 		 */
698 		if (mciSendCommand(0, MCI_OPEN,
699 			MCI_WAIT | MCI_OPEN_SHAREABLE | MCIOPEN_Type_ID,
700 							&mciOpenParms, 0)
701 				!= MCIERR_SUCCESS) {
702 			/*
703 			 * no sound
704 			 */
705 			errmsgno(EX_BAD, _("No sound devices available!\n"));
706 			global.echo = 0;
707 			return (1);
708 		}
709 		/*
710 		 * try to set the parameters
711 		 */
712 		DeviceID = mciOpenParms.usDeviceID;
713 
714 		{
715 			MCI_WAVE_SET_PARMS	mciWaveSetParms;
716 
717 			memset(&mciWaveSetParms, 0, sizeof (mciWaveSetParms));
718 			mciWaveSetParms.ulSamplesPerSec = rate;
719 			mciWaveSetParms.usBitsPerSample = bits;
720 			mciWaveSetParms.usChannels = global.channels;
721 			mciWaveSetParms.ulAudio = MCI_SET_AUDIO_ALL;
722 
723 			/*
724 			 * set play-parameters
725 			 */
726 			if (mciSendCommand(DeviceID, MCI_SET,
727 					MCI_WAIT | MCI_WAVE_SET_SAMPLESPERSEC |
728 					MCI_WAVE_SET_BITSPERSAMPLE |
729 					MCI_WAVE_SET_CHANNELS,
730 					(PVOID) & mciWaveSetParms, 0)) {
731 				MCI_GENERIC_PARMS	mciGenericParms;
732 
733 				errmsgno(EX_BAD,
734 				_("Soundcard capabilities are not sufficient!\n"));
735 				global.echo = 0;
736 				/*
737 				 * close
738 				 */
739 				mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT,
740 							&mciGenericParms, 0);
741 				return (1);
742 			}
743 		}
744 
745 #	endif /* EMX MMPM OS2 sound */
746 #	else
747 #	if defined(HAVE_ALSA)
748 		int		card = -1;
749 		int		rtn;
750 
751 /*error("setting ALSA device: '%s'.\n", snd_device);*/
752 		rtn = snd_pcm_open(&pcm_handle,
753 			snd_device, SND_PCM_STREAM_PLAYBACK, 0);
754 		if (rtn < 0) {
755 			errmsg(_("Error opening ALSA sound device (%s).\n"), snd_strerror(rtn));
756 			return (1);
757 		}
758 
759 		if ((rtn = snd_pcm_set_params(
760 			pcm_handle,
761 			bits == 8 ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16_LE,
762 			SND_PCM_ACCESS_RW_INTERLEAVED,
763 			global.channels,
764 			rate,
765 			1,
766 			500000)) < 0) {
767 			error("Error setting ALSA parameters: %s.\n",
768 				snd_strerror(rtn));
769 			return (1);
770 		}
771 
772 		frame_factor = ((bits == 8 ? 1 : 2) * global.channels);
773 
774 
775 #	endif /* QNX sound */
776 #	endif /* EMX OS2 sound */
777 #	endif /* CYGWIN Windows sound */
778 #   endif /* else SUN audio */
779 #  endif /* else HAVE_OSS */
780 # endif /* else HAVE_SNDIO_H */
781 	}
782 #endif /* ifdef ECHO_TO_SOUNDCARD */
783 	return (0);
784 }
785 
786 int
787 open_snd_device()
788 {
789 #if	defined ECHO_TO_SOUNDCARD && !defined HAVE_WINSOUND && !defined HAVE_OS2SOUND
790 #if	defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
791 	int	fl;
792 #endif
793 
794 	global.soundcard_fd = open(snd_device,
795 #ifdef	linux
796 		/*
797 		 * Linux BUG: the sound driver open() blocks,
798 		 * if the device is in use.
799 		 */
800 		O_NONBLOCK |
801 #endif
802 		O_WRONLY, 0);
803 
804 #if	defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
805 	fl = fcntl(global.soundcard_fd, F_GETFL, 0);
806 	fl &= ~O_NONBLOCK;
807 	fcntl(global.soundcard_fd, F_SETFL, fl);
808 #endif
809 
810 	return (global.soundcard_fd < 0);
811 
812 #else	/* def ECHO_TO_SOUNDCARD && !def __CYGWIN32__ && !def __CYGWIN32__ && !def __MINGW32__ && !def __EMX__ */
813 	return (0);
814 #endif
815 }
816 
817 int
818 close_snd_device()
819 {
820 #if	!defined ECHO_TO_SOUNDCARD
821 	return (0);
822 #else
823 
824 #if	defined HAVE_PULSE_PULSEAUDIO_H
825 #ifdef	PULSEAUDIO_SIMPLE_API
826 	if (ptr_pa_simple) {
827 		int ret = 0;
828 		if (pa_simple_drain(ptr_pa_simple, &ret) < 0) {
829 			errmsg(_("Soundcard pulse audio drain error: %d!\n"), ret);
830 		}
831 		pa_simple_free(ptr_pa_simple);
832 		ptr_pa_simple = NULL;
833 		return (0);
834 	}
835 #else
836 	if (ptr_pa_stream) {
837 		pa_operation * ptr_op =
838 			pa_stream_drain(
839 				ptr_pa_stream,		/* stream */
840 				NULL,			/* callback */
841 				NULL);			/* user data */
842 		if (ptr_op == NULL) {
843 			errmsg(_("Soundcard pulse audio drain error!\n"));
844 		}
845 		while (pa_operation_get_state(ptr_op) != PA_OPERATION_DONE) {
846 			if (pa_context_get_state(ptr_pa_context) != PA_CONTEXT_READY ||
847 			    pa_stream_get_state(ptr_pa_stream) != PA_STREAM_READY ||
848 			    pa_mainloop_iterate(ptr_pa_mainloop, 1, NULL) < 0) {
849 				pa_operation_cancel(ptr_op);
850 				break;
851 			}
852 		}
853 		pa_operation_unref(ptr_op);
854 
855 		pa_stream_disconnect(ptr_pa_stream);
856 		pa_stream_unref(ptr_pa_stream);
857 
858 		if (ptr_pa_context)
859 			pa_context_unref(ptr_pa_context);
860 		if (ptr_pa_mainloop) {
861 			pa_signal_done();
862 			pa_mainloop_free(ptr_pa_mainloop);
863 		}
864 		ptr_pa_stream = NULL;
865 		ptr_pa_context = NULL;
866 		ptr_pa_mainloop = NULL;
867 		ptr_pa_mainloop_api = NULL;
868 		return (0);
869 	}
870 #endif
871 	/* FALLTHROUGH to native API */
872 #endif /* !HAVE_PULSE_PULSEAUDIO_H */
873 
874 # if	defined(HAVE_WINSOUND)
875 	waveOutReset(0);
876 	CloseHandle(waveOutEvent);
877 	return (waveOutClose(DeviceID));
878 # else /* !Cygwin32 */
879 
880 #  if	defined HAVE_OS2SOUND
881 #   if	defined HAVE_MMPM
882 	/*
883 	 * close the sound device
884 	 */
885 	MCI_GENERIC_PARMS mciGenericParms;
886 	mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, &mciGenericParms, 0);
887 #   else /* HAVE_MMPM */
888 	return (0);
889 #   endif /* HAVE_MMPM */
890 #  else /* !EMX */
891 #   if	defined	HAVE_ALSA
892 	snd_pcm_drain(pcm_handle);
893 	return (snd_pcm_close(pcm_handle));
894 #   else /* !ALSA */
895 #    if defined HAVE_SNDIO_H
896 	if (hdl != NULL) {
897 		sio_close(hdl);
898 		hdl = NULL;
899 	}
900 	return (0);
901 #    else
902 	return (close(global.soundcard_fd));
903 #    endif /* !HAVE_SNDIO_H */
904 #   endif /* !QNX */
905 #  endif /* !EMX */
906 # endif /* !Cygwin32 */
907 #endif /* ifdef ECHO_TO_SOUNDCARD */
908 }
909 
910 int
911 write_snd_device(buffer, todo)
912 	char		*buffer;
913 	unsigned	todo;
914 {
915 	int	result = 0;
916 #ifdef	ECHO_TO_SOUNDCARD
917 
918 #if	defined HAVE_PULSE_PULSEAUDIO_H
919 #ifdef	PULSEAUDIO_SIMPLE_API
920 	if (ptr_pa_simple) {
921 		int ret = pa_simple_write(
922 			ptr_pa_simple,		/* object */
923 			buffer,			/* sample data */
924 			todo,			/* size in bytes */
925 			&result);		/* error code */
926 		if (ret < 0) {
927 			errmsgno(EX_BAD,
928 				_("Soundcard write error (pulseaudio): %d, %s!\n"), result, pa_strerror(result));
929 		}
930 		return (0);
931 	}
932 #else
933 	if (ptr_pa_stream) {
934 		result = pa_stream_write(
935 			ptr_pa_stream,		/* stream */
936 			buffer,			/* sample data */
937 			todo,			/* size in bytes */
938 			NULL,			/* callback to free() data */
939 			0LL,			/* seek offset */
940 			PA_SEEK_RELATIVE);	/* seek mode */
941 		if (result < 0) {
942 			errmsgno(EX_BAD,
943 				_("Soundcard write error (pulseaudio): %d, %s!\n"), result, pa_strerror(result));
944 		}
945 		return (result);
946 	}
947 #endif
948 	/* FALLTHROUGH to native transport */
949 #endif /* defined HAVE_PULSE_PULSEAUDIO_H */
950 
951 #if	defined HAVE_SNDIO_H
952 	if (hdl == NULL || !sio_write(hdl, buffer, todo)) {
953 		errmsgno(EX_BAD,
954 			_("Soundcard write error (sndio)!\n"));
955 		return (1);
956 	}
957 #else
958 #if	defined(HAVE_WINSOUND)
959 	MMRESULT	mmres;
960 
961 	wavehdr[lastwav].dwBufferLength = todo;
962 	memcpy(wavehdr[lastwav].lpData, buffer, todo);
963 
964 	while (wavehdrinuse >= WAVEHDRS) {
965 		WaitForSingleObject(waveOutEvent, INFINITE);
966 		ResetEvent(waveOutEvent);
967 	}
968 	wavehdrinuse++;
969 	mmres = waveOutWrite(DeviceID, &wavehdr[lastwav], sizeof (WAVEHDR));
970 	if (mmres) {
971 		char erstr[129];
972 
973 		waveOutGetErrorText(mmres, erstr, sizeof (erstr));
974 		errmsgno(EX_BAD, _("Soundcard write error (cygwin): %s!\n"), erstr);
975 		return (1);
976 	}
977 	if (++lastwav >= WAVEHDRS)
978 		lastwav -= WAVEHDRS;
979 	result = mmres;
980 #else
981 #if	defined HAVE_OS2SOUND
982 	Playlist[BufferInd].ulOperand1 = buffer;
983 	Playlist[BufferInd].ulOperand2 = todo;
984 	Playlist[BufferInd].ulOperand3 = 0;
985 	if (++BufferInd >= FRAGMENTS)
986 		BufferInd -= FRAGMENTS;
987 
988 	/*
989 	 * no MCI_WAIT here, because application program has to continue
990 	 */
991 	memset(&mciPlayParms, 0, sizeof (mciPlayParms));
992 	if (mciSendCommand(DeviceID, MCI_PLAY, MCI_FROM, &mciPlayParms, 0)) {
993 		errmsgno(EX_BAD, _("Soundcard write error (os/2): %s!\n"), erstr);
994 		return (1);
995 	}
996 	result = 0;
997 #else
998 	int retval2;
999 	int towrite;
1000 
1001 #if	defined	HAVE_OSS && defined SNDCTL_DSP_GETOSPACE
1002 	towrite = abinfo.fragments * abinfo.fragsize;
1003 	if (towrite == 0)
1004 #endif
1005 		towrite = todo;
1006 	do {
1007 		int		wrote;
1008 #ifdef	HAVE_SELECT
1009 		fd_set		writefds[1];
1010 		struct timeval	timeout2;
1011 
1012 		timeout2.tv_sec = 0;
1013 		timeout2.tv_usec = 4*120000;
1014 
1015 		FD_ZERO(writefds);
1016 		FD_SET(global.soundcard_fd, writefds);
1017 		retval2 = select(global.soundcard_fd + 1,
1018 				NULL, writefds, NULL, &timeout2);
1019 		switch (retval2) {
1020 
1021 		default:
1022 		case -1: errmsg(_("Select failed.\n"));
1023 			/* FALLTHROUGH */
1024 		case 0: /* timeout */
1025 			result = 2;
1026 			goto outside_loop;
1027 		case 1: break;
1028 		}
1029 #endif	/* HAVE_SELECT */
1030 		if (towrite > todo) {
1031 			towrite = todo;
1032 		}
1033 #if		defined HAVE_ALSA
1034 		{
1035 			snd_pcm_sframes_t frames = towrite / frame_factor;
1036 			wrote = snd_pcm_writei(pcm_handle, buffer, frames);
1037 			if (wrote < 0) {
1038 				wrote = snd_pcm_recover(pcm_handle, wrote, 0);
1039 			}
1040 			wrote *= frame_factor;
1041 		}
1042 #else
1043 		wrote = write(global.soundcard_fd, buffer, towrite);
1044 #endif
1045 		if (wrote <= 0) {
1046 #if		defined HAVE_ALSA
1047 			errmsg(_("Can't write audio (alsa).\n"));
1048 #else
1049 			errmsg(_("Can't write audio (oss).\n"));
1050 #endif
1051 			result = 1;
1052 			goto outside_loop;
1053 		} else {
1054 			todo -= wrote;
1055 			buffer += wrote;
1056 		}
1057 	} while (todo > 0);
1058 outside_loop:
1059 	;
1060 #endif	/* !defined __EMX__ */
1061 #endif	/* !defined __CYGWIN32__ */
1062 #endif	/* !defined HAVE_SNDIO_H */
1063 #endif	/* ECHO_TO_SOUNDCARD */
1064 	return (result);
1065 }
1066