--- /dev/null
+/* MikMod sound library
+ (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
+ complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Driver for output on Linux and FreeBSD Open Sound System (OSS) (/dev/dsp)
+
+==============================================================================*/
+
+/*
+
+ Written by Chris Conn <cconn@tohs.abacom.com>
+
+ Extended by Miodrag Vallat:
+ - compatible with all OSS/Voxware versions on Linux/i386, at least
+ - support for uLaw output (for sparc systems)
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mikmod_internals.h"
+
+#ifdef DRV_OSS
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOUNDCARD_H
+#include <sys/soundcard.h> /* Linux and newer BSD versions - OSS standart */
+#elif defined(HAVE_MACHINE_SOUNDCARD_H)
+#include <machine/soundcard.h> /* Some old BSD versions */
+#elif defined(HAVE_SOUNDCARD_H)
+#include <soundcard.h> /* Some old BSD versions and also newer OpenBSD versions */
+#endif
+
+/* Compatibility with old versions of OSS
+ (Voxware <= 2.03, Linux kernel < 1.1.31) */
+#ifndef AFMT_S16_LE
+#define AFMT_S16_LE 16
+#endif
+#ifndef AFMT_S16_BE
+#define AFMT_S16_BE 0x20
+#endif
+#ifndef AFMT_U8
+#define AFMT_U8 8
+#endif
+#ifndef SNDCTL_DSP_SETFMT
+#define SNDCTL_DSP_SETFMT SNDCTL_DSP_SAMPLESIZE
+#endif
+
+/* Compatibility with not-so-old versions of OSS
+ (OSS < 3.7, Linux kernel < 2.1.16) */
+#ifndef AFMT_S16_NE
+#if defined(_AIX) || defined(AIX) || defined(sparc) || defined(HPPA) || defined(PPC)
+#define AFMT_S16_NE AFMT_S16_BE
+#else
+#define AFMT_S16_NE AFMT_S16_LE
+#endif
+#endif
+
+/* Compatibility with OSS 4.x: AFMT_FLOAT is documented
+ as "not recommended" on the OSS website. This post
+ on the OSS mailing lists says: "In general AFMT_FLOAT
+ is not supported by OSS except in some special cases."
+ (http://sf.net/p/opensound/mailman/message/28840674/) */
+#ifndef AFMT_FLOAT
+#define AFMT_FLOAT 0x00004000
+#endif
+
+static int sndfd=-1;
+static SBYTE *audiobuffer=NULL;
+static int buffersize;
+static int play_precision;
+
+#define DEFAULT_CARD 0
+static int card=DEFAULT_CARD;
+
+#ifdef SNDCTL_DSP_SETFRAGMENT
+
+#define DEFAULT_FRAGSIZE 14
+#define DEFAULT_NUMFRAGS 16
+
+static int fragsize=DEFAULT_FRAGSIZE;
+static int numfrags=DEFAULT_NUMFRAGS;
+
+#endif
+
+static void OSS_CommandLine(const CHAR *cmdline)
+{
+ CHAR *ptr;
+
+#ifdef SNDCTL_DSP_SETFRAGMENT
+ if((ptr=MD_GetAtom("buffer",cmdline,0)) != NULL) {
+ fragsize=atoi(ptr);
+ if((fragsize<7)||(fragsize>17)) fragsize=DEFAULT_FRAGSIZE;
+ MikMod_free(ptr);
+ }
+ if((ptr=MD_GetAtom("count",cmdline,0)) != NULL) {
+ numfrags=atoi(ptr);
+ if((numfrags<2)||(numfrags>255)) numfrags=DEFAULT_NUMFRAGS;
+ MikMod_free(ptr);
+ }
+#endif
+ if((ptr=MD_GetAtom("card",cmdline,0)) != NULL) {
+ card = atoi(ptr);
+ if((card<0)||(card>99)) card=DEFAULT_CARD;
+ MikMod_free(ptr);
+ }
+}
+
+static char *OSS_GetDeviceName(void)
+{
+ static char sounddevice[20];
+
+ /* First test for devfs enabled Linux sound devices */
+ if (card)
+ sprintf(sounddevice,"/dev/sound/dsp%d",card);
+ else
+ strcpy(sounddevice,"/dev/sound/dsp");
+ if(!access(sounddevice,F_OK))
+ return sounddevice;
+
+ sprintf(sounddevice,"/dev/dsp%d",card);
+ if (!card) {
+ /* prefer /dev/dsp0 over /dev/dsp, as /dev/dsp might be a symbolic link
+ to something else than /dev/dsp0. Revert to /dev/dsp if /dev/dsp0
+ does not exist. */
+ if(access("/dev/dsp0",F_OK))
+ strcpy(sounddevice,"/dev/dsp");
+ }
+
+ return sounddevice;
+}
+
+static BOOL OSS_IsThere(void)
+{
+ /* under Linux, and perhaps other Unixes, access()ing the device is not
+ enough since it won't fail if the machine doesn't have sound support
+ in the kernel or sound hardware */
+ int fd;
+
+ if((fd=open(OSS_GetDeviceName(),O_WRONLY|O_NONBLOCK))>=0) {
+ close(fd);
+ return 1;
+ }
+ return (errno==EACCES?1:0);
+}
+
+static int OSS_Init_internal(void)
+{
+ int play_stereo,play_rate;
+ int orig_precision,orig_stereo;
+ int formats;
+#if SOUND_VERSION >= 301
+ audio_buf_info buffinf;
+#endif
+
+#ifdef SNDCTL_DSP_GETFMTS
+ /* Ask device for supported formats */
+ if(ioctl(sndfd,SNDCTL_DSP_GETFMTS,&formats)<0) {
+ _mm_errno=MMERR_OPENING_AUDIO;
+ return 1;
+ }
+#else
+ formats=AFMT_S16_NE|AFMT_U8;
+#endif
+
+ orig_precision=play_precision=(md_mode&DMODE_FLOAT)? AFMT_FLOAT :
+ (md_mode&DMODE_16BITS)? AFMT_S16_NE : AFMT_U8;
+
+ /* Device does not support the format we would prefer... */
+ if(!(formats & play_precision)) {
+ if(play_precision==AFMT_FLOAT) {
+ _mm_errno=MMERR_NO_FLOAT32;
+ return 1;
+ }
+ /* We could try 8 bit sound if available */
+ if(play_precision==AFMT_S16_NE &&(formats&AFMT_U8)) {
+ _mm_errno=MMERR_8BIT_ONLY;
+ return 1;
+ }
+#ifdef AFMT_MU_LAW
+ /* We could try uLaw if available */
+ if(formats&AFMT_MU_LAW) {
+ if((md_mode&DMODE_STEREO)||(md_mode&DMODE_16BITS)||
+ md_mixfreq!=8000) {
+ _mm_errno=MMERR_ULAW;
+ return 1;
+ } else
+ orig_precision=play_precision=AFMT_MU_LAW;
+ } else
+#endif
+ /* Otherwise, just abort */
+ {
+ _mm_errno=MMERR_OSS_SETSAMPLESIZE;
+ return 1;
+ }
+ }
+
+ if((ioctl(sndfd,SNDCTL_DSP_SETFMT,&play_precision)<0)||
+ (orig_precision!=play_precision)) {
+ _mm_errno=MMERR_OSS_SETSAMPLESIZE;
+ return 1;
+ }
+#ifdef SNDCTL_DSP_CHANNELS
+ orig_stereo=play_stereo=(md_mode&DMODE_STEREO)?2:1;
+ if((ioctl(sndfd,SNDCTL_DSP_CHANNELS,&play_stereo)<0)||
+ (orig_stereo!=play_stereo)) {
+ _mm_errno=MMERR_OSS_SETSTEREO;
+ return 1;
+ }
+#else
+ orig_stereo=play_stereo=(md_mode&DMODE_STEREO)?1:0;
+ if((ioctl(sndfd,SNDCTL_DSP_STEREO,&play_stereo)<0)||
+ (orig_stereo!=play_stereo)) {
+ _mm_errno=MMERR_OSS_SETSTEREO;
+ return 1;
+ }
+#endif
+
+ play_rate=md_mixfreq;
+ if((ioctl(sndfd,SNDCTL_DSP_SPEED,&play_rate)<0)) {
+ _mm_errno=MMERR_OSS_SETSPEED;
+ return 1;
+ }
+ md_mixfreq=play_rate;
+
+#if SOUND_VERSION >= 301
+ /* This call fails on Linux/PPC */
+ if((ioctl(sndfd,SNDCTL_DSP_GETOSPACE,&buffinf)<0))
+ ioctl(sndfd,SNDCTL_DSP_GETBLKSIZE,&buffinf.fragsize);
+ if(!(audiobuffer=(SBYTE*)MikMod_malloc(buffinf.fragsize)))
+ return 1;
+
+ buffersize = buffinf.fragsize;
+#else
+ ioctl(sndfd,SNDCTL_DSP_GETBLKSIZE,&buffersize);
+ if(!(audiobuffer=(SBYTE*)MikMod_malloc(buffersize)))
+ return 1;
+#endif
+
+ return VC_Init();
+}
+
+static int OSS_Init(void)
+{
+#ifdef SNDCTL_DSP_SETFRAGMENT
+ int fragmentsize;
+#endif
+
+ if((sndfd=open(OSS_GetDeviceName(),O_WRONLY))<0) {
+ _mm_errno=MMERR_OPENING_AUDIO;
+ return 1;
+ }
+
+#ifdef SNDCTL_DSP_SETFRAGMENT
+ if((fragsize==DEFAULT_FRAGSIZE)&&(getenv("MM_FRAGSIZE"))) {
+ fragsize=atoi(getenv("MM_FRAGSIZE"));
+ if((fragsize<7)||(fragsize>17)) fragsize=DEFAULT_FRAGSIZE;
+ }
+ if((numfrags==DEFAULT_NUMFRAGS)&&(getenv("MM_NUMFRAGS"))) {
+ numfrags=atoi(getenv("MM_NUMFRAGS"));
+ if((numfrags<2)||(numfrags>255)) numfrags=DEFAULT_NUMFRAGS;
+ }
+
+ fragmentsize=(numfrags<<16)|fragsize;
+
+ if(ioctl(sndfd,SNDCTL_DSP_SETFRAGMENT,&fragmentsize)<0) {
+ _mm_errno=MMERR_OSS_SETFRAGMENT;
+ return 1;
+ }
+#endif
+
+ return OSS_Init_internal();
+}
+
+static void OSS_Exit_internal(void)
+{
+ VC_Exit();
+ MikMod_free(audiobuffer);
+ audiobuffer = NULL;
+}
+
+static void OSS_Exit(void)
+{
+ OSS_Exit_internal();
+
+ if (sndfd>=0) {
+ close(sndfd);
+ sndfd=-1;
+ }
+}
+
+static void OSS_PlayStop(void)
+{
+ VC_PlayStop();
+
+ ioctl(sndfd,SNDCTL_DSP_POST,0);
+}
+
+static void OSS_Update(void)
+{
+ int done;
+
+#if SOUND_VERSION >= 301
+ audio_buf_info buffinf;
+
+ buffinf.fragments = 2;
+ for(;;) {
+ /* This call fails on Linux/PPC */
+ if ((ioctl(sndfd,SNDCTL_DSP_GETOSPACE,&buffinf)<0)) {
+ buffinf.fragments--;
+ buffinf.fragsize = buffinf.bytes = buffersize;
+ }
+ if(!buffinf.fragments)
+ break;
+ done=VC_WriteBytes(audiobuffer,buffinf.fragsize>buffinf.bytes?
+ buffinf.bytes:buffinf.fragsize);
+#ifdef AFMT_MU_LAW
+ if (play_precision==AFMT_MU_LAW)
+ unsignedtoulaw((char *)audiobuffer,done);
+#endif
+ write(sndfd,audiobuffer,done);
+ }
+#else
+ done=VC_WriteBytes(audiobuffer,buffersize);
+#ifdef AFMT_MU_LAW
+ if (play_precision==AFMT_MU_LAW)
+ unsignedtoulaw(audiobuffer,done);
+#endif
+ write(sndfd,audiobuffer,done);
+#endif
+}
+
+static int OSS_Reset(void)
+{
+ OSS_Exit_internal();
+ ioctl(sndfd,SNDCTL_DSP_RESET,0);
+ return OSS_Init_internal();
+}
+
+MIKMODAPI MDRIVER drv_oss={
+ NULL,
+ "Open Sound System",
+ "Open Sound System driver v1.7",
+ 0,255,
+ "oss",
+#ifdef SNDCTL_DSP_SETFRAGMENT
+ "buffer:r:7,17,14:Audio buffer log2 size\n"
+ "count:r:2,255,16:Audio buffer count\n"
+#endif
+ "card:r:0,99,0:Sound card id\n",
+ OSS_CommandLine,
+ OSS_IsThere,
+ VC_SampleLoad,
+ VC_SampleUnload,
+ VC_SampleSpace,
+ VC_SampleLength,
+ OSS_Init,
+ OSS_Exit,
+ OSS_Reset,
+ VC_SetNumVoices,
+ VC_PlayStart,
+ OSS_PlayStop,
+ OSS_Update,
+ NULL,
+ VC_VoiceSetVolume,
+ VC_VoiceGetVolume,
+ VC_VoiceSetFrequency,
+ VC_VoiceGetFrequency,
+ VC_VoiceSetPanning,
+ VC_VoiceGetPanning,
+ VC_VoicePlay,
+ VC_VoiceStop,
+ VC_VoiceStopped,
+ VC_VoiceGetPosition,
+ VC_VoiceRealVolume
+};
+
+#else
+
+MISSING(drv_oss);
+
+#endif
+
+/* ex:set ts=4: */