/* Extended Module Player * Copyright (C) 1996-2001 Claudio Matsuoka and Hipolito Carraro Jr * * This file is part of the Extended Module Player and is distributed * under the terms of the GNU General Public License. See doc/COPYING * for more information. * * $Id: oss_mix.c,v 1.3 2001/01/15 03:01:18 claudio Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #if defined(HAVE_SYS_SOUNDCARD_H) #include #elif defined(HAVE_MACHINE_SOUNDCARD_H) #include #endif #include "driver.h" #include "mixer.h" static int audio_fd; static void from_fmt (struct xmp_control *, int); static int to_fmt (struct xmp_control *); static void setaudio (struct xmp_control *); static int init (struct xmp_control *); static void shutdown (void); static void bufdump (int); static void flush (void); static void dummy () { } static char *help[] = { "frag=num,size", "Set the number and size of fragments", "dev=", "Audio device to use (default /dev/dsp)", "nosync", "Don't flush OSS buffers between modules", #ifdef HAVE_AUDIO_BUF_INFO "voxware", "For VoxWare 2.90 (used in Linux 1.2.13)", #endif NULL }; struct xmp_drv_info drv_oss_mix = { "oss_mix", /* driver ID */ "OSS PCM audio", /* driver description */ help, /* help */ init, /* init */ shutdown, /* shutdown */ xmp_smix_numvoices, /* numvoices */ dummy, /* voicepos */ xmp_smix_echoback, /* echoback */ dummy, /* setpatch */ xmp_smix_setvol, /* setvol */ dummy, /* setnote */ xmp_smix_setpan, /* setpan */ dummy, /* setbend */ xmp_smix_seteffect, /* seteffect */ dummy, /* starttimer */ flush, /* stctlimer */ dummy, /* reset */ bufdump, /* bufdump */ dummy, /* bufwipe */ dummy, /* clearmem */ dummy, /* sync */ xmp_smix_writepatch,/* writepatch */ xmp_smix_getmsg, /* getmsg */ NULL }; static char *dev_audio = "/dev/dsp"; static int fragnum, fragsize; static int do_sync = 1; #ifdef HAVE_AUDIO_BUF_INFO static int voxware = 0; /* For Linux 1.2.13 */ #endif static int to_fmt (struct xmp_control *ctl) { int fmt; if (!ctl->resol) return AFMT_MU_LAW; if (ctl->resol == 8) fmt = AFMT_U8 | AFMT_S8; else { #ifdef WORDS_BIGENDIAN fmt = AFMT_S16_BE | AFMT_U16_BE; #else fmt = AFMT_S16_LE | AFMT_U16_LE; #endif } if (ctl->outfmt & XMP_FMT_UNS) fmt &= AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE; else fmt &= AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE; return fmt; } static void from_fmt (struct xmp_control *ctl, int outfmt) { if (outfmt & AFMT_MU_LAW) { ctl->resol = 0; return; } if (outfmt & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ctl->resol = 16; else ctl->resol = 8; if (outfmt & (AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE)) ctl->outfmt |= XMP_FMT_UNS; else ctl->outfmt &= ~XMP_FMT_UNS; if (outfmt & (AFMT_S16_BE | AFMT_U16_BE)) ctl->outfmt |= XMP_FMT_BIGEND; else ctl->outfmt &= ~XMP_FMT_BIGEND; } static void setaudio (struct xmp_control *ctl) { static int fragset = 0; int frag = 0; int fmt; frag = (fragnum << 16) + fragsize; fmt = to_fmt (ctl); ioctl (audio_fd, SNDCTL_DSP_SETFMT, &fmt); from_fmt (ctl, fmt); fmt = !(ctl->outfmt & XMP_FMT_MONO); ioctl (audio_fd, SNDCTL_DSP_STEREO, &fmt); if (fmt) ctl->outfmt &= ~XMP_FMT_MONO; else ctl->outfmt |= XMP_FMT_MONO; ioctl (audio_fd, SNDCTL_DSP_SPEED, &ctl->freq); /* Set the fragments only once */ if (!fragset) { if (fragnum && fragsize) ioctl (audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag); fragset++; } } static int init (struct xmp_control *ctl) { #ifdef HAVE_AUDIO_BUF_INFO audio_buf_info info; static char buf[80]; #endif char *token, **parm; int i = 1024; parm_init (); chkparm2 ("frag", "%d,%d", &fragnum, &i); chkparm1 ("dev", dev_audio = token); #ifdef HAVE_AUDIO_BUF_INFO chkparm1 ("voxware", voxware = 1); #endif chkparm1 ("nosync", do_sync = 0); parm_end (); for (fragsize = 0; i >>= 1; fragsize++); if (fragsize < 4) fragsize = 4; if (fragnum > 1) fragnum--; if ((audio_fd = open (dev_audio, O_WRONLY)) == -1) return XMP_ERR_DINIT; setaudio (ctl); #ifdef HAVE_AUDIO_BUF_INFO if (!voxware) { if (ioctl (audio_fd, SNDCTL_DSP_GETOSPACE, &info) == 0) { snprintf (buf, 80, "%s [%d fragments of %d bytes]", drv_oss_mix.description, info.fragstotal, info.fragsize); drv_oss_mix.description = buf; } } #endif return xmp_smix_on (ctl); } /* Build and write one tick (one PAL frame or 1/50 s in standard vblank * timed mods) of audio data to the output device. */ static void bufdump (int i) { int j; void *b; /* Doesn't work if EINTR -- reported by Ruda Moura */ /* for (; i -= write (audio_fd, xmp_smix_buffer (), i); ); */ b = xmp_smix_buffer (); while (i) { if ((j = write (audio_fd, b, i)) > 0) { i -= j; (char *)b += j; } else break; }; } static void shutdown () { xmp_smix_off (); close (audio_fd); } static void flush () { if (!do_sync) return; ioctl (audio_fd, SNDCTL_DSP_SYNC); } XMP_DRV_REGISTER(drv_oss_mix)