1 /* MikMod sound library
2 (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 /*==============================================================================
25 Driver for output on Linux and FreeBSD Open Sound System (OSS) (/dev/dsp)
27 ==============================================================================*/
31 Written by Chris Conn <cconn@tohs.abacom.com>
33 Extended by Miodrag Vallat:
34 - compatible with all OSS/Voxware versions on Linux/i386, at least
35 - support for uLaw output (for sparc systems)
43 #include "mikmod_internals.h"
56 #ifdef HAVE_SYS_IOCTL_H
57 #include <sys/ioctl.h>
59 #ifdef HAVE_SYS_SOUNDCARD_H
60 #include <sys/soundcard.h> /* Linux and newer BSD versions - OSS standart */
61 #elif defined(HAVE_MACHINE_SOUNDCARD_H)
62 #include <machine/soundcard.h> /* Some old BSD versions */
63 #elif defined(HAVE_SOUNDCARD_H)
64 #include <soundcard.h> /* Some old BSD versions and also newer OpenBSD versions */
67 /* Compatibility with old versions of OSS
68 (Voxware <= 2.03, Linux kernel < 1.1.31) */
70 #define AFMT_S16_LE 16
73 #define AFMT_S16_BE 0x20
78 #ifndef SNDCTL_DSP_SETFMT
79 #define SNDCTL_DSP_SETFMT SNDCTL_DSP_SAMPLESIZE
82 /* Compatibility with not-so-old versions of OSS
83 (OSS < 3.7, Linux kernel < 2.1.16) */
85 #if defined(_AIX) || defined(AIX) || defined(sparc) || defined(HPPA) || defined(PPC)
86 #define AFMT_S16_NE AFMT_S16_BE
88 #define AFMT_S16_NE AFMT_S16_LE
92 /* Compatibility with OSS 4.x: AFMT_FLOAT is documented
93 as "not recommended" on the OSS website. This post
94 on the OSS mailing lists says: "In general AFMT_FLOAT
95 is not supported by OSS except in some special cases."
96 (http://sf.net/p/opensound/mailman/message/28840674/) */
98 #define AFMT_FLOAT 0x00004000
102 static SBYTE *audiobuffer=NULL;
103 static int buffersize;
104 static int play_precision;
106 #define DEFAULT_CARD 0
107 static int card=DEFAULT_CARD;
109 #ifdef SNDCTL_DSP_SETFRAGMENT
111 #define DEFAULT_FRAGSIZE 14
112 #define DEFAULT_NUMFRAGS 16
114 static int fragsize=DEFAULT_FRAGSIZE;
115 static int numfrags=DEFAULT_NUMFRAGS;
119 static void OSS_CommandLine(const CHAR *cmdline)
123 #ifdef SNDCTL_DSP_SETFRAGMENT
124 if((ptr=MD_GetAtom("buffer",cmdline,0)) != NULL) {
126 if((fragsize<7)||(fragsize>17)) fragsize=DEFAULT_FRAGSIZE;
129 if((ptr=MD_GetAtom("count",cmdline,0)) != NULL) {
131 if((numfrags<2)||(numfrags>255)) numfrags=DEFAULT_NUMFRAGS;
135 if((ptr=MD_GetAtom("card",cmdline,0)) != NULL) {
137 if((card<0)||(card>99)) card=DEFAULT_CARD;
142 static char *OSS_GetDeviceName(void)
144 static char sounddevice[20];
146 /* First test for devfs enabled Linux sound devices */
148 sprintf(sounddevice,"/dev/sound/dsp%d",card);
150 strcpy(sounddevice,"/dev/sound/dsp");
151 if(!access(sounddevice,F_OK))
154 sprintf(sounddevice,"/dev/dsp%d",card);
156 /* prefer /dev/dsp0 over /dev/dsp, as /dev/dsp might be a symbolic link
157 to something else than /dev/dsp0. Revert to /dev/dsp if /dev/dsp0
159 if(access("/dev/dsp0",F_OK))
160 strcpy(sounddevice,"/dev/dsp");
166 static BOOL OSS_IsThere(void)
168 /* under Linux, and perhaps other Unixes, access()ing the device is not
169 enough since it won't fail if the machine doesn't have sound support
170 in the kernel or sound hardware */
173 if((fd=open(OSS_GetDeviceName(),O_WRONLY|O_NONBLOCK))>=0) {
177 return (errno==EACCES?1:0);
180 static int OSS_Init_internal(void)
182 int play_stereo,play_rate;
183 int orig_precision,orig_stereo;
185 #if SOUND_VERSION >= 301
186 audio_buf_info buffinf;
189 #ifdef SNDCTL_DSP_GETFMTS
190 /* Ask device for supported formats */
191 if(ioctl(sndfd,SNDCTL_DSP_GETFMTS,&formats)<0) {
192 _mm_errno=MMERR_OPENING_AUDIO;
196 formats=AFMT_S16_NE|AFMT_U8;
199 orig_precision=play_precision=(md_mode&DMODE_FLOAT)? AFMT_FLOAT :
200 (md_mode&DMODE_16BITS)? AFMT_S16_NE : AFMT_U8;
202 /* Device does not support the format we would prefer... */
203 if(!(formats & play_precision)) {
204 if(play_precision==AFMT_FLOAT) {
205 _mm_errno=MMERR_NO_FLOAT32;
208 /* We could try 8 bit sound if available */
209 if(play_precision==AFMT_S16_NE &&(formats&AFMT_U8)) {
210 _mm_errno=MMERR_8BIT_ONLY;
214 /* We could try uLaw if available */
215 if(formats&AFMT_MU_LAW) {
216 if((md_mode&DMODE_STEREO)||(md_mode&DMODE_16BITS)||
218 _mm_errno=MMERR_ULAW;
221 orig_precision=play_precision=AFMT_MU_LAW;
224 /* Otherwise, just abort */
226 _mm_errno=MMERR_OSS_SETSAMPLESIZE;
231 if((ioctl(sndfd,SNDCTL_DSP_SETFMT,&play_precision)<0)||
232 (orig_precision!=play_precision)) {
233 _mm_errno=MMERR_OSS_SETSAMPLESIZE;
236 #ifdef SNDCTL_DSP_CHANNELS
237 orig_stereo=play_stereo=(md_mode&DMODE_STEREO)?2:1;
238 if((ioctl(sndfd,SNDCTL_DSP_CHANNELS,&play_stereo)<0)||
239 (orig_stereo!=play_stereo)) {
240 _mm_errno=MMERR_OSS_SETSTEREO;
244 orig_stereo=play_stereo=(md_mode&DMODE_STEREO)?1:0;
245 if((ioctl(sndfd,SNDCTL_DSP_STEREO,&play_stereo)<0)||
246 (orig_stereo!=play_stereo)) {
247 _mm_errno=MMERR_OSS_SETSTEREO;
252 play_rate=md_mixfreq;
253 if((ioctl(sndfd,SNDCTL_DSP_SPEED,&play_rate)<0)) {
254 _mm_errno=MMERR_OSS_SETSPEED;
257 md_mixfreq=play_rate;
259 #if SOUND_VERSION >= 301
260 /* This call fails on Linux/PPC */
261 if((ioctl(sndfd,SNDCTL_DSP_GETOSPACE,&buffinf)<0))
262 ioctl(sndfd,SNDCTL_DSP_GETBLKSIZE,&buffinf.fragsize);
263 if(!(audiobuffer=(SBYTE*)MikMod_malloc(buffinf.fragsize)))
266 buffersize = buffinf.fragsize;
268 ioctl(sndfd,SNDCTL_DSP_GETBLKSIZE,&buffersize);
269 if(!(audiobuffer=(SBYTE*)MikMod_malloc(buffersize)))
276 static int OSS_Init(void)
278 #ifdef SNDCTL_DSP_SETFRAGMENT
282 if((sndfd=open(OSS_GetDeviceName(),O_WRONLY))<0) {
283 _mm_errno=MMERR_OPENING_AUDIO;
287 #ifdef SNDCTL_DSP_SETFRAGMENT
288 if((fragsize==DEFAULT_FRAGSIZE)&&(getenv("MM_FRAGSIZE"))) {
289 fragsize=atoi(getenv("MM_FRAGSIZE"));
290 if((fragsize<7)||(fragsize>17)) fragsize=DEFAULT_FRAGSIZE;
292 if((numfrags==DEFAULT_NUMFRAGS)&&(getenv("MM_NUMFRAGS"))) {
293 numfrags=atoi(getenv("MM_NUMFRAGS"));
294 if((numfrags<2)||(numfrags>255)) numfrags=DEFAULT_NUMFRAGS;
297 fragmentsize=(numfrags<<16)|fragsize;
299 if(ioctl(sndfd,SNDCTL_DSP_SETFRAGMENT,&fragmentsize)<0) {
300 _mm_errno=MMERR_OSS_SETFRAGMENT;
305 return OSS_Init_internal();
308 static void OSS_Exit_internal(void)
311 MikMod_free(audiobuffer);
315 static void OSS_Exit(void)
325 static void OSS_PlayStop(void)
329 ioctl(sndfd,SNDCTL_DSP_POST,0);
332 static void OSS_Update(void)
336 #if SOUND_VERSION >= 301
337 audio_buf_info buffinf;
339 buffinf.fragments = 2;
341 /* This call fails on Linux/PPC */
342 if ((ioctl(sndfd,SNDCTL_DSP_GETOSPACE,&buffinf)<0)) {
344 buffinf.fragsize = buffinf.bytes = buffersize;
346 if(!buffinf.fragments)
348 done=VC_WriteBytes(audiobuffer,buffinf.fragsize>buffinf.bytes?
349 buffinf.bytes:buffinf.fragsize);
351 if (play_precision==AFMT_MU_LAW)
352 unsignedtoulaw((char *)audiobuffer,done);
354 write(sndfd,audiobuffer,done);
357 done=VC_WriteBytes(audiobuffer,buffersize);
359 if (play_precision==AFMT_MU_LAW)
360 unsignedtoulaw(audiobuffer,done);
362 write(sndfd,audiobuffer,done);
366 static int OSS_Reset(void)
369 ioctl(sndfd,SNDCTL_DSP_RESET,0);
370 return OSS_Init_internal();
373 MIKMODAPI MDRIVER drv_oss={
376 "Open Sound System driver v1.7",
379 #ifdef SNDCTL_DSP_SETFRAGMENT
380 "buffer:r:7,17,14:Audio buffer log2 size\n"
381 "count:r:2,255,16:Audio buffer count\n"
383 "card:r:0,99,0:Sound card id\n",
400 VC_VoiceSetFrequency,
401 VC_VoiceGetFrequency,