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