1 /*	MikMod sound library
2 	(c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
3 	complete list.
4 
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.
9 
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.
14 
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
18 	02111-1307, USA.
19 */
20 
21 /*==============================================================================
22 
23   $Id$
24 
25   Driver for output on Linux and FreeBSD Open Sound System (OSS) (/dev/dsp)
26 
27 ==============================================================================*/
28 
29 /*
30 
31 	Written by Chris Conn <cconn@tohs.abacom.com>
32 
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)
36 
37 */
38 
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42 
43 #include "mikmod_internals.h"
44 
45 #ifdef DRV_OSS
46 
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 #include <errno.h>
51 #ifdef HAVE_FCNTL_H
52 #include <fcntl.h>
53 #endif
54 #include <string.h>
55 #include <stdlib.h>
56 #ifdef HAVE_SYS_IOCTL_H
57 #include <sys/ioctl.h>
58 #endif
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 */
65 #endif
66 
67 /* Compatibility with old versions of OSS
68    (Voxware <= 2.03, Linux kernel < 1.1.31) */
69 #ifndef AFMT_S16_LE
70 #define AFMT_S16_LE 16
71 #endif
72 #ifndef AFMT_S16_BE
73 #define AFMT_S16_BE 0x20
74 #endif
75 #ifndef AFMT_U8
76 #define AFMT_U8 8
77 #endif
78 #ifndef SNDCTL_DSP_SETFMT
79 #define SNDCTL_DSP_SETFMT SNDCTL_DSP_SAMPLESIZE
80 #endif
81 
82 /* Compatibility with not-so-old versions of OSS
83    (OSS < 3.7, Linux kernel < 2.1.16) */
84 #ifndef AFMT_S16_NE
85 #if defined(_AIX) || defined(AIX) || defined(sparc) || defined(HPPA) || defined(PPC)
86 #define AFMT_S16_NE AFMT_S16_BE
87 #else
88 #define AFMT_S16_NE AFMT_S16_LE
89 #endif
90 #endif
91 
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/) */
97 #ifndef AFMT_FLOAT
98 #define AFMT_FLOAT 0x00004000
99 #endif
100 
101 static	int sndfd=-1;
102 static	SBYTE *audiobuffer=NULL;
103 static	int buffersize;
104 static	int play_precision;
105 
106 #define DEFAULT_CARD 0
107 static	int card=DEFAULT_CARD;
108 
109 #ifdef SNDCTL_DSP_SETFRAGMENT
110 
111 #define DEFAULT_FRAGSIZE 14
112 #define DEFAULT_NUMFRAGS 16
113 
114 static	int fragsize=DEFAULT_FRAGSIZE;
115 static	int numfrags=DEFAULT_NUMFRAGS;
116 
117 #endif
118 
OSS_CommandLine(const CHAR * cmdline)119 static void OSS_CommandLine(const CHAR *cmdline)
120 {
121 	CHAR *ptr;
122 
123 #ifdef SNDCTL_DSP_SETFRAGMENT
124 	if((ptr=MD_GetAtom("buffer",cmdline,0)) != NULL) {
125 		fragsize=atoi(ptr);
126 		if((fragsize<7)||(fragsize>17)) fragsize=DEFAULT_FRAGSIZE;
127 		MikMod_free(ptr);
128 	}
129 	if((ptr=MD_GetAtom("count",cmdline,0)) != NULL) {
130 		numfrags=atoi(ptr);
131 		if((numfrags<2)||(numfrags>255)) numfrags=DEFAULT_NUMFRAGS;
132 		MikMod_free(ptr);
133 	}
134 #endif
135 	if((ptr=MD_GetAtom("card",cmdline,0)) != NULL) {
136 		card = atoi(ptr);
137 		if((card<0)||(card>99)) card=DEFAULT_CARD;
138 		MikMod_free(ptr);
139 	}
140 }
141 
OSS_GetDeviceName(void)142 static char *OSS_GetDeviceName(void)
143 {
144 	static char sounddevice[20];
145 
146 	/* First test for devfs enabled Linux sound devices */
147 	if (card)
148 		sprintf(sounddevice,"/dev/sound/dsp%d",card);
149 	else
150 		strcpy(sounddevice,"/dev/sound/dsp");
151 	if(!access(sounddevice,F_OK))
152 		return sounddevice;
153 
154 	sprintf(sounddevice,"/dev/dsp%d",card);
155 	if (!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
158 		   does not exist. */
159 		if(access("/dev/dsp0",F_OK))
160 			strcpy(sounddevice,"/dev/dsp");
161 	}
162 
163 	return sounddevice;
164 }
165 
OSS_IsThere(void)166 static BOOL OSS_IsThere(void)
167 {
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                                      */
171 	int fd;
172 
173 	if((fd=open(OSS_GetDeviceName(),O_WRONLY|O_NONBLOCK))>=0) {
174 		close(fd);
175 		return 1;
176 	}
177 	return (errno==EACCES?1:0);
178 }
179 
OSS_Init_internal(void)180 static int OSS_Init_internal(void)
181 {
182 	int play_stereo,play_rate;
183 	int orig_precision,orig_stereo;
184 	int formats;
185 #if SOUND_VERSION >= 301
186 	audio_buf_info buffinf;
187 #endif
188 
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;
193 		return 1;
194 	}
195 #else
196 	formats=AFMT_S16_NE|AFMT_U8;
197 #endif
198 
199 	orig_precision=play_precision=(md_mode&DMODE_FLOAT)? AFMT_FLOAT :
200 					(md_mode&DMODE_16BITS)? AFMT_S16_NE : AFMT_U8;
201 
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;
206             return 1;
207         }
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;
211             return 1;
212         }
213 #ifdef AFMT_MU_LAW
214         /* We could try uLaw if available */
215         if(formats&AFMT_MU_LAW) {
216             if((md_mode&DMODE_STEREO)||(md_mode&DMODE_16BITS)||
217                 md_mixfreq!=8000) {
218                 _mm_errno=MMERR_ULAW;
219                 return 1;
220             } else
221                 orig_precision=play_precision=AFMT_MU_LAW;
222         } else
223 #endif
224             /* Otherwise, just abort */
225         {
226             _mm_errno=MMERR_OSS_SETSAMPLESIZE;
227             return 1;
228         }
229     }
230 
231 	if((ioctl(sndfd,SNDCTL_DSP_SETFMT,&play_precision)<0)||
232 	   (orig_precision!=play_precision)) {
233 		_mm_errno=MMERR_OSS_SETSAMPLESIZE;
234 		return 1;
235 	}
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;
241 		return 1;
242 	}
243 #else
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;
248 		return 1;
249 	}
250 #endif
251 
252 	play_rate=md_mixfreq;
253 	if((ioctl(sndfd,SNDCTL_DSP_SPEED,&play_rate)<0)) {
254 		_mm_errno=MMERR_OSS_SETSPEED;
255 		return 1;
256 	}
257 	md_mixfreq=play_rate;
258 
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)))
264 		return 1;
265 
266 	buffersize = buffinf.fragsize;
267 #else
268 	ioctl(sndfd,SNDCTL_DSP_GETBLKSIZE,&buffersize);
269 	if(!(audiobuffer=(SBYTE*)MikMod_malloc(buffersize)))
270 		return 1;
271 #endif
272 
273 	return VC_Init();
274 }
275 
OSS_Init(void)276 static int OSS_Init(void)
277 {
278 #ifdef SNDCTL_DSP_SETFRAGMENT
279 	int fragmentsize;
280 #endif
281 
282 	if((sndfd=open(OSS_GetDeviceName(),O_WRONLY))<0) {
283 		_mm_errno=MMERR_OPENING_AUDIO;
284 		return 1;
285 	}
286 
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;
291 	}
292 	if((numfrags==DEFAULT_NUMFRAGS)&&(getenv("MM_NUMFRAGS"))) {
293 		numfrags=atoi(getenv("MM_NUMFRAGS"));
294 		if((numfrags<2)||(numfrags>255)) numfrags=DEFAULT_NUMFRAGS;
295 	}
296 
297 	fragmentsize=(numfrags<<16)|fragsize;
298 
299 	if(ioctl(sndfd,SNDCTL_DSP_SETFRAGMENT,&fragmentsize)<0) {
300 		_mm_errno=MMERR_OSS_SETFRAGMENT;
301 		return 1;
302 	}
303 #endif
304 
305 	return OSS_Init_internal();
306 }
307 
OSS_Exit_internal(void)308 static void OSS_Exit_internal(void)
309 {
310 	VC_Exit();
311 	MikMod_free(audiobuffer);
312 	audiobuffer = NULL;
313 }
314 
OSS_Exit(void)315 static void OSS_Exit(void)
316 {
317 	OSS_Exit_internal();
318 
319 	if (sndfd>=0) {
320 		close(sndfd);
321 		sndfd=-1;
322 	}
323 }
324 
OSS_PlayStop(void)325 static void OSS_PlayStop(void)
326 {
327 	VC_PlayStop();
328 
329 	ioctl(sndfd,SNDCTL_DSP_POST,0);
330 }
331 
OSS_Update(void)332 static void OSS_Update(void)
333 {
334 	int done;
335 
336 #if SOUND_VERSION >= 301
337 	audio_buf_info buffinf;
338 
339 	buffinf.fragments = 2;
340 	for(;;) {
341 		/* This call fails on Linux/PPC */
342 		if ((ioctl(sndfd,SNDCTL_DSP_GETOSPACE,&buffinf)<0)) {
343 			buffinf.fragments--;
344 			buffinf.fragsize = buffinf.bytes = buffersize;
345 		}
346 		if(!buffinf.fragments)
347 			break;
348 		done=VC_WriteBytes(audiobuffer,buffinf.fragsize>buffinf.bytes?
349 						   buffinf.bytes:buffinf.fragsize);
350 #ifdef AFMT_MU_LAW
351 		if (play_precision==AFMT_MU_LAW)
352 			unsignedtoulaw((char *)audiobuffer,done);
353 #endif
354 		write(sndfd,audiobuffer,done);
355 	}
356 #else
357 	done=VC_WriteBytes(audiobuffer,buffersize);
358 #ifdef AFMT_MU_LAW
359 	if (play_precision==AFMT_MU_LAW)
360 		unsignedtoulaw(audiobuffer,done);
361 #endif
362 	write(sndfd,audiobuffer,done);
363 #endif
364 }
365 
OSS_Reset(void)366 static int OSS_Reset(void)
367 {
368 	OSS_Exit_internal();
369 	ioctl(sndfd,SNDCTL_DSP_RESET,0);
370 	return OSS_Init_internal();
371 }
372 
373 MIKMODAPI MDRIVER drv_oss={
374 	NULL,
375 	"Open Sound System",
376 	"Open Sound System driver v1.7",
377 	0,255,
378 	"oss",
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"
382 #endif
383 	"card:r:0,99,0:Sound card id\n",
384 	OSS_CommandLine,
385 	OSS_IsThere,
386 	VC_SampleLoad,
387 	VC_SampleUnload,
388 	VC_SampleSpace,
389 	VC_SampleLength,
390 	OSS_Init,
391 	OSS_Exit,
392 	OSS_Reset,
393 	VC_SetNumVoices,
394 	VC_PlayStart,
395 	OSS_PlayStop,
396 	OSS_Update,
397 	NULL,
398 	VC_VoiceSetVolume,
399 	VC_VoiceGetVolume,
400 	VC_VoiceSetFrequency,
401 	VC_VoiceGetFrequency,
402 	VC_VoiceSetPanning,
403 	VC_VoiceGetPanning,
404 	VC_VoicePlay,
405 	VC_VoiceStop,
406 	VC_VoiceStopped,
407 	VC_VoiceGetPosition,
408 	VC_VoiceRealVolume
409 };
410 
411 #else
412 
413 MISSING(drv_oss);
414 
415 #endif
416 
417 /* ex:set ts=4: */
418