Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / icedax / sndconfig.c
1 /*
2  * This file has been modified for the cdrkit suite.
3  *
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).
6  *
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.
10  *
11  */
12
13 /* @(#)sndconfig.c      1.17 04/08/03 Copyright 1998-2004 Heiko Eissfeldt */
14 /* os dependent functions */
15 #include "config.h"
16 #include <stdio.h>
17 #include <stdxlib.h>
18 #include <strdefs.h>
19 #include <fctldefs.h>
20 #include <unixstd.h>
21 #include <sys/ioctl.h>
22
23 #if     !defined __CYGWIN32__
24 # include <timedefs.h>
25 #endif
26 #include <schily.h>
27
28
29 /* soundcard setup */
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>
33 # else
34 #  if defined (HAVE_MACHINE_SOUNDCARD_H)
35 #   include <machine/soundcard.h>
36 #  else
37 #   if defined (HAVE_SYS_SOUNDCARD_H)
38 #    include <sys/soundcard.h>
39 #   else
40 #    if defined (HAVE_LINUX_SOUNDCARD_H)
41 #     include <linux/soundcard.h>
42 #    endif
43 #   endif
44 #  endif
45 # endif
46 #endif
47
48 #include "mytype.h"
49 #include "byteorder.h"
50 #include "lowlevel.h"
51 #include "global.h"
52 #include "sndconfig.h"
53
54 #ifdef  ECHO_TO_SOUNDCARD
55 #   if defined(__CYGWIN32__)
56 #      include <windows.h>
57 #      include "mmsystem.h"
58 #   endif
59
60 #   if  defined(__EMX__)
61 #      define   INCL_DOS
62 #      define   INCL_OS2MM
63 #      include  <os2.h>
64 #      define   PPFN    _PPFN
65 #      include  <os2me.h>
66 #      undef    PPFN
67 static unsigned long    DeviceID;
68
69 #      define   FRAGMENTS       2
70 /* playlist-structure */
71 typedef struct {
72         ULONG ulCommand;
73         ULONG ulOperand1, ulOperand2, ulOperand3;
74 } PLAYLISTSTRUCTURE;
75
76 static PLAYLISTSTRUCTURE PlayList[FRAGMENTS + 1];
77 static unsigned BufferInd;
78 #   endif /* defined __EMX__ */
79
80 static char snd_device[200] = SOUND_DEV;
81
82 int set_snd_device(const char *devicename)
83 {
84         strncpy(snd_device, devicename, sizeof(snd_device));
85         return 0;
86 }
87
88 #   if  defined __CYGWIN32__
89 static HWAVEOUT DeviceID;
90 #      define WAVEHDRS  3
91 static WAVEHDR  wavehdr[WAVEHDRS];
92 static unsigned lastwav = 0;
93
94 static int check_winsound_caps(int bits, double rate, int channels);
95
96 static int check_winsound_caps(int bits, double rate, int channels)
97 {
98   int result = 0;
99
100   WAVEOUTCAPS caps;
101
102   /* get caps */
103   if (waveOutGetDevCaps(0, &caps, sizeof(caps))) {
104      fprintf(stderr, "cannot get soundcard capabilities!\n");
105      return 1;
106   }
107
108   /* check caps */
109   if ((bits == 8 && !(caps.dwFormats & 0x333)) ||
110       (bits == 16 && !(caps.dwFormats & 0xccc))) {
111       fprintf(stderr, "%d bits sound are not supported\n", bits);
112       result = 2;
113   }
114
115   if ((channels == 1 && !(caps.dwFormats & 0x555)) ||
116       (channels == 2 && !(caps.dwFormats & 0xaaa))) {
117       fprintf(stderr, "%d sound channels are not supported\n", channels);
118       result = 3;
119   }
120
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);
125       result = 4;
126   }
127
128   return result;
129 }
130 #   endif /* defined CYGWIN */
131 #endif /* defined ECHO_TO_SOUNDCARD */
132
133 #ifdef  HAVE_SUN_AUDIOIO_H
134 # include <sun/audioio.h>
135 #endif
136 #ifdef  HAVE_SYS_AUDIOIO_H
137 # include <sys/audioio.h>
138 #endif
139
140 #ifdef  HAVE_SYS_ASOUNDLIB_H
141 # include <sys/asoundlib.h>
142 snd_pcm_t       *pcm_handle;
143 #endif
144
145 #if     defined HAVE_OSS && defined SNDCTL_DSP_GETOSPACE
146 audio_buf_info abinfo;
147 #endif
148
149 int init_soundcard(double rate, int bits)
150 {
151 #ifdef  ECHO_TO_SOUNDCARD
152   if (global.echo) {
153 # if    defined(HAVE_OSS) && HAVE_OSS == 1
154     if (open_snd_device() != 0) {
155         errmsg("Cannot open sound device '%s'\n", snd_device);
156         global.echo = 0;
157     } else { 
158         /* This the sound device initialisation for 4front Open sound drivers */
159
160         int dummy;
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);
165         int mask;
166
167         if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_GETBLKSIZE, &dummy) == -1) {
168             fprintf(stderr, "Cannot get blocksize for %s\n", snd_device);
169             global.echo = 0;
170         }
171         if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_SYNC, NULL) == -1) {
172             fprintf(stderr, "Cannot sync for %s\n", snd_device);
173             global.echo = 0;
174         }
175
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;
180         }
181 #endif
182
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:");
186                 return -1;
187         }
188         if ((mask & myformat) == 0) {
189                 fprintf(stderr, "sound format (%d bits signed) is not available\n", bits);
190                 if ((mask & AFMT_U8) != 0) {
191                         bits = 8;
192                         myformat = AFMT_U8;
193                 }
194         }
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);
197             global.echo = 0;
198         }
199
200         /* limited sound devices may not support stereo */
201         if (stereo
202             && ioctl(global.soundcard_fd, (int)SNDCTL_DSP_STEREO, &stereo) == -1) {
203             fprintf(stderr, "Cannot set stereo mode for %s\n", snd_device);
204             stereo = 0;
205         }
206         if (!stereo
207             && ioctl(global.soundcard_fd, (int)SNDCTL_DSP_STEREO, &stereo) == -1) {
208             fprintf(stderr, "Cannot set mono mode for %s\n", snd_device);
209             global.echo = 0;
210         }
211
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);
216             global.echo = 0;
217         }
218         if ( abs((long)rate - garbled_rate) > rate / 20) {
219             fprintf(stderr, "sound device: next best sample rate is %d\n",garbled_rate);
220         }
221     }
222
223 # else /* HAVE_OSS */
224
225 #  if defined   HAVE_SYS_AUDIOIO_H || defined HAVE_SUN_AUDIOIO_H
226         /* This is the SunOS / Solaris and compatibles sound initialisation */
227
228     if ((global.soundcard_fd = open(snd_device, O_WRONLY, 0)) == EOF) {
229         perror("");
230         fprintf(stderr, "Cannot open %s\n",snd_device);
231         global.echo = 0;
232     } else { 
233
234         audio_info_t            info;
235
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;
242         info.play.pause = 0;
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);
247             global.echo = 0;
248         }
249 #   else
250         fprintf(stderr, "Cannot init sound device with 44.1 KHz sample rate on %s (sun compatible)\n", snd_device);
251         global.echo = 0;
252 #   endif
253     }
254 #  else /* SUN audio */
255 #   if defined(__CYGWIN32__)
256     /* Windows sound info */
257
258     MMRESULT mmres;
259     WAVEFORMATEX wavform;
260
261     if (waveOutGetNumDevs() < 1) {
262         fprintf( stderr, "no sound devices available!\n");
263         global.echo = 0;
264         return 1;
265     }
266
267     /* check capabilities */
268     if (check_winsound_caps(bits, rate, global.channels) != 0) {
269         fprintf( stderr, "soundcard capabilities are not sufficient!\n");
270         global.echo = 0;
271         return 1;
272     }
273
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);
282   
283     DeviceID = 0;
284     mmres = waveOutOpen(&DeviceID, WAVE_MAPPER, &wavform, (unsigned long)WIN_CallBack, 0, CALLBACK_FUNCTION);
285     if (mmres) {
286         char erstr[329];
287
288         waveOutGetErrorText(mmres, erstr, sizeof(erstr));
289         fprintf( stderr, "soundcard open error: %s!\n", erstr);
290         global.echo = 0;
291         return 1;
292     }
293
294     global.soundcard_fd = 0;
295
296     /* init all wavehdrs */
297     { int i;
298         for (i=0; i < WAVEHDRS; i++) {
299             wavehdr[i].dwBufferLength = (global.channels*(bits/ 8)*(int)rate*
300                                 global.nsectors)/75;
301             wavehdr[i].lpData = malloc(wavehdr[i].dwBufferLength);
302             if (wavehdr[i].lpData == NULL) {
303                     fprintf(stderr, "no memory for sound buffers available\n");
304                     waveOutReset(0);
305                     waveOutClose(DeviceID);
306                     return 1;
307             }
308             
309             mmres = waveOutPrepareHeader(DeviceID, &wavehdr[i], sizeof(WAVEHDR));
310             if (mmres) {
311                 char erstr[129];
312
313                 waveOutGetErrorText(mmres, erstr, sizeof(erstr));
314                 fprintf( stderr, "soundcard prepare error: %s!\n", erstr);
315                 return 1;
316             }
317
318             wavehdr[i].dwLoops = 0;
319             wavehdr[i].dwFlags = WHDR_DONE;
320             wavehdr[i].dwBufferLength = 0;
321         }
322     }
323
324 #   else
325 #    if defined(__EMX__)
326 #       if defined (HAVE_MMPM)
327     /* OS/2 MMPM/2 MCI sound info */
328
329     MCI_OPEN_PARMS mciOpenParms;
330     int i;
331
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 */
338     }
339     PlayList[FRAGMENTS].ulCommand = BRANCH_OPERATION;       /* jump */
340     PlayList[FRAGMENTS].ulOperand1 = 0;
341     PlayList[FRAGMENTS].ulOperand2 = 0;     /* destination */
342     PlayList[FRAGMENTS].ulOperand3 = 0;
343
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;
347
348     /* try to open the sound device */
349     if (mciSendCommand(0, MCI_OPEN,
350          MCI_WAIT | MCI_OPEN_SHAREABLE | MCIOPEN_Type_ID, &mciOpenParms, 0)
351          != MCIERR_SUCCESS) {
352         /* no sound */
353         fprintf( stderr, "no sound devices available!\n");
354         global.echo = 0;
355         return 1;
356     }
357     /* try to set the parameters */
358     DeviceID = mciOpenParms.usDeviceID;
359
360     {
361         MCI_WAVE_SET_PARMS mciWaveSetParms;
362
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;
368
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");
376                 global.echo = 0;
377                 /* close */
378                 mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, &mciGenericParms, 0);
379                 return 1;
380         }
381     }
382
383 #       endif /* EMX MMPM OS2 sound */
384 #    else
385 #      if defined(__QNX__)
386
387         int     card = -1;
388         int     dev = 0;
389         int     rtn;
390         snd_pcm_channel_info_t  pi;
391         snd_pcm_channel_params_t        pp;
392
393         if (card == -1) {
394                 rtn = snd_pcm_open_preferred(&pcm_handle, 
395                         &card, &dev, SND_PCM_OPEN_PLAYBACK);
396                 if (rtn < 0) {
397                         perror("sound device open");
398                         return 1;
399                 }
400         } else {
401                 rtn = snd_pcm_open(&pcm_handle, 
402                         card, dev, SND_PCM_OPEN_PLAYBACK);
403                 if (rtn < 0) {
404                         perror("sound device open");
405                         return 1;
406                 }
407         }
408
409         memset(&pi, 0, sizeof(pi));
410         pi.channel = SND_PCM_CHANNEL_PLAYBACK;
411         rtn = snd_pcm_plugin_info(pcm_handle, &pi);
412         if (rtn < 0) {
413                 fprintf(stderr, "snd_pcm_plugin_info failed: %s\n", snd_strerror(rtn));
414                 return 1;
415         }
416
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;
422
423         pp.buf.block.frag_size = pi.max_fragment_size;
424         pp.buf.block.frags_max = 1;
425         pp.buf.block.frags_min = 1;
426
427         pp.format.interleave = 1;
428         pp.format.rate = rate;
429         pp.format.voices = global.channels;
430         if (bits == 8) {
431                 pp.format.format = SND_PCM_SFMT_U8;
432         } else {
433                 pp.format.format = SND_PCM_SFMT_S16_LE;
434         }
435
436         rtn = snd_pcm_plugin_params(pcm_handle, &pp);
437         if (rtn < 0) {
438                 fprintf(stderr, "snd_pcm_plugin_params failed: %s\n", snd_strerror(rtn));
439                 return 1;
440         }
441
442         rtn = snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK);
443         if (rtn < 0) {
444                 fprintf(stderr, "snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rtn));
445                 return 1;
446         }
447
448         global.soundcard_fd = snd_pcm_file_descriptor(pcm_handle, SND_PCM_CHANNEL_PLAYBACK);
449
450 #      endif /* QNX sound */
451 #    endif /* EMX OS2 sound */
452 #   endif /* CYGWIN Windows sound */
453 #  endif /* else SUN audio */
454 # endif /* else HAVE_OSS */
455   }
456 #endif /* ifdef ECHO_TO_SOUNDCARD */
457   return 0;
458 }
459
460 int open_snd_device()
461 {
462 #if     defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
463         int     fl;
464 #endif
465
466 #if     defined ECHO_TO_SOUNDCARD && !defined __CYGWIN32__ && !defined __EMX__
467         global.soundcard_fd = open(snd_device, O_WRONLY
468 #ifdef  linux
469                 /* Linux BUG: the sound driver open() blocks, if the device is in use. */
470                  | O_NONBLOCK
471 #endif
472                 , 0);
473
474 #if     defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
475         fl = fcntl(global.soundcard_fd, F_GETFL, 0);
476         fl &= ~O_NONBLOCK;
477         fcntl(global.soundcard_fd, F_SETFL, fl);
478 #endif
479
480         return (global.soundcard_fd < 0);
481 #else
482         return 0;
483 #endif
484 }
485
486 int close_snd_device ()
487 {
488 #if     !defined ECHO_TO_SOUNDCARD
489         return 0;
490 #else
491
492 # if    defined __CYGWIN32__
493         waveOutReset(0);
494         return waveOutClose(DeviceID);
495 # else /* !Cygwin32 */
496
497 #  if   defined __EMX__
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 */
503         return 0;
504 #   endif /* HAVE_MMPM */
505 #  else /* !EMX */
506 #   if  defined __QNX__
507         snd_pcm_plugin_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK);
508         return snd_pcm_close(pcm_handle);
509 #   else /* !QNX */
510         return close(global.soundcard_fd);
511 #   endif /* !QNX */
512 #  endif /* !EMX */
513 # endif /* !Cygwin32 */
514 #endif /* ifdef ECHO_TO_SOUNDCARD */
515 }
516
517 int write_snd_device(char *buffer, unsigned todo)
518 {
519         int result = 0;
520 #ifdef  ECHO_TO_SOUNDCARD
521 #if     defined __CYGWIN32__
522         MMRESULT mmres;
523
524         wavehdr[lastwav].dwBufferLength = todo;
525         memcpy(wavehdr[lastwav].lpData, buffer, todo);
526
527         mmres = waveOutWrite(DeviceID, &wavehdr[lastwav], sizeof(WAVEHDR));
528         if (mmres) {
529                 char erstr[129];
530
531                 waveOutGetErrorText(mmres, erstr, sizeof(erstr));
532                 fprintf( stderr, "soundcard write error: %s!\n", erstr);
533                 return 1;
534         }
535         if (++lastwav >= WAVEHDRS)
536                  lastwav -= WAVEHDRS;
537         result = mmres;
538 #else
539 #if     defined __EMX__
540         Playlist[BufferInd].ulOperand1 = buffer;
541         Playlist[BufferInd].ulOperand2 = todo;
542         Playlist[BufferInd].ulOperand3 = 0;
543         if (++BufferInd >= FRAGMENTS)
544                 BufferInd -= FRAGMENTS;
545
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);
550                 return 1;
551         }
552         result = 0;
553 #else
554         int retval2;
555         int towrite;
556
557 #if     defined HAVE_OSS && defined SNDCTL_DSP_GETOSPACE
558         towrite = abinfo.fragments * abinfo.fragsize;
559         if (towrite == 0)
560 #endif
561         towrite = todo;
562         do {
563                 fd_set writefds[1];
564                 struct timeval timeout2;
565                 int wrote;
566
567                 timeout2.tv_sec = 0;
568                 timeout2.tv_usec = 4*120000;
569
570                 FD_ZERO(writefds);
571                 FD_SET(global.soundcard_fd, writefds);
572                 retval2 = select(global.soundcard_fd + 1,
573                                  NULL, writefds, NULL, &timeout2);
574                 switch (retval2) {
575                         default:
576                         case -1: perror ("select failed");
577                         /* fall through */
578                         case 0: /* timeout */
579                                 result = 2;
580                                 goto outside_loop;
581                         case 1: break;
582                 }
583                 if (towrite > todo) {
584                         towrite = todo;
585                 }
586 #if             defined __QNX__ && defined HAVE_SYS_ASOUNDLIB_H
587                 wrote = snd_pcm_plugin_write(pcm_handle, buffer, towrite);
588 #else
589                 wrote = write(global.soundcard_fd, buffer, towrite);
590 #endif
591                 if (wrote <= 0) {
592                         perror( "cant write audio");
593                         result = 1;
594                         goto outside_loop;
595                 } else {
596                         todo -= wrote;
597                         buffer += wrote;
598                 }
599         } while (todo > 0);
600 outside_loop:
601         ;
602 #endif  /* !defined __EMX__ */
603 #endif  /* !defined __CYGWIN32__ */
604 #endif  /* ECHO_TO_SOUNDCARD */
605         return result;
606 }
607