2 * This file has been modified for the cdrkit suite.
4 * The behaviour and appearence of the program code below can differ to a major
5 * extent from the version distributed by the original author(s).
7 * For details, see Changelog file distributed with the cdrkit package. If you
8 * received this file from another source then ask the distributing person for
9 * a log of modifications.
13 /* @(#)sndconfig.c 1.17 04/08/03 Copyright 1998-2004 Heiko Eissfeldt */
14 /* os dependent functions */
21 #include <sys/ioctl.h>
23 #if !defined __CYGWIN32__
24 # include <timedefs.h>
30 #if defined (HAVE_SOUNDCARD_H) || defined (HAVE_LINUX_SOUNDCARD_H) || defined (HAVE_SYS_SOUNDCARD_H) || defined (HAVE_MACHINE_SOUNDCARD_H)
31 # if defined (HAVE_SOUNDCARD_H)
32 # include <soundcard.h>
34 # if defined (HAVE_MACHINE_SOUNDCARD_H)
35 # include <machine/soundcard.h>
37 # if defined (HAVE_SYS_SOUNDCARD_H)
38 # include <sys/soundcard.h>
40 # if defined (HAVE_LINUX_SOUNDCARD_H)
41 # include <linux/soundcard.h>
49 #include "byteorder.h"
52 #include "sndconfig.h"
54 #ifdef ECHO_TO_SOUNDCARD
55 # if defined(__CYGWIN32__)
57 # include "mmsystem.h"
67 static unsigned long DeviceID;
70 /* playlist-structure */
73 ULONG ulOperand1, ulOperand2, ulOperand3;
76 static PLAYLISTSTRUCTURE PlayList[FRAGMENTS + 1];
77 static unsigned BufferInd;
78 # endif /* defined __EMX__ */
80 static char snd_device[200] = SOUND_DEV;
82 int set_snd_device(const char *devicename)
84 strncpy(snd_device, devicename, sizeof(snd_device));
88 # if defined __CYGWIN32__
89 static HWAVEOUT DeviceID;
91 static WAVEHDR wavehdr[WAVEHDRS];
92 static unsigned lastwav = 0;
94 static int check_winsound_caps(int bits, double rate, int channels);
96 static int check_winsound_caps(int bits, double rate, int channels)
103 if (waveOutGetDevCaps(0, &caps, sizeof(caps))) {
104 fprintf(stderr, "cannot get soundcard capabilities!\n");
109 if ((bits == 8 && !(caps.dwFormats & 0x333)) ||
110 (bits == 16 && !(caps.dwFormats & 0xccc))) {
111 fprintf(stderr, "%d bits sound are not supported\n", bits);
115 if ((channels == 1 && !(caps.dwFormats & 0x555)) ||
116 (channels == 2 && !(caps.dwFormats & 0xaaa))) {
117 fprintf(stderr, "%d sound channels are not supported\n", channels);
121 if ((rate == 44100.0 && !(caps.dwFormats & 0xf00)) ||
122 (rate == 22050.0 && !(caps.dwFormats & 0xf0)) ||
123 (rate == 11025.0 && !(caps.dwFormats & 0xf))) {
124 fprintf(stderr, "%d sample rate is not supported\n", (int)rate);
130 # endif /* defined CYGWIN */
131 #endif /* defined ECHO_TO_SOUNDCARD */
133 #ifdef HAVE_SUN_AUDIOIO_H
134 # include <sun/audioio.h>
136 #ifdef HAVE_SYS_AUDIOIO_H
137 # include <sys/audioio.h>
140 #ifdef HAVE_SYS_ASOUNDLIB_H
141 # include <sys/asoundlib.h>
142 snd_pcm_t *pcm_handle;
145 #if defined HAVE_OSS && defined SNDCTL_DSP_GETOSPACE
146 audio_buf_info abinfo;
149 int init_soundcard(double rate, int bits)
151 #ifdef ECHO_TO_SOUNDCARD
153 # if defined(HAVE_OSS) && HAVE_OSS == 1
154 if (open_snd_device() != 0) {
155 errmsg("Cannot open sound device '%s'\n", snd_device);
158 /* This the sound device initialisation for 4front Open sound drivers */
161 int garbled_rate = rate;
162 int stereo = (global.channels == 2);
163 int myformat = bits == 8 ? AFMT_U8 :
164 (MY_LITTLE_ENDIAN ? AFMT_S16_LE : AFMT_S16_BE);
167 if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_GETBLKSIZE, &dummy) == -1) {
168 fprintf(stderr, "Cannot get blocksize for %s\n", snd_device);
171 if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_SYNC, NULL) == -1) {
172 fprintf(stderr, "Cannot sync for %s\n", snd_device);
176 #if defined SNDCTL_DSP_GETOSPACE
177 if (ioctl(global.soundcard_fd, SNDCTL_DSP_GETOSPACE, &abinfo) == -1) {
178 fprintf(stderr, "Cannot get input buffersize for %s\n", snd_device);
179 abinfo.fragments = 0;
183 /* check, if the sound device can do the requested format */
184 if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_GETFMTS, &mask) == -1) {
185 perror("fatal error:");
188 if ((mask & myformat) == 0) {
189 fprintf(stderr, "sound format (%d bits signed) is not available\n", bits);
190 if ((mask & AFMT_U8) != 0) {
195 if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_SETFMT, &myformat) == -1) {
196 fprintf(stderr, "Cannot set %d bits/sample for %s\n",bits, snd_device);
200 /* limited sound devices may not support stereo */
202 && ioctl(global.soundcard_fd, (int)SNDCTL_DSP_STEREO, &stereo) == -1) {
203 fprintf(stderr, "Cannot set stereo mode for %s\n", snd_device);
207 && ioctl(global.soundcard_fd, (int)SNDCTL_DSP_STEREO, &stereo) == -1) {
208 fprintf(stderr, "Cannot set mono mode for %s\n", snd_device);
212 /* set the sample rate */
213 if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_SPEED, &garbled_rate) == -1) {
214 fprintf(stderr, "Cannot set rate %d.%2d Hz for %s\n",
215 (int)rate, (int)(rate*100)%100, snd_device);
218 if ( abs((long)rate - garbled_rate) > rate / 20) {
219 fprintf(stderr, "sound device: next best sample rate is %d\n",garbled_rate);
223 # else /* HAVE_OSS */
225 # if defined HAVE_SYS_AUDIOIO_H || defined HAVE_SUN_AUDIOIO_H
226 /* This is the SunOS / Solaris and compatibles sound initialisation */
228 if ((global.soundcard_fd = open(snd_device, O_WRONLY, 0)) == EOF) {
230 fprintf(stderr, "Cannot open %s\n",snd_device);
236 # if defined (AUDIO_INITINFO) && defined (AUDIO_ENCODING_LINEAR)
237 AUDIO_INITINFO(&info);
238 info.play.sample_rate = rate;
239 info.play.channels = global.channels;
240 info.play.precision = bits;
241 info.play.encoding = AUDIO_ENCODING_LINEAR;
243 info.record.pause = 0;
244 info.monitor_gain = 0;
245 if (ioctl(global.soundcard_fd, AUDIO_SETINFO, &info) < 0) {
246 fprintf(stderr, "Cannot init %s (sun)\n", snd_device);
250 fprintf(stderr, "Cannot init sound device with 44.1 KHz sample rate on %s (sun compatible)\n", snd_device);
254 # else /* SUN audio */
255 # if defined(__CYGWIN32__)
256 /* Windows sound info */
259 WAVEFORMATEX wavform;
261 if (waveOutGetNumDevs() < 1) {
262 fprintf( stderr, "no sound devices available!\n");
267 /* check capabilities */
268 if (check_winsound_caps(bits, rate, global.channels) != 0) {
269 fprintf( stderr, "soundcard capabilities are not sufficient!\n");
274 wavform.wFormatTag = WAVE_FORMAT_PCM;
275 wavform.nChannels = global.channels;
276 wavform.nSamplesPerSec = (int)rate;
277 wavform.wBitsPerSample = bits;
278 wavform.cbSize = sizeof(wavform);
279 wavform.nAvgBytesPerSec = (int)rate * global.channels *
280 (wavform.wBitsPerSample / 8);
281 wavform.nBlockAlign = global.channels * (wavform.wBitsPerSample / 8);
284 mmres = waveOutOpen(&DeviceID, WAVE_MAPPER, &wavform, (unsigned long)WIN_CallBack, 0, CALLBACK_FUNCTION);
288 waveOutGetErrorText(mmres, erstr, sizeof(erstr));
289 fprintf( stderr, "soundcard open error: %s!\n", erstr);
294 global.soundcard_fd = 0;
296 /* init all wavehdrs */
298 for (i=0; i < WAVEHDRS; i++) {
299 wavehdr[i].dwBufferLength = (global.channels*(bits/ 8)*(int)rate*
301 wavehdr[i].lpData = malloc(wavehdr[i].dwBufferLength);
302 if (wavehdr[i].lpData == NULL) {
303 fprintf(stderr, "no memory for sound buffers available\n");
305 waveOutClose(DeviceID);
309 mmres = waveOutPrepareHeader(DeviceID, &wavehdr[i], sizeof(WAVEHDR));
313 waveOutGetErrorText(mmres, erstr, sizeof(erstr));
314 fprintf( stderr, "soundcard prepare error: %s!\n", erstr);
318 wavehdr[i].dwLoops = 0;
319 wavehdr[i].dwFlags = WHDR_DONE;
320 wavehdr[i].dwBufferLength = 0;
325 # if defined(__EMX__)
326 # if defined (HAVE_MMPM)
327 /* OS/2 MMPM/2 MCI sound info */
329 MCI_OPEN_PARMS mciOpenParms;
332 /* create playlist */
333 for (i = 0; i < FRAGMENTS; i++) {
334 PlayList[i].ulCommand = DATA_OPERATION; /* play data */
335 PlayList[i].ulOperand1 = 0; /* address */
336 PlayList[i].ulOperand2 = 0; /* size */
337 PlayList[i].ulOperand3 = 0; /* offset */
339 PlayList[FRAGMENTS].ulCommand = BRANCH_OPERATION; /* jump */
340 PlayList[FRAGMENTS].ulOperand1 = 0;
341 PlayList[FRAGMENTS].ulOperand2 = 0; /* destination */
342 PlayList[FRAGMENTS].ulOperand3 = 0;
344 memset(&mciOpenParms, 0, sizeof(mciOpenParms));
345 mciOpenParms.pszDeviceType = (PSZ) (((unsigned long) MCI_DEVTYPE_WAVEFORM_AUDIO << 16) | (unsigned short) DeviceIndex);
346 mciOpenParms.pszElementName = (PSZ) & PlayList;
348 /* try to open the sound device */
349 if (mciSendCommand(0, MCI_OPEN,
350 MCI_WAIT | MCI_OPEN_SHAREABLE | MCIOPEN_Type_ID, &mciOpenParms, 0)
353 fprintf( stderr, "no sound devices available!\n");
357 /* try to set the parameters */
358 DeviceID = mciOpenParms.usDeviceID;
361 MCI_WAVE_SET_PARMS mciWaveSetParms;
363 memset(&mciWaveSetParms, 0, sizeof(mciWaveSetParms));
364 mciWaveSetParms.ulSamplesPerSec = rate;
365 mciWaveSetParms.usBitsPerSample = bits;
366 mciWaveSetParms.usChannels = global.channels;
367 mciWaveSetParms.ulAudio = MCI_SET_AUDIO_ALL;
369 /* set play-parameters */
370 if (mciSendCommand(DeviceID, MCI_SET,
371 MCI_WAIT | MCI_WAVE_SET_SAMPLESPERSEC |
372 MCI_WAVE_SET_BITSPERSAMPLE | MCI_WAVE_SET_CHANNELS,
373 (PVOID) & mciWaveSetParms, 0)) {
374 MCI_GENERIC_PARMS mciGenericParms;
375 fprintf( stderr, "soundcard capabilities are not sufficient!\n");
378 mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, &mciGenericParms, 0);
383 # endif /* EMX MMPM OS2 sound */
385 # if defined(__QNX__)
390 snd_pcm_channel_info_t pi;
391 snd_pcm_channel_params_t pp;
394 rtn = snd_pcm_open_preferred(&pcm_handle,
395 &card, &dev, SND_PCM_OPEN_PLAYBACK);
397 perror("sound device open");
401 rtn = snd_pcm_open(&pcm_handle,
402 card, dev, SND_PCM_OPEN_PLAYBACK);
404 perror("sound device open");
409 memset(&pi, 0, sizeof(pi));
410 pi.channel = SND_PCM_CHANNEL_PLAYBACK;
411 rtn = snd_pcm_plugin_info(pcm_handle, &pi);
413 fprintf(stderr, "snd_pcm_plugin_info failed: %s\n", snd_strerror(rtn));
417 memset(&pp, 0, sizeof(pp));
418 pp.mode = SND_PCM_MODE_BLOCK;
419 pp.channel = SND_PCM_CHANNEL_PLAYBACK;
420 pp.start_mode = SND_PCM_START_FULL;
421 pp.stop_mode = SND_PCM_STOP_STOP;
423 pp.buf.block.frag_size = pi.max_fragment_size;
424 pp.buf.block.frags_max = 1;
425 pp.buf.block.frags_min = 1;
427 pp.format.interleave = 1;
428 pp.format.rate = rate;
429 pp.format.voices = global.channels;
431 pp.format.format = SND_PCM_SFMT_U8;
433 pp.format.format = SND_PCM_SFMT_S16_LE;
436 rtn = snd_pcm_plugin_params(pcm_handle, &pp);
438 fprintf(stderr, "snd_pcm_plugin_params failed: %s\n", snd_strerror(rtn));
442 rtn = snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK);
444 fprintf(stderr, "snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rtn));
448 global.soundcard_fd = snd_pcm_file_descriptor(pcm_handle, SND_PCM_CHANNEL_PLAYBACK);
450 # endif /* QNX sound */
451 # endif /* EMX OS2 sound */
452 # endif /* CYGWIN Windows sound */
453 # endif /* else SUN audio */
454 # endif /* else HAVE_OSS */
456 #endif /* ifdef ECHO_TO_SOUNDCARD */
460 int open_snd_device()
462 #if defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
466 #if defined ECHO_TO_SOUNDCARD && !defined __CYGWIN32__ && !defined __EMX__
467 global.soundcard_fd = open(snd_device, O_WRONLY
469 /* Linux BUG: the sound driver open() blocks, if the device is in use. */
474 #if defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
475 fl = fcntl(global.soundcard_fd, F_GETFL, 0);
477 fcntl(global.soundcard_fd, F_SETFL, fl);
480 return (global.soundcard_fd < 0);
486 int close_snd_device ()
488 #if !defined ECHO_TO_SOUNDCARD
492 # if defined __CYGWIN32__
494 return waveOutClose(DeviceID);
495 # else /* !Cygwin32 */
498 # if defined HAVE_MMPM
499 /* close the sound device */
500 MCI_GENERIC_PARMS mciGenericParms;
501 mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, &mciGenericParms, 0);
502 # else /* HAVE_MMPM */
504 # endif /* HAVE_MMPM */
507 snd_pcm_plugin_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK);
508 return snd_pcm_close(pcm_handle);
510 return close(global.soundcard_fd);
513 # endif /* !Cygwin32 */
514 #endif /* ifdef ECHO_TO_SOUNDCARD */
517 int write_snd_device(char *buffer, unsigned todo)
520 #ifdef ECHO_TO_SOUNDCARD
521 #if defined __CYGWIN32__
524 wavehdr[lastwav].dwBufferLength = todo;
525 memcpy(wavehdr[lastwav].lpData, buffer, todo);
527 mmres = waveOutWrite(DeviceID, &wavehdr[lastwav], sizeof(WAVEHDR));
531 waveOutGetErrorText(mmres, erstr, sizeof(erstr));
532 fprintf( stderr, "soundcard write error: %s!\n", erstr);
535 if (++lastwav >= WAVEHDRS)
540 Playlist[BufferInd].ulOperand1 = buffer;
541 Playlist[BufferInd].ulOperand2 = todo;
542 Playlist[BufferInd].ulOperand3 = 0;
543 if (++BufferInd >= FRAGMENTS)
544 BufferInd -= FRAGMENTS;
546 /* no MCI_WAIT here, because application program has to continue */
547 memset(&mciPlayParms, 0, sizeof(mciPlayParms));
548 if (mciSendCommand(DeviceID, MCI_PLAY, MCI_FROM, &mciPlayParms, 0)) {
549 fprintf( stderr, "soundcard write error: %s!\n", erstr);
557 #if defined HAVE_OSS && defined SNDCTL_DSP_GETOSPACE
558 towrite = abinfo.fragments * abinfo.fragsize;
564 struct timeval timeout2;
568 timeout2.tv_usec = 4*120000;
571 FD_SET(global.soundcard_fd, writefds);
572 retval2 = select(global.soundcard_fd + 1,
573 NULL, writefds, NULL, &timeout2);
576 case -1: perror ("select failed");
578 case 0: /* timeout */
583 if (towrite > todo) {
586 #if defined __QNX__ && defined HAVE_SYS_ASOUNDLIB_H
587 wrote = snd_pcm_plugin_write(pcm_handle, buffer, towrite);
589 wrote = write(global.soundcard_fd, buffer, towrite);
592 perror( "cant write audio");
602 #endif /* !defined __EMX__ */
603 #endif /* !defined __CYGWIN32__ */
604 #endif /* ECHO_TO_SOUNDCARD */