1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19 
20 //
21 // unix_snd_oss.c
22 //
23 
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <sys/ioctl.h>
29 #include <sys/mman.h>
30 #include <sys/shm.h>
31 #include <sys/wait.h>
32 #ifdef __linux__
33 #include <linux/soundcard.h>
34 #else
35 #include <sys/soundcard.h>
36 #endif
37 #include <stdio.h>
38 
39 #include "../client/snd_local.h"
40 #include "unix_local.h"
41 
42 static cVar_t	*s_oss_device;
43 
44 static int		oss_audioFD = -1;
45 static char		*oss_curDevice;
46 static qBool	oss_initialized;
47 static int		oss_tryRates[] = {  48000, 44100, 22050, 11025, 8000 };
48 
49 /*
50 ==============
51 OSS_Init
52 ===============
53 */
OSS_Init(void)54 qBool OSS_Init (void)
55 {
56 	int				rc;
57     int				fmt;
58 	int				tmp;
59     int				i;
60     char			*s;
61 	struct audio_buf_info info;
62 	int				caps;
63 	extern uid_t	saved_euid;
64 
65 	// Register variables
66 	if (!s_oss_device) {
67 		s_oss_device	= Cvar_Register ("s_oss_device",	"default",				CVAR_ARCHIVE);
68 	}
69 
70 	if (oss_initialized)
71 		return qTrue;	// FIXME: can't snd_restart right now...
72 
73 	// Find the target device
74 	if (oss_curDevice)
75 		Mem_Free (oss_curDevice);
76 	if (!Q_stricmp (s_oss_device->string, "default"))
77 		oss_curDevice = Mem_PoolStrDup ("/dev/dsp", cl_soundSysPool, 0);
78 	else
79 		oss_curDevice = Mem_PoolStrDup (s_oss_device->string, cl_soundSysPool, 0);
80 
81 	// Open /dev/dsp, confirm capability to mmap, and get size of dma buffer
82 	if (oss_audioFD == -1) {
83 		seteuid (saved_euid);
84 
85 		oss_audioFD = open (oss_curDevice, O_RDWR);
86 
87 		seteuid (getuid ());
88 
89 		if (oss_audioFD < 0) {
90 			perror (oss_curDevice);
91 			Com_Printf (PRNT_ERROR, "Could not open %s\n", oss_curDevice);
92 			return qFalse;
93 		}
94 	}
95 
96     rc = ioctl (oss_audioFD, SNDCTL_DSP_RESET, 0);
97     if (rc < 0) {
98 		perror (oss_curDevice);
99 		Com_Printf (PRNT_ERROR, "Could not reset %s\n", oss_curDevice);
100 		close (oss_audioFD);
101 		return qFalse;
102 	}
103 
104 	if (ioctl (oss_audioFD, SNDCTL_DSP_GETCAPS, &caps) == -1) {
105 		perror (oss_curDevice);
106         Com_Printf (PRNT_ERROR, "Sound driver too old\n");
107 		close (oss_audioFD);
108 		return qFalse;
109 	}
110 
111 	if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) {
112 		Com_Printf (PRNT_ERROR, "Sorry but your soundcard can't do this\n");
113 		close (oss_audioFD);
114 		return qFalse;
115 	}
116 
117 	// Set sample bits & speed
118     snd_audioDMA.sampleBits = s_bits->intVal;
119 	if (snd_audioDMA.sampleBits != 16 && snd_audioDMA.sampleBits != 8) {
120         ioctl (oss_audioFD, SNDCTL_DSP_GETFMTS, &fmt);
121         if (fmt & AFMT_S16_LE)
122 			snd_audioDMA.sampleBits = 16;
123         else if (fmt & AFMT_U8)
124 			snd_audioDMA.sampleBits = 8;
125     }
126 
127     snd_audioDMA.speed = s_speed->intVal;
128 	switch (s_khz->intVal) {
129 	case 48:	snd_audioDMA.speed = 48000;	break;
130 	case 44:	snd_audioDMA.speed = 44100;	break;
131 	case 22:	snd_audioDMA.speed = 22050;	break;
132 	default:	snd_audioDMA.speed = 11025;	break;
133 	}
134     if (!snd_audioDMA.speed) {
135         for (i=0 ; i<sizeof(oss_tryRates)/4 ; i++)
136             if (!ioctl (oss_audioFD, SNDCTL_DSP_SPEED, &oss_tryRates[i])) break;
137         snd_audioDMA.speed = oss_tryRates[i];
138     }
139 
140 	snd_audioDMA.channels = s_channels->intVal;
141 	if (snd_audioDMA.channels < 1 || snd_audioDMA.channels > 2)
142 		snd_audioDMA.channels = 2;
143 
144 	tmp = 0;
145 	if (snd_audioDMA.channels == 2)
146 		tmp = 1;
147     rc = ioctl (oss_audioFD, SNDCTL_DSP_STEREO, &tmp);
148     if (rc < 0) {
149 		perror (oss_curDevice);
150         Com_Printf (PRNT_ERROR, "Could not set %s to stereo=%d", oss_curDevice, snd_audioDMA.channels);
151 		close (oss_audioFD);
152         return qFalse;
153     }
154 	if (tmp)
155 		snd_audioDMA.channels = 2;
156 	else
157 		snd_audioDMA.channels = 1;
158 
159     rc = ioctl (oss_audioFD, SNDCTL_DSP_SPEED, &snd_audioDMA.speed);
160     if (rc < 0) {
161 		perror (oss_curDevice);
162         Com_Printf (PRNT_ERROR, "Could not set %s speed to %d", oss_curDevice, snd_audioDMA.speed);
163 		close (oss_audioFD);
164         return qFalse;
165     }
166 
167     if (snd_audioDMA.sampleBits == 16) {
168         rc = AFMT_S16_LE;
169         rc = ioctl (oss_audioFD, SNDCTL_DSP_SETFMT, &rc);
170         if (rc < 0) {
171 			perror (oss_curDevice);
172 			Com_Printf (PRNT_ERROR, "Could not support 16-bit data.  Try 8-bit.\n");
173 			close (oss_audioFD);
174 			return qFalse;
175 		}
176     }
177 	else if (snd_audioDMA.sampleBits == 8) {
178         rc = AFMT_U8;
179         rc = ioctl (oss_audioFD, SNDCTL_DSP_SETFMT, &rc);
180         if (rc < 0) {
181 			perror (oss_curDevice);
182 			Com_Printf (PRNT_ERROR, "Could not support 8-bit data.\n");
183 			close (oss_audioFD);
184 			return qFalse;
185 		}
186     }
187 	else {
188 		perror (oss_curDevice);
189 		Com_Printf (PRNT_ERROR, "%d-bit sound not supported.", snd_audioDMA.sampleBits);
190 		close (oss_audioFD);
191 		return qFalse;
192 	}
193 
194     if (ioctl (oss_audioFD, SNDCTL_DSP_GETOSPACE, &info) == -1) {
195         perror ("GETOSPACE");
196 		Com_Printf (PRNT_ERROR, "Um, can't do GETOSPACE?\n");
197 		close (oss_audioFD);
198 		return qFalse;
199     }
200 
201 	snd_audioDMA.samples = info.fragstotal * info.fragsize / (snd_audioDMA.sampleBits/8);
202 	snd_audioDMA.submissionChunk = 1;
203 
204 	// Memory map the dma buffer
205 	if (!snd_audioDMA.buffer)
206 		snd_audioDMA.buffer = (unsigned char *) mmap(NULL, info.fragstotal
207 			* info.fragsize, PROT_WRITE, MAP_FILE|MAP_SHARED, oss_audioFD, 0);
208 
209 	if (!snd_audioDMA.buffer) {
210 		perror (oss_curDevice);
211 		Com_Printf (PRNT_ERROR, "Could not mmap %s\n", oss_curDevice);
212 		close (oss_audioFD);
213 		return qFalse;
214 	}
215 
216 	// Toggle the trigger & start her up
217     tmp = 0;
218     rc  = ioctl (oss_audioFD, SNDCTL_DSP_SETTRIGGER, &tmp);
219 	if (rc < 0) {
220 		perror (oss_curDevice);
221 		Com_Printf (PRNT_ERROR, "Could not toggle.\n");
222 		close (oss_audioFD);
223 		return qFalse;
224 	}
225 
226     tmp = PCM_ENABLE_OUTPUT;
227     rc = ioctl (oss_audioFD, SNDCTL_DSP_SETTRIGGER, &tmp);
228 	if (rc < 0) {
229 		perror (oss_curDevice);
230 		Com_Printf (PRNT_ERROR, "Could not toggle.\n");
231 		close (oss_audioFD);
232 		return qFalse;
233 	}
234 
235 	snd_audioDMA.samplePos = 0;
236 
237 	Com_Printf (0,"Stereo: %d\n", snd_audioDMA.channels - 1);
238 	Com_Printf (0,"Samples: %d\n", snd_audioDMA.samples);
239 	Com_Printf (0,"Samplepos: %d\n", snd_audioDMA.samplePos);
240 	Com_Printf (0,"Samplebits: %d\n", snd_audioDMA.sampleBits);
241 	Com_Printf (0,"Submission_chunk: %d\n", snd_audioDMA.submissionChunk);
242 	Com_Printf (0,"Speed: %d\n", snd_audioDMA.speed);
243 
244 	oss_initialized = qTrue;
245 	Snd_Activate (qTrue);
246 	return qTrue;
247 }
248 
249 
250 /*
251 ==============
252 OSS_Shutdown
253 ===============
254 */
OSS_Shutdown(void)255 void OSS_Shutdown (void)
256 {
257 /*
258 	FIXME: some problem when really shutting down here
259 
260 	Snd_Activate (qFalse);
261 	if (oss_audioFD != -1) {
262 		close (oss_audioFD);
263 		oss_audioFD = -1;
264 	}
265 */
266 }
267 
268 
269 /*
270 ==============
271 OSS_GetDMAPos
272 ===============
273 */
OSS_GetDMAPos(void)274 int OSS_GetDMAPos (void)
275 {
276 	struct count_info count;
277 
278 	if (!oss_initialized)
279 		return 0;
280 
281 	if (ioctl (oss_audioFD, SNDCTL_DSP_GETOPTR, &count) == -1) {
282 		perror (oss_curDevice);
283 		Com_Printf (PRNT_ERROR, "SndImp_GetDMAPos: Uh, sound dead.\n");
284 		close (oss_audioFD);
285 		oss_initialized = qFalse;
286 		return 0;
287 	}
288 
289 	snd_audioDMA.samplePos = count.ptr / (snd_audioDMA.sampleBits / 8);
290 
291 	return snd_audioDMA.samplePos;
292 }
293 
294 /*
295 ==============
296 OSS_GetDMAPos
297 ===============
298 */
OSS_BeginPainting(void)299 void OSS_BeginPainting (void)
300 {
301 }
302 
303 
304 /*
305 ==============
306 OSS_GetDMAPos
307 ===============
308 */
OSS_Submit(void)309 void OSS_Submit (void)
310 {
311 }
312