/* * This file has been modified for the cdrkit suite. * * The behaviour and appearence of the program code below can differ to a major * extent from the version distributed by the original author(s). * * For details, see Changelog file distributed with the cdrkit package. If you * received this file from another source then ask the distributing person for * a log of modifications. * */ /* @(#)sndconfig.c 1.17 04/08/03 Copyright 1998-2004 Heiko Eissfeldt */ /* os dependent functions */ #include "config.h" #include #include #include #include #include #include #if !defined __CYGWIN32__ # include #endif #include /* soundcard setup */ #if defined (HAVE_SOUNDCARD_H) || defined (HAVE_LINUX_SOUNDCARD_H) || defined (HAVE_SYS_SOUNDCARD_H) || defined (HAVE_MACHINE_SOUNDCARD_H) # if defined (HAVE_SOUNDCARD_H) # include # else # if defined (HAVE_MACHINE_SOUNDCARD_H) # include # else # if defined (HAVE_SYS_SOUNDCARD_H) # include # else # if defined (HAVE_LINUX_SOUNDCARD_H) # include # endif # endif # endif # endif #endif #include "mytype.h" #include "byteorder.h" #include "lowlevel.h" #include "global.h" #include "sndconfig.h" #ifdef ECHO_TO_SOUNDCARD # if defined(__CYGWIN32__) # include # include "mmsystem.h" # endif # if defined(__EMX__) # define INCL_DOS # define INCL_OS2MM # include # define PPFN _PPFN # include # undef PPFN static unsigned long DeviceID; # define FRAGMENTS 2 /* playlist-structure */ typedef struct { ULONG ulCommand; ULONG ulOperand1, ulOperand2, ulOperand3; } PLAYLISTSTRUCTURE; static PLAYLISTSTRUCTURE PlayList[FRAGMENTS + 1]; static unsigned BufferInd; # endif /* defined __EMX__ */ static char snd_device[200] = SOUND_DEV; int set_snd_device(const char *devicename) { strncpy(snd_device, devicename, sizeof(snd_device)); return 0; } # if defined __CYGWIN32__ static HWAVEOUT DeviceID; # define WAVEHDRS 3 static WAVEHDR wavehdr[WAVEHDRS]; static unsigned lastwav = 0; static int check_winsound_caps(int bits, double rate, int channels); static int check_winsound_caps(int bits, double rate, int channels) { int result = 0; WAVEOUTCAPS caps; /* get caps */ if (waveOutGetDevCaps(0, &caps, sizeof(caps))) { fprintf(stderr, "cannot get soundcard capabilities!\n"); return 1; } /* check caps */ if ((bits == 8 && !(caps.dwFormats & 0x333)) || (bits == 16 && !(caps.dwFormats & 0xccc))) { fprintf(stderr, "%d bits sound are not supported\n", bits); result = 2; } if ((channels == 1 && !(caps.dwFormats & 0x555)) || (channels == 2 && !(caps.dwFormats & 0xaaa))) { fprintf(stderr, "%d sound channels are not supported\n", channels); result = 3; } if ((rate == 44100.0 && !(caps.dwFormats & 0xf00)) || (rate == 22050.0 && !(caps.dwFormats & 0xf0)) || (rate == 11025.0 && !(caps.dwFormats & 0xf))) { fprintf(stderr, "%d sample rate is not supported\n", (int)rate); result = 4; } return result; } # endif /* defined CYGWIN */ #endif /* defined ECHO_TO_SOUNDCARD */ #ifdef HAVE_SUN_AUDIOIO_H # include #endif #ifdef HAVE_SYS_AUDIOIO_H # include #endif #ifdef HAVE_SYS_ASOUNDLIB_H # include snd_pcm_t *pcm_handle; #endif #if defined HAVE_OSS && defined SNDCTL_DSP_GETOSPACE audio_buf_info abinfo; #endif int init_soundcard(double rate, int bits) { #ifdef ECHO_TO_SOUNDCARD if (global.echo) { # if defined(HAVE_OSS) && HAVE_OSS == 1 if (open_snd_device() != 0) { errmsg("Cannot open sound device '%s'\n", snd_device); global.echo = 0; } else { /* This the sound device initialisation for 4front Open sound drivers */ int dummy; int garbled_rate = rate; int stereo = (global.channels == 2); int myformat = bits == 8 ? AFMT_U8 : (MY_LITTLE_ENDIAN ? AFMT_S16_LE : AFMT_S16_BE); int mask; if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_GETBLKSIZE, &dummy) == -1) { fprintf(stderr, "Cannot get blocksize for %s\n", snd_device); global.echo = 0; } if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_SYNC, NULL) == -1) { fprintf(stderr, "Cannot sync for %s\n", snd_device); global.echo = 0; } #if defined SNDCTL_DSP_GETOSPACE if (ioctl(global.soundcard_fd, SNDCTL_DSP_GETOSPACE, &abinfo) == -1) { fprintf(stderr, "Cannot get input buffersize for %s\n", snd_device); abinfo.fragments = 0; } #endif /* check, if the sound device can do the requested format */ if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_GETFMTS, &mask) == -1) { perror("fatal error:"); return -1; } if ((mask & myformat) == 0) { fprintf(stderr, "sound format (%d bits signed) is not available\n", bits); if ((mask & AFMT_U8) != 0) { bits = 8; myformat = AFMT_U8; } } if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_SETFMT, &myformat) == -1) { fprintf(stderr, "Cannot set %d bits/sample for %s\n",bits, snd_device); global.echo = 0; } /* limited sound devices may not support stereo */ if (stereo && ioctl(global.soundcard_fd, (int)SNDCTL_DSP_STEREO, &stereo) == -1) { fprintf(stderr, "Cannot set stereo mode for %s\n", snd_device); stereo = 0; } if (!stereo && ioctl(global.soundcard_fd, (int)SNDCTL_DSP_STEREO, &stereo) == -1) { fprintf(stderr, "Cannot set mono mode for %s\n", snd_device); global.echo = 0; } /* set the sample rate */ if (ioctl(global.soundcard_fd, (int)SNDCTL_DSP_SPEED, &garbled_rate) == -1) { fprintf(stderr, "Cannot set rate %d.%2d Hz for %s\n", (int)rate, (int)(rate*100)%100, snd_device); global.echo = 0; } if ( abs((long)rate - garbled_rate) > rate / 20) { fprintf(stderr, "sound device: next best sample rate is %d\n",garbled_rate); } } # else /* HAVE_OSS */ # if defined HAVE_SYS_AUDIOIO_H || defined HAVE_SUN_AUDIOIO_H /* This is the SunOS / Solaris and compatibles sound initialisation */ if ((global.soundcard_fd = open(snd_device, O_WRONLY, 0)) == EOF) { perror(""); fprintf(stderr, "Cannot open %s\n",snd_device); global.echo = 0; } else { audio_info_t info; # if defined (AUDIO_INITINFO) && defined (AUDIO_ENCODING_LINEAR) AUDIO_INITINFO(&info); info.play.sample_rate = rate; info.play.channels = global.channels; info.play.precision = bits; info.play.encoding = AUDIO_ENCODING_LINEAR; info.play.pause = 0; info.record.pause = 0; info.monitor_gain = 0; if (ioctl(global.soundcard_fd, AUDIO_SETINFO, &info) < 0) { fprintf(stderr, "Cannot init %s (sun)\n", snd_device); global.echo = 0; } # else fprintf(stderr, "Cannot init sound device with 44.1 KHz sample rate on %s (sun compatible)\n", snd_device); global.echo = 0; # endif } # else /* SUN audio */ # if defined(__CYGWIN32__) /* Windows sound info */ MMRESULT mmres; WAVEFORMATEX wavform; if (waveOutGetNumDevs() < 1) { fprintf( stderr, "no sound devices available!\n"); global.echo = 0; return 1; } /* check capabilities */ if (check_winsound_caps(bits, rate, global.channels) != 0) { fprintf( stderr, "soundcard capabilities are not sufficient!\n"); global.echo = 0; return 1; } wavform.wFormatTag = WAVE_FORMAT_PCM; wavform.nChannels = global.channels; wavform.nSamplesPerSec = (int)rate; wavform.wBitsPerSample = bits; wavform.cbSize = sizeof(wavform); wavform.nAvgBytesPerSec = (int)rate * global.channels * (wavform.wBitsPerSample / 8); wavform.nBlockAlign = global.channels * (wavform.wBitsPerSample / 8); DeviceID = 0; mmres = waveOutOpen(&DeviceID, WAVE_MAPPER, &wavform, (unsigned long)WIN_CallBack, 0, CALLBACK_FUNCTION); if (mmres) { char erstr[329]; waveOutGetErrorText(mmres, erstr, sizeof(erstr)); fprintf( stderr, "soundcard open error: %s!\n", erstr); global.echo = 0; return 1; } global.soundcard_fd = 0; /* init all wavehdrs */ { int i; for (i=0; i < WAVEHDRS; i++) { wavehdr[i].dwBufferLength = (global.channels*(bits/ 8)*(int)rate* global.nsectors)/75; wavehdr[i].lpData = malloc(wavehdr[i].dwBufferLength); if (wavehdr[i].lpData == NULL) { fprintf(stderr, "no memory for sound buffers available\n"); waveOutReset(0); waveOutClose(DeviceID); return 1; } mmres = waveOutPrepareHeader(DeviceID, &wavehdr[i], sizeof(WAVEHDR)); if (mmres) { char erstr[129]; waveOutGetErrorText(mmres, erstr, sizeof(erstr)); fprintf( stderr, "soundcard prepare error: %s!\n", erstr); return 1; } wavehdr[i].dwLoops = 0; wavehdr[i].dwFlags = WHDR_DONE; wavehdr[i].dwBufferLength = 0; } } # else # if defined(__EMX__) # if defined (HAVE_MMPM) /* OS/2 MMPM/2 MCI sound info */ MCI_OPEN_PARMS mciOpenParms; int i; /* create playlist */ for (i = 0; i < FRAGMENTS; i++) { PlayList[i].ulCommand = DATA_OPERATION; /* play data */ PlayList[i].ulOperand1 = 0; /* address */ PlayList[i].ulOperand2 = 0; /* size */ PlayList[i].ulOperand3 = 0; /* offset */ } PlayList[FRAGMENTS].ulCommand = BRANCH_OPERATION; /* jump */ PlayList[FRAGMENTS].ulOperand1 = 0; PlayList[FRAGMENTS].ulOperand2 = 0; /* destination */ PlayList[FRAGMENTS].ulOperand3 = 0; memset(&mciOpenParms, 0, sizeof(mciOpenParms)); mciOpenParms.pszDeviceType = (PSZ) (((unsigned long) MCI_DEVTYPE_WAVEFORM_AUDIO << 16) | (unsigned short) DeviceIndex); mciOpenParms.pszElementName = (PSZ) & PlayList; /* try to open the sound device */ if (mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_SHAREABLE | MCIOPEN_Type_ID, &mciOpenParms, 0) != MCIERR_SUCCESS) { /* no sound */ fprintf( stderr, "no sound devices available!\n"); global.echo = 0; return 1; } /* try to set the parameters */ DeviceID = mciOpenParms.usDeviceID; { MCI_WAVE_SET_PARMS mciWaveSetParms; memset(&mciWaveSetParms, 0, sizeof(mciWaveSetParms)); mciWaveSetParms.ulSamplesPerSec = rate; mciWaveSetParms.usBitsPerSample = bits; mciWaveSetParms.usChannels = global.channels; mciWaveSetParms.ulAudio = MCI_SET_AUDIO_ALL; /* set play-parameters */ if (mciSendCommand(DeviceID, MCI_SET, MCI_WAIT | MCI_WAVE_SET_SAMPLESPERSEC | MCI_WAVE_SET_BITSPERSAMPLE | MCI_WAVE_SET_CHANNELS, (PVOID) & mciWaveSetParms, 0)) { MCI_GENERIC_PARMS mciGenericParms; fprintf( stderr, "soundcard capabilities are not sufficient!\n"); global.echo = 0; /* close */ mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, &mciGenericParms, 0); return 1; } } # endif /* EMX MMPM OS2 sound */ # else # if defined(__QNX__) int card = -1; int dev = 0; int rtn; snd_pcm_channel_info_t pi; snd_pcm_channel_params_t pp; if (card == -1) { rtn = snd_pcm_open_preferred(&pcm_handle, &card, &dev, SND_PCM_OPEN_PLAYBACK); if (rtn < 0) { perror("sound device open"); return 1; } } else { rtn = snd_pcm_open(&pcm_handle, card, dev, SND_PCM_OPEN_PLAYBACK); if (rtn < 0) { perror("sound device open"); return 1; } } memset(&pi, 0, sizeof(pi)); pi.channel = SND_PCM_CHANNEL_PLAYBACK; rtn = snd_pcm_plugin_info(pcm_handle, &pi); if (rtn < 0) { fprintf(stderr, "snd_pcm_plugin_info failed: %s\n", snd_strerror(rtn)); return 1; } memset(&pp, 0, sizeof(pp)); pp.mode = SND_PCM_MODE_BLOCK; pp.channel = SND_PCM_CHANNEL_PLAYBACK; pp.start_mode = SND_PCM_START_FULL; pp.stop_mode = SND_PCM_STOP_STOP; pp.buf.block.frag_size = pi.max_fragment_size; pp.buf.block.frags_max = 1; pp.buf.block.frags_min = 1; pp.format.interleave = 1; pp.format.rate = rate; pp.format.voices = global.channels; if (bits == 8) { pp.format.format = SND_PCM_SFMT_U8; } else { pp.format.format = SND_PCM_SFMT_S16_LE; } rtn = snd_pcm_plugin_params(pcm_handle, &pp); if (rtn < 0) { fprintf(stderr, "snd_pcm_plugin_params failed: %s\n", snd_strerror(rtn)); return 1; } rtn = snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK); if (rtn < 0) { fprintf(stderr, "snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rtn)); return 1; } global.soundcard_fd = snd_pcm_file_descriptor(pcm_handle, SND_PCM_CHANNEL_PLAYBACK); # endif /* QNX sound */ # endif /* EMX OS2 sound */ # endif /* CYGWIN Windows sound */ # endif /* else SUN audio */ # endif /* else HAVE_OSS */ } #endif /* ifdef ECHO_TO_SOUNDCARD */ return 0; } int open_snd_device() { #if defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK) int fl; #endif #if defined ECHO_TO_SOUNDCARD && !defined __CYGWIN32__ && !defined __EMX__ global.soundcard_fd = open(snd_device, O_WRONLY #ifdef linux /* Linux BUG: the sound driver open() blocks, if the device is in use. */ | O_NONBLOCK #endif , 0); #if defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK) fl = fcntl(global.soundcard_fd, F_GETFL, 0); fl &= ~O_NONBLOCK; fcntl(global.soundcard_fd, F_SETFL, fl); #endif return (global.soundcard_fd < 0); #else return 0; #endif } int close_snd_device () { #if !defined ECHO_TO_SOUNDCARD return 0; #else # if defined __CYGWIN32__ waveOutReset(0); return waveOutClose(DeviceID); # else /* !Cygwin32 */ # if defined __EMX__ # if defined HAVE_MMPM /* close the sound device */ MCI_GENERIC_PARMS mciGenericParms; mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, &mciGenericParms, 0); # else /* HAVE_MMPM */ return 0; # endif /* HAVE_MMPM */ # else /* !EMX */ # if defined __QNX__ snd_pcm_plugin_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK); return snd_pcm_close(pcm_handle); # else /* !QNX */ return close(global.soundcard_fd); # endif /* !QNX */ # endif /* !EMX */ # endif /* !Cygwin32 */ #endif /* ifdef ECHO_TO_SOUNDCARD */ } int write_snd_device(char *buffer, unsigned todo) { int result = 0; #ifdef ECHO_TO_SOUNDCARD #if defined __CYGWIN32__ MMRESULT mmres; wavehdr[lastwav].dwBufferLength = todo; memcpy(wavehdr[lastwav].lpData, buffer, todo); mmres = waveOutWrite(DeviceID, &wavehdr[lastwav], sizeof(WAVEHDR)); if (mmres) { char erstr[129]; waveOutGetErrorText(mmres, erstr, sizeof(erstr)); fprintf( stderr, "soundcard write error: %s!\n", erstr); return 1; } if (++lastwav >= WAVEHDRS) lastwav -= WAVEHDRS; result = mmres; #else #if defined __EMX__ Playlist[BufferInd].ulOperand1 = buffer; Playlist[BufferInd].ulOperand2 = todo; Playlist[BufferInd].ulOperand3 = 0; if (++BufferInd >= FRAGMENTS) BufferInd -= FRAGMENTS; /* no MCI_WAIT here, because application program has to continue */ memset(&mciPlayParms, 0, sizeof(mciPlayParms)); if (mciSendCommand(DeviceID, MCI_PLAY, MCI_FROM, &mciPlayParms, 0)) { fprintf( stderr, "soundcard write error: %s!\n", erstr); return 1; } result = 0; #else int retval2; int towrite; #if defined HAVE_OSS && defined SNDCTL_DSP_GETOSPACE towrite = abinfo.fragments * abinfo.fragsize; if (towrite == 0) #endif towrite = todo; do { fd_set writefds[1]; struct timeval timeout2; int wrote; timeout2.tv_sec = 0; timeout2.tv_usec = 4*120000; FD_ZERO(writefds); FD_SET(global.soundcard_fd, writefds); retval2 = select(global.soundcard_fd + 1, NULL, writefds, NULL, &timeout2); switch (retval2) { default: case -1: perror ("select failed"); /* fall through */ case 0: /* timeout */ result = 2; goto outside_loop; case 1: break; } if (towrite > todo) { towrite = todo; } #if defined __QNX__ && defined HAVE_SYS_ASOUNDLIB_H wrote = snd_pcm_plugin_write(pcm_handle, buffer, towrite); #else wrote = write(global.soundcard_fd, buffer, towrite); #endif if (wrote <= 0) { perror( "cant write audio"); result = 1; goto outside_loop; } else { todo -= wrote; buffer += wrote; } } while (todo > 0); outside_loop: ; #endif /* !defined __EMX__ */ #endif /* !defined __CYGWIN32__ */ #endif /* ECHO_TO_SOUNDCARD */ return result; }