Two new cvars for blood particles: decal alpha, decal random scaling (min/max)
[divverent/darkplaces.git] / snd_oss.c
1 /*
2 Copyright (C) 1996-1997 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 // OSS module, used by Linux and FreeBSD
22
23 #include "quakedef.h"
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <sys/ioctl.h>
28 #include <sys/soundcard.h>
29 #include <unistd.h>
30
31 #include "snd_main.h"
32
33
34 #define NB_FRAGMENTS 4
35
36 static int audio_fd = -1;
37 static int old_osstime = 0;
38 static unsigned int osssoundtime;
39
40
41 /*
42 ====================
43 SndSys_Init
44
45 Create "snd_renderbuffer" with the proper sound format if the call is successful
46 May return a suggested format if the requested format isn't available
47 ====================
48 */
49 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
50 {
51         int flags, ioctl_param, prev_value;
52         unsigned int fragmentsize;
53
54         Con_DPrint("SndSys_Init: using the OSS module\n");
55
56         // Check the requested sound format
57         if (requested->width < 1 || requested->width > 2)
58         {
59                 Con_Printf("SndSys_Init: invalid sound width (%hu)\n",
60                                         requested->width);
61
62                 if (suggested != NULL)
63                 {
64                         memcpy(suggested, requested, sizeof(*suggested));
65
66                         if (requested->width < 1)
67                                 suggested->width = 1;
68                         else
69                                 suggested->width = 2;
70                 }
71                 
72                 return false;
73     }
74
75         // Open /dev/dsp
76     audio_fd = open("/dev/dsp", O_WRONLY);
77         if (audio_fd < 0)
78         {
79                 perror("/dev/dsp");
80                 Con_Print("SndSys_Init: could not open /dev/dsp\n");
81                 return false;
82         }
83         
84         // Use non-blocking IOs if possible
85         flags = fcntl(audio_fd, F_GETFL);
86         if (flags != -1)
87         {
88                 if (fcntl(audio_fd, F_SETFL, flags | O_NONBLOCK) == -1)
89                         Con_Print("SndSys_Init : fcntl(F_SETFL, O_NONBLOCK) failed!\n");
90         }
91         else
92                 Con_Print("SndSys_Init: fcntl(F_GETFL) failed!\n");
93         
94         // Set the fragment size (up to "NB_FRAGMENTS" fragments of "fragmentsize" bytes)
95         fragmentsize = requested->speed * requested->channels * requested->width / 10;
96         fragmentsize = (unsigned int)ceilf((float)fragmentsize / (float)NB_FRAGMENTS);
97         fragmentsize = CeilPowerOf2(fragmentsize);
98         ioctl_param = (NB_FRAGMENTS << 16) | log2i(fragmentsize);
99         if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &ioctl_param) == -1)
100         {
101                 Con_Print ("SndSys_Init: could not set the fragment size\n");
102                 SndSys_Shutdown ();
103                 return false;
104         }
105         Con_Printf ("SndSys_Init: using %u fragments of %u bytes\n",
106                                 ioctl_param >> 16, 1 << (ioctl_param & 0xFFFF));
107
108         // Set the sound width
109         if (requested->width == 1)
110                 ioctl_param = AFMT_U8;
111         else
112                 ioctl_param = AFMT_S16_NE;
113         prev_value = ioctl_param;
114         if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &ioctl_param) == -1 ||
115                 ioctl_param != prev_value)
116         {
117                 if (ioctl_param != prev_value && suggested != NULL)
118                 {
119                         memcpy(suggested, requested, sizeof(*suggested));
120
121                         if (ioctl_param == AFMT_S16_NE)
122                                 suggested->width = 2;
123                         else
124                                 suggested->width = 1;
125                 }
126
127                 Con_Printf("SndSys_Init: could not set the sound width to %hu\n",
128                                         requested->width);
129                 SndSys_Shutdown();
130                 return false;
131         }
132
133         // Set the sound channels
134         ioctl_param = requested->channels;
135         if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ioctl_param) == -1 ||
136                 ioctl_param != requested->channels)
137         {
138                 if (ioctl_param != requested->channels && suggested != NULL)
139                 {
140                         memcpy(suggested, requested, sizeof(*suggested));
141                         suggested->channels = ioctl_param;
142                 }
143
144                 Con_Printf("SndSys_Init: could not set the number of channels to %hu\n",
145                                         requested->channels);
146                 SndSys_Shutdown();
147                 return false;
148         }
149
150         // Set the sound speed
151         ioctl_param = requested->speed;
152         if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &ioctl_param) == -1 ||
153                 (unsigned int)ioctl_param != requested->speed)
154         {
155                 if ((unsigned int)ioctl_param != requested->speed && suggested != NULL)
156                 {
157                         memcpy(suggested, requested, sizeof(*suggested));
158                         suggested->speed = ioctl_param;
159                 }
160
161                 Con_Printf("SndSys_Init: could not set the sound speed to %u\n",
162                                         requested->speed);
163                 SndSys_Shutdown();
164                 return false;
165         }
166
167         // TOCHECK: I'm not sure which channel layout OSS uses for 5.1 and 7.1
168         if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO)
169                 Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_ALSA);
170
171         old_osstime = 0;
172         osssoundtime = 0;
173         snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
174         return true;
175 }
176
177
178 /*
179 ====================
180 SndSys_Shutdown
181
182 Stop the sound card, delete "snd_renderbuffer" and free its other resources
183 ====================
184 */
185 void SndSys_Shutdown (void)
186 {
187         // Stop the sound and close the device
188         if (audio_fd >= 0)
189         {
190                 ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
191                 close(audio_fd);
192                 audio_fd = -1;
193         }
194
195         if (snd_renderbuffer != NULL)
196         {
197                 Mem_Free(snd_renderbuffer->ring);
198                 Mem_Free(snd_renderbuffer);
199                 snd_renderbuffer = NULL;
200         }
201 }
202
203
204 /*
205 ====================
206 SndSys_Write
207 ====================
208 */
209 static int SndSys_Write (const unsigned char* buffer, unsigned int nb_bytes)
210 {
211         int written;
212         unsigned int factor;
213
214         written = write (audio_fd, buffer, nb_bytes);
215         if (written < 0)
216         {
217                 if (errno != EAGAIN)
218                         Con_Printf ("SndSys_Write: audio write returned %d! (errno= %d)\n",
219                                                 written, errno);
220                 return written;
221         }
222
223         factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
224         if (written % factor != 0)
225                 Sys_Error ("SndSys_Write: nb of bytes written (%d) isn't aligned to a frame sample!\n",
226                                    written);
227
228         snd_renderbuffer->startframe += written / factor;
229
230         if ((unsigned int)written < nb_bytes)
231         {
232                 Con_DPrintf("SndSys_Submit: audio can't keep up! (%u < %u)\n",
233                                         written, nb_bytes);
234         }
235
236         return written;
237 }
238
239
240 /*
241 ====================
242 SndSys_Submit
243
244 Submit the contents of "snd_renderbuffer" to the sound card
245 ====================
246 */
247 void SndSys_Submit (void)
248 {
249         unsigned int startoffset, factor, limit, nbframes;
250         int written;
251         
252         if (audio_fd < 0 ||
253                 snd_renderbuffer->startframe == snd_renderbuffer->endframe)
254                 return;
255
256         startoffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
257         factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
258         limit = snd_renderbuffer->maxframes - startoffset;
259         nbframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
260         if (nbframes > limit)
261         {
262                 written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], limit * factor);
263                 if (written < 0 || (unsigned int)written < limit * factor)
264                         return;
265                 
266                 nbframes -= limit;
267                 startoffset = 0;
268         }
269
270         SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], nbframes * factor);
271 }
272
273
274 /*
275 ====================
276 SndSys_GetSoundTime
277
278 Returns the number of sample frames consumed since the sound started
279 ====================
280 */
281 unsigned int SndSys_GetSoundTime (void)
282 {
283         struct count_info count;
284         int new_osstime;
285         unsigned int timediff;
286
287         if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1)
288         {
289                 Con_Print ("SndSys_GetSoundTimeDiff: can't ioctl (SNDCTL_DSP_GETOPTR)\n");
290                 return 0;
291         }
292         new_osstime = count.bytes / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels);
293
294         if (new_osstime >= old_osstime)
295                 timediff = new_osstime - old_osstime;
296         else
297         {
298                 Con_Print ("SndSys_GetSoundTime: osstime wrapped\n");
299                 timediff = 0;
300         }
301
302         old_osstime = new_osstime;
303         osssoundtime += timediff;
304         return osssoundtime;
305 }
306
307
308 /*
309 ====================
310 SndSys_LockRenderBuffer
311
312 Get the exclusive lock on "snd_renderbuffer"
313 ====================
314 */
315 qboolean SndSys_LockRenderBuffer (void)
316 {
317         // Nothing to do
318         return true;
319 }
320
321
322 /*
323 ====================
324 SndSys_UnlockRenderBuffer
325
326 Release the exclusive lock on "snd_renderbuffer"
327 ====================
328 */
329 void SndSys_UnlockRenderBuffer (void)
330 {
331         // Nothing to do
332 }
333
334 /*
335 ====================
336 SndSys_SendKeyEvents
337
338 Send keyboard events originating from the sound system (e.g. MIDI)
339 ====================
340 */
341 void SndSys_SendKeyEvents(void)
342 {
343         // not supported
344 }