/* @(#)sndconfig.c 1.44 15/10/14 Copyright 1998-2004,2015 Heiko Eissfeldt, Copyright 2004-2015 J. Schilling */ #include "config.h" #ifndef lint static UConst char sccsid[] = "@(#)sndconfig.c 1.44 15/10/14 Copyright 1998-2004,2015 Heiko Eissfeldt, Copyright 2004-2015 J. Schilling"; #endif /* * os dependent functions */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * A copy of the CDDL is also available via the Internet at * http://www.opensource.org/licenses/cddl1.txt * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #if defined HAVE_SOUNDCARD_H || defined HAVE_SYS_SOUNDCARD_H || \ defined HAVE_LINUX_SOUNDCARD_H || defined HAVE_MACHINE_SOUNDCARD_H #define HAVE_OSS 1 #endif #if defined HAVE_ALSA_ASOUNDLIB_H || defined HAVE_SYS_ASOUNDLIB_H #define HAVE_ALSA 1 #undef HAVE_OSS #endif /* * OpenSolaris switched to OSS in 2008, so is it really wise to * prefer the old sound system? */ #if defined HAVE_SYS_AUDIOIO_H || defined HAVE_SUN_AUDIOIO_H #define HAVE_SUNSOUND 1 #undef HAVE_ALSA #undef HAVE_OSS #endif #if defined HAVE_WINDOWS_H && defined HAVE_MMSYSTEM_H #undef HAVE_WINSOUND #endif #if defined HAVE_OS2_H && defined HAVE_OS2ME_H #define HAVE_OS2SOUND 1 #undef HAVE_OSS #endif /* soundcard setup */ #if !defined HAVE_SUNSOUND # 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 #endif #if defined HAVE_SNDIO_H # include #undef SOUND_DEV #define SOUND_DEV SIO_DEVANY #endif #include "mytype.h" #include "byteorder.h" #include "lowlevel.h" #include "global.h" #include "sndconfig.h" #ifdef ECHO_TO_SOUNDCARD # if defined(HAVE_WINSOUND) # include # include "mmsystem.h" # endif # if defined(HAVE_OS2SOUND) # 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; ULONG ulOperand2; ULONG ulOperand3; } PLAYLISTSTRUCTURE; static PLAYLISTSTRUCTURE PlayList[FRAGMENTS + 1]; static unsigned BufferInd; # endif /* defined __EMX__ */ static char snd_device[200] = SOUND_DEV; int set_snd_device(devicename) const char *devicename; { strncpy(snd_device, devicename, sizeof (snd_device)); return (0); } # if defined(HAVE_WINSOUND) static HWAVEOUT DeviceID; # define WAVEHDRS 3 static WAVEHDR wavehdr[WAVEHDRS]; static unsigned lastwav = 0; static unsigned wavehdrinuse = 0; static HANDLE waveOutEvent; static int check_winsound_caps __PR((int bits, double rate, int channels)); static int check_winsound_caps(bits, rate, channels) int bits; double rate; int channels; { int result = 0; WAVEOUTCAPS caps; /* * get caps */ if (waveOutGetDevCaps(0, &caps, sizeof (caps))) { errmsgno(EX_BAD, _("Cannot get soundcard capabilities!\n")); return (1); } /* * check caps */ if ((bits == 8 && !(caps.dwFormats & 0x333)) || (bits == 16 && !(caps.dwFormats & 0xccc))) { errmsgno(EX_BAD, _("%d bits sound are not supported.\n"), bits); result = 2; } if ((channels == 1 && !(caps.dwFormats & 0x555)) || (channels == 2 && !(caps.dwFormats & 0xaaa))) { errmsgno(EX_BAD, _("%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))) { errmsgno(EX_BAD, _("%d sample rate is not supported.\n"), (int)rate); result = 4; } return (result); } static void CALLBACK waveOutProc __PR((HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)); static void CALLBACK waveOutProc(hwo, uMsg, dwInstance, dwParam1, dwParam2) HWAVEOUT hwo; UINT uMsg; DWORD dwInstance; DWORD dwParam1; DWORD dwParam2; { if (uMsg == WOM_DONE) { if (wavehdrinuse) { wavehdrinuse--; SetEvent(waveOutEvent); } } } # endif /* defined HAVE_WINSOUND */ #endif /* defined ECHO_TO_SOUNDCARD */ #ifdef HAVE_SUN_AUDIOIO_H # include #endif #ifdef HAVE_SYS_AUDIOIO_H # include #endif #ifdef HAVE_ALSA #ifdef HAVE_SYS_ASOUNDLIB_H # include #endif #ifdef HAVE_ALSA_ASOUNDLIB_H # include #endif snd_pcm_t *pcm_handle; unsigned frame_factor; #endif #if defined HAVE_SNDIO_H struct sio_hdl *hdl; #endif #if defined HAVE_OSS && defined SNDCTL_DSP_GETOSPACE audio_buf_info abinfo; #endif #if defined HAVE_PULSE_PULSEAUDIO_H # include #define PULSEAUDIO_SIMPLE_API 1 /*#undef PULSEAUDIO_SIMPLE_API*/ #ifdef PULSEAUDIO_SIMPLE_API # include pa_simple *ptr_pa_simple = NULL; #else pa_mainloop *ptr_pa_mainloop = NULL; pa_mainloop_api *ptr_pa_mainloop_api = NULL; pa_context *ptr_pa_context = NULL; pa_stream *ptr_pa_stream = NULL; #endif struct pa_sample_spec _pa_sample_spec; #endif int init_soundcard(rate, bits) double rate; int bits; { #ifdef ECHO_TO_SOUNDCARD if (global.echo) { #if defined HAVE_PULSE_PULSEAUDIO_H int ret = 1; if (bits != 16 && bits != 8) { error("Cannot use pulseaudio sound device with %d bits per sample\n", bits); return (1); } /* setup format */ _pa_sample_spec.format = bits == 16 ? PA_SAMPLE_S16LE : PA_SAMPLE_U8; _pa_sample_spec.channels = 2; _pa_sample_spec.rate = rate; #ifdef PULSEAUDIO_SIMPLE_API ptr_pa_simple = pa_simple_new( NULL, /* default server */ pa_locale_to_utf8("Cdda2wav"), /* application name */ PA_STREAM_PLAYBACK, /* sound transfer direction */ NULL, /* default device */ pa_locale_to_utf8("Music"), /* stream description */ &_pa_sample_spec, /* sample format */ NULL, /* default channel map */ NULL, /* default buffering attributes */ &ret); /* error code */ if (ptr_pa_simple == NULL) { #ifdef SND_DEBUG error("Cannot use pulseaudio sound daemon (no connected object): %d. Trying native sound device...\n", ret); #endif goto pa_outsimple; } #else /* get a mainloop */ ptr_pa_mainloop = pa_mainloop_new(); if (ptr_pa_mainloop == NULL) { error("Cannot use pulseaudio sound daemon (no mainloop). Trying native sound device...\n"); goto pa_out0; } ptr_pa_mainloop_api = pa_mainloop_get_api(ptr_pa_mainloop); /* get a context */ ptr_pa_context = pa_context_new( ptr_pa_mainloop_api, pa_locale_to_utf8("Cdda2wav_ctx")); if (ptr_pa_context == NULL) { error("Cannot use pulseaudio sound daemon (no context). Trying native sound device...\n"); goto pa_out1; } /* connect a context */ if (pa_context_connect( ptr_pa_context, NULL, /* default server */ 0, /* flags */ NULL) < 0 { /* spawn API */ #ifdef SND_DEBUG error("Cannot open pulseaudio sound device (no context connect). Trying native sound device...\n"); #endif goto pa_out2; } int state = 0; do { if (pa_mainloop_iterate(ptr_pa_mainloop, 1, NULL) < 0) { error( "Cannot open pulseaudio sound device (mainloop_iterate failed). Trying native sound device...\n"); goto pa_out2; } state = pa_context_get_state(ptr_pa_context); if (state != PA_CONTEXT_CONNECTING && state != PA_CONTEXT_AUTHORIZING && state != PA_CONTEXT_SETTING_NAME && state != PA_CONTEXT_READY) { error( "Cannot open pulseaudio sound device (bad context state %d). Trying native sound device...\n", state); goto pa_out2; } } while (state != PA_CONTEXT_READY); pa_channel_map pacmap; pa_channel_map_init_auto(&pacmap, _pa_sample_spec.channels, PA_CHANNEL_MAP_WAVEEX); ptr_pa_stream = pa_stream_new( ptr_pa_context, pa_locale_to_utf8("Cdda2wav"), &_pa_sample_spec, &pacmap); if (ptr_pa_stream == NULL) { error("Cannot use pulseaudio sound daemon (no stream). Trying native sound device...\n"); goto pa_out2; } if (pa_stream_connect_playback( ptr_pa_stream, NULL, NULL, 0, NULL, NULL) < 0) { error("Cannot use pulseaudio sound daemon (no connect). Trying native sound device...\n"); goto pa_out2; } state = 0; do { if (pa_mainloop_iterate(ptr_pa_mainloop, 1, NULL) < 0) { error( "Cannot open pulseaudio sound device (mainloop_iterate failed). Trying native sound device...\n"); goto pa_out2; } state = pa_stream_get_state(ptr_pa_stream); if (state != PA_STREAM_CREATING && state != PA_STREAM_READY) { error( "Cannot open pulseaudio sound device (bad stream state %d). Trying native sound device...\n", state); goto pa_out2; } } while (state != PA_STREAM_READY); return (0); /* if unsuccessful, fallback to native sound API */ /* clean up */ pa_out2: if (ptr_pa_context) pa_context_unref(ptr_pa_context); pa_out1: if (ptr_pa_mainloop) pa_mainloop_free(ptr_pa_mainloop); pa_out0: ptr_pa_stream = NULL; ptr_pa_context = NULL; ptr_pa_mainloop = NULL; ptr_pa_mainloop_api = NULL; #endif pa_outsimple: ; #endif # if defined(HAVE_SNDIO_H) struct sio_par par; hdl = sio_open(snd_device, SIO_PLAY, 0); if (hdl == NULL) { errmsg("Cannot open sndio sound device '%s'.\n", snd_device); return (1); } sio_initpar(&par); par.bits = bits; par.sig = 1; par.le = SIO_LE_NATIVE; par.pchan = 2; par.rate = rate; par.appbufsz = 44100 / 4; /* 61152 */ if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par)) { errmsg("Cannot set sound parameters for '%s'.\n", snd_device); sio_close(hdl); hdl = NULL; return (1); } if (par.bits != bits || par.sig != 1 || par.le != SIO_LE_NATIVE || par.pchan != 2 || par.rate != (int)rate) { errmsg("Unsupported sound parameters for '%s'.\n", snd_device); sio_close(hdl); hdl = NULL; return (1); } if (!sio_start(hdl)) { errmsg("Couldn't start sound device '%s'.\n", snd_device); sio_close(hdl); hdl = NULL; return (1); } # else # if defined(HAVE_OSS) && HAVE_OSS == 1 if (open_snd_device() != 0) { errmsg(_("Cannot open oss 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, SNDCTL_DSP_GETBLKSIZE, &dummy) == -1) { errmsg(_("Cannot get blocksize for %s.\n"), snd_device); global.echo = 0; } if (ioctl(global.soundcard_fd, SNDCTL_DSP_SYNC, NULL) == -1) { errmsg(_("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) { errmsg(_("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, SNDCTL_DSP_GETFMTS, &mask) == -1) { errmsg(_("Fatal error in ioctl(SNDCTL_DSP_GETFMTS).\n")); return (-1); } if ((mask & myformat) == 0) { errmsgno(EX_BAD, _("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, SNDCTL_DSP_SETFMT, &myformat) == -1) { errmsg(_("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, SNDCTL_DSP_STEREO, &stereo) == -1) { errmsg(_("Cannot set stereo mode for %s.\n"), snd_device); stereo = 0; } if (!stereo && ioctl(global.soundcard_fd, SNDCTL_DSP_STEREO, &stereo) == -1) { errmsg(_("Cannot set mono mode for %s.\n"), snd_device); global.echo = 0; } /* * set the sample rate */ if (ioctl(global.soundcard_fd, SNDCTL_DSP_SPEED, &garbled_rate) == -1) { errmsg(_("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) { errmsgno(EX_BAD, _("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 / NetBSD * sound initialisation */ if ((global.soundcard_fd = open(snd_device, O_WRONLY, 0)) == EOF) { errmsg(_("Cannot open '%s'.\n"), snd_device); global.echo = 0; } else { # if defined(AUDIO_INITINFO) && defined(AUDIO_ENCODING_LINEAR) audio_info_t info; 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) { errmsg(_("Cannot init %s (sun).\n"), snd_device); global.echo = 0; } # else errmsgno(EX_BAD, _("Cannot init sound device with %u.%u kHz sample rate on %s (sun compatible).\n"), rate / 1000, (rate % 1000) / 100, snd_device); global.echo = 0; # endif } # else /* SUN audio */ # if defined(HAVE_WINSOUND) /* * Windows sound info */ MMRESULT mmres; WAVEFORMATEX wavform; if (waveOutGetNumDevs() < 1) { errmsgno(EX_BAD, _("No sound devices available!\n")); global.echo = 0; return (1); } /* * check capabilities */ if (check_winsound_caps(bits, rate, global.channels) != 0) { errmsgno(EX_BAD, _("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); waveOutEvent = CreateEvent(NULL, TRUE, FALSE, NULL); DeviceID = 0; mmres = waveOutOpen(&DeviceID, WAVE_MAPPER, &wavform, (uint32_t)waveOutProc, 0, CALLBACK_FUNCTION); if (mmres) { char erstr[329]; waveOutGetErrorText(mmres, erstr, sizeof (erstr)); errmsgno(EX_BAD, _("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) { errmsg( _("No memory for sound buffers available.\n")); waveOutReset(0); CloseHandle(waveOutEvent); waveOutClose(DeviceID); return (1); } mmres = waveOutPrepareHeader(DeviceID, &wavehdr[i], sizeof (WAVEHDR)); if (mmres) { char erstr[129]; waveOutGetErrorText(mmres, erstr, sizeof (erstr)); errmsgno(EX_BAD, _("soundcard prepare error: %s!\n"), erstr); return (1); } wavehdr[i].dwLoops = 0; wavehdr[i].dwFlags = WHDR_DONE; wavehdr[i].dwBufferLength = 0; } } # else # if defined(HAVE_OS2SOUND) # 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 */ errmsgno(EX_BAD, _("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; errmsgno(EX_BAD, _("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(HAVE_ALSA) int card = -1; int rtn; /*error("setting ALSA device: '%s'.\n", snd_device);*/ rtn = snd_pcm_open(&pcm_handle, snd_device, SND_PCM_STREAM_PLAYBACK, 0); if (rtn < 0) { errmsg(_("Error opening ALSA sound device (%s).\n"), snd_strerror(rtn)); return (1); } if ((rtn = snd_pcm_set_params( pcm_handle, bits == 8 ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, global.channels, rate, 1, 500000)) < 0) { error("Error setting ALSA parameters: %s.\n", snd_strerror(rtn)); return (1); } frame_factor = ((bits == 8 ? 1 : 2) * global.channels); # endif /* QNX sound */ # endif /* EMX OS2 sound */ # endif /* CYGWIN Windows sound */ # endif /* else SUN audio */ # endif /* else HAVE_OSS */ # endif /* else HAVE_SNDIO_H */ } #endif /* ifdef ECHO_TO_SOUNDCARD */ return (0); } int open_snd_device() { #if defined ECHO_TO_SOUNDCARD && !defined HAVE_WINSOUND && !defined HAVE_OS2SOUND #if defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK) int fl; #endif global.soundcard_fd = open(snd_device, #ifdef linux /* * Linux BUG: the sound driver open() blocks, * if the device is in use. */ O_NONBLOCK | #endif O_WRONLY, 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 /* def ECHO_TO_SOUNDCARD && !def __CYGWIN32__ && !def __CYGWIN32__ && !def __MINGW32__ && !def __EMX__ */ return (0); #endif } int close_snd_device() { #if !defined ECHO_TO_SOUNDCARD return (0); #else #if defined HAVE_PULSE_PULSEAUDIO_H #ifdef PULSEAUDIO_SIMPLE_API if (ptr_pa_simple) { int ret = 0; if (pa_simple_drain(ptr_pa_simple, &ret) < 0) { errmsg(_("Soundcard pulse audio drain error: %d!\n"), ret); } pa_simple_free(ptr_pa_simple); ptr_pa_simple = NULL; return (0); } #else if (ptr_pa_stream) { pa_operation * ptr_op = pa_stream_drain( ptr_pa_stream, /* stream */ NULL, /* callback */ NULL); /* user data */ if (ptr_op == NULL) { errmsg(_("Soundcard pulse audio drain error!\n")); } while (pa_operation_get_state(ptr_op) != PA_OPERATION_DONE) { if (pa_context_get_state(ptr_pa_context) != PA_CONTEXT_READY || pa_stream_get_state(ptr_pa_stream) != PA_STREAM_READY || pa_mainloop_iterate(ptr_pa_mainloop, 1, NULL) < 0) { pa_operation_cancel(ptr_op); break; } } pa_operation_unref(ptr_op); pa_stream_disconnect(ptr_pa_stream); pa_stream_unref(ptr_pa_stream); if (ptr_pa_context) pa_context_unref(ptr_pa_context); if (ptr_pa_mainloop) { pa_signal_done(); pa_mainloop_free(ptr_pa_mainloop); } ptr_pa_stream = NULL; ptr_pa_context = NULL; ptr_pa_mainloop = NULL; ptr_pa_mainloop_api = NULL; return (0); } #endif /* FALLTHROUGH to native API */ #endif /* !HAVE_PULSE_PULSEAUDIO_H */ # if defined(HAVE_WINSOUND) waveOutReset(0); CloseHandle(waveOutEvent); return (waveOutClose(DeviceID)); # else /* !Cygwin32 */ # if defined HAVE_OS2SOUND # 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 HAVE_ALSA snd_pcm_drain(pcm_handle); return (snd_pcm_close(pcm_handle)); # else /* !ALSA */ # if defined HAVE_SNDIO_H if (hdl != NULL) { sio_close(hdl); hdl = NULL; } return (0); # else return (close(global.soundcard_fd)); # endif /* !HAVE_SNDIO_H */ # endif /* !QNX */ # endif /* !EMX */ # endif /* !Cygwin32 */ #endif /* ifdef ECHO_TO_SOUNDCARD */ } int write_snd_device(buffer, todo) char *buffer; unsigned todo; { int result = 0; #ifdef ECHO_TO_SOUNDCARD #if defined HAVE_PULSE_PULSEAUDIO_H #ifdef PULSEAUDIO_SIMPLE_API if (ptr_pa_simple) { int ret = pa_simple_write( ptr_pa_simple, /* object */ buffer, /* sample data */ todo, /* size in bytes */ &result); /* error code */ if (ret < 0) { errmsgno(EX_BAD, _("Soundcard write error (pulseaudio): %d, %s!\n"), result, pa_strerror(result)); } return (0); } #else if (ptr_pa_stream) { result = pa_stream_write( ptr_pa_stream, /* stream */ buffer, /* sample data */ todo, /* size in bytes */ NULL, /* callback to free() data */ 0LL, /* seek offset */ PA_SEEK_RELATIVE); /* seek mode */ if (result < 0) { errmsgno(EX_BAD, _("Soundcard write error (pulseaudio): %d, %s!\n"), result, pa_strerror(result)); } return (result); } #endif /* FALLTHROUGH to native transport */ #endif /* defined HAVE_PULSE_PULSEAUDIO_H */ #if defined HAVE_SNDIO_H if (hdl == NULL || !sio_write(hdl, buffer, todo)) { errmsgno(EX_BAD, _("Soundcard write error (sndio)!\n")); return (1); } #else #if defined(HAVE_WINSOUND) MMRESULT mmres; wavehdr[lastwav].dwBufferLength = todo; memcpy(wavehdr[lastwav].lpData, buffer, todo); while (wavehdrinuse >= WAVEHDRS) { WaitForSingleObject(waveOutEvent, INFINITE); ResetEvent(waveOutEvent); } wavehdrinuse++; mmres = waveOutWrite(DeviceID, &wavehdr[lastwav], sizeof (WAVEHDR)); if (mmres) { char erstr[129]; waveOutGetErrorText(mmres, erstr, sizeof (erstr)); errmsgno(EX_BAD, _("Soundcard write error (cygwin): %s!\n"), erstr); return (1); } if (++lastwav >= WAVEHDRS) lastwav -= WAVEHDRS; result = mmres; #else #if defined HAVE_OS2SOUND 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)) { errmsgno(EX_BAD, _("Soundcard write error (os/2): %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 { int wrote; #ifdef HAVE_SELECT fd_set writefds[1]; struct timeval timeout2; 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: errmsg(_("Select failed.\n")); /* FALLTHROUGH */ case 0: /* timeout */ result = 2; goto outside_loop; case 1: break; } #endif /* HAVE_SELECT */ if (towrite > todo) { towrite = todo; } #if defined HAVE_ALSA { snd_pcm_sframes_t frames = towrite / frame_factor; wrote = snd_pcm_writei(pcm_handle, buffer, frames); if (wrote < 0) { wrote = snd_pcm_recover(pcm_handle, wrote, 0); } wrote *= frame_factor; } #else wrote = write(global.soundcard_fd, buffer, towrite); #endif if (wrote <= 0) { #if defined HAVE_ALSA errmsg(_("Can't write audio (alsa).\n")); #else errmsg(_("Can't write audio (oss).\n")); #endif result = 1; goto outside_loop; } else { todo -= wrote; buffer += wrote; } } while (todo > 0); outside_loop: ; #endif /* !defined __EMX__ */ #endif /* !defined __CYGWIN32__ */ #endif /* !defined HAVE_SNDIO_H */ #endif /* ECHO_TO_SOUNDCARD */ return (result); }