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