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