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