2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
21 // OSS module, used by Linux and FreeBSD
25 #include <sys/ioctl.h>
26 #include <sys/soundcard.h>
33 #define NB_FRAGMENTS 4
35 static int audio_fd = -1;
36 static int old_osstime = 0;
37 static unsigned int osssoundtime;
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
48 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
50 int flags, ioctl_param, prev_value;
51 unsigned int fragmentsize;
53 Con_DPrint("SndSys_Init: using the OSS module\n");
55 // Check the requested sound format
56 if (requested->width < 1 || requested->width > 2)
58 Con_Printf("SndSys_Init: invalid sound width (%hu)\n",
61 if (suggested != NULL)
63 memcpy(suggested, requested, sizeof(suggested));
65 if (requested->width < 1)
75 audio_fd = open("/dev/dsp", O_WRONLY);
79 Con_Print("SndSys_Init: could not open /dev/dsp\n");
83 // Use non-blocking IOs if possible
84 flags = fcntl(audio_fd, F_GETFL);
87 if (fcntl(audio_fd, F_SETFL, flags | O_NONBLOCK) == -1)
88 Con_Print("SndSys_Init : fcntl(F_SETFL, O_NONBLOCK) failed!\n");
91 Con_Print("SndSys_Init: fcntl(F_GETFL) failed!\n");
93 ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
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)
102 Con_Print ("SndSys_Init: could not set the fragment size\n");
107 // Set the sound width
108 if (requested->width == 1)
109 ioctl_param = AFMT_U8;
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)
116 if (ioctl_param != prev_value && suggested != NULL)
118 memcpy(suggested, requested, sizeof(suggested));
120 if (ioctl_param == AFMT_S16_NE)
121 suggested->width = 2;
123 suggested->width = 1;
126 Con_Printf("SndSys_Init: could not set the sound width to %hu\n",
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)
137 if (ioctl_param != requested->channels && suggested != NULL)
139 memcpy(suggested, requested, sizeof(suggested));
140 suggested->channels = ioctl_param;
143 Con_Printf("SndSys_Init: could not set the number of channels to %hu\n",
144 requested->channels);
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)
154 if ((unsigned int)ioctl_param != requested->speed && suggested != NULL)
156 memcpy(suggested, requested, sizeof(suggested));
157 suggested->speed = ioctl_param;
160 Con_Printf("SndSys_Init: could not set the sound speed to %u\n",
167 alsaspeakerlayout = true;
169 alsaspeakerlayout = false;
174 snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
183 Stop the sound card, delete "snd_renderbuffer" and free its other resources
186 void SndSys_Shutdown (void)
188 // Stop the sound and close the device
191 ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
196 if (snd_renderbuffer != NULL)
198 Mem_Free(snd_renderbuffer->ring);
199 Mem_Free(snd_renderbuffer);
200 snd_renderbuffer = NULL;
209 Submit the contents of "snd_renderbuffer" to the sound card
212 void SndSys_Submit (void)
214 unsigned int startoffset, factor, limit, nbframes;
218 snd_renderbuffer->startframe == snd_renderbuffer->endframe)
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)
227 written = write (audio_fd, &snd_renderbuffer->ring[startoffset * factor], limit * factor);
230 Con_Printf("SndSys_Submit: audio write returned %d!\n", written);
234 if (written % factor != 0)
235 Sys_Error("SndSys_Submit: nb of bytes written (%d) isn't aligned to a frame sample!\n", written);
237 snd_renderbuffer->startframe += written / factor;
239 if ((unsigned int)written < limit * factor)
241 Con_Printf("SndSys_Submit: audio can't keep up! (%u < %u)\n", written, limit * factor);
249 written = write (audio_fd, &snd_renderbuffer->ring[startoffset * factor], nbframes * factor);
252 Con_Printf("SndSys_Submit: audio write returned %d!\n", written);
256 if (written % factor != 0)
257 Sys_Error("SndSys_Submit: nb of bytes written (%d) isn't aligned to a frame sample!\n", written);
259 snd_renderbuffer->startframe += written / factor;
261 if ((unsigned int)written < nbframes * factor)
262 Con_Printf("SndSys_Submit: audio can't keep up! (%u < %u)\n", written, nbframes * factor);
270 Returns the number of sample frames consumed since the sound started
273 unsigned int SndSys_GetSoundTime (void)
275 struct count_info count;
277 unsigned int timediff;
279 // TODO: use SNDCTL_DSP_GETODELAY instead
280 if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1)
282 Con_Print ("SndSys_GetSoundTimeDiff: can't ioctl (SNDCTL_DSP_GETOPTR)\n");
285 new_osstime = count.bytes / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels);
287 if (new_osstime >= old_osstime)
288 timediff = new_osstime - old_osstime;
291 Con_Print ("SndSys_GetSoundTime: osstime wrapped\n");
295 old_osstime = new_osstime;
296 osssoundtime += timediff;
303 SndSys_LockRenderBuffer
305 Get the exclusive lock on "snd_renderbuffer"
308 qboolean SndSys_LockRenderBuffer (void)
317 SndSys_UnlockRenderBuffer
319 Release the exclusive lock on "snd_renderbuffer"
322 void SndSys_UnlockRenderBuffer (void)