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
27 #include <sys/ioctl.h>
28 #include <sys/soundcard.h>
34 #define NB_FRAGMENTS 4
36 static int audio_fd = -1;
37 static int old_osstime = 0;
38 static unsigned int osssoundtime;
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
49 qbool SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
51 int flags, ioctl_param, prev_value;
52 unsigned int fragmentsize;
54 Con_DPrint("SndSys_Init: using the OSS module\n");
56 // Check the requested sound format
57 if (requested->width < 1 || requested->width > 2)
59 Con_Printf("SndSys_Init: invalid sound width (%hu)\n",
62 if (suggested != NULL)
64 memcpy(suggested, requested, sizeof(*suggested));
66 if (requested->width < 1)
76 audio_fd = open("/dev/dsp", O_WRONLY);
80 Con_Print("SndSys_Init: could not open /dev/dsp\n");
84 // Use non-blocking IOs if possible
85 flags = fcntl(audio_fd, F_GETFL);
88 if (fcntl(audio_fd, F_SETFL, flags | O_NONBLOCK) == -1)
89 Con_Print("SndSys_Init : fcntl(F_SETFL, O_NONBLOCK) failed!\n");
92 Con_Print("SndSys_Init: fcntl(F_GETFL) failed!\n");
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)
101 Con_Print ("SndSys_Init: could not set the fragment size\n");
105 Con_Printf ("SndSys_Init: using %u fragments of %u bytes\n",
106 ioctl_param >> 16, 1 << (ioctl_param & 0xFFFF));
108 // Set the sound width
109 if (requested->width == 1)
110 ioctl_param = AFMT_U8;
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)
117 if (ioctl_param != prev_value && suggested != NULL)
119 memcpy(suggested, requested, sizeof(*suggested));
121 if (ioctl_param == AFMT_S16_NE)
122 suggested->width = 2;
124 suggested->width = 1;
127 Con_Printf("SndSys_Init: could not set the sound width to %hu\n",
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)
138 if (ioctl_param != requested->channels && suggested != NULL)
140 memcpy(suggested, requested, sizeof(*suggested));
141 suggested->channels = ioctl_param;
144 Con_Printf("SndSys_Init: could not set the number of channels to %hu\n",
145 requested->channels);
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)
155 if ((unsigned int)ioctl_param != requested->speed && suggested != NULL)
157 memcpy(suggested, requested, sizeof(*suggested));
158 suggested->speed = ioctl_param;
161 Con_Printf("SndSys_Init: could not set the sound speed to %u\n",
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);
173 snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
182 Stop the sound card, delete "snd_renderbuffer" and free its other resources
185 void SndSys_Shutdown (void)
187 // Stop the sound and close the device
190 ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
195 if (snd_renderbuffer != NULL)
197 Mem_Free(snd_renderbuffer->ring);
198 Mem_Free(snd_renderbuffer);
199 snd_renderbuffer = NULL;
209 static int SndSys_Write (const unsigned char* buffer, unsigned int nb_bytes)
214 written = write (audio_fd, buffer, nb_bytes);
218 Con_Printf ("SndSys_Write: audio write returned %d! (errno= %d)\n",
223 factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
224 if (written % factor != 0)
225 Sys_Abort ("SndSys_Write: nb of bytes written (%d) isn't aligned to a frame sample!\n",
228 snd_renderbuffer->startframe += written / factor;
230 if ((unsigned int)written < nb_bytes)
232 Con_DPrintf("SndSys_Submit: audio can't keep up! (%u < %u)\n",
244 Submit the contents of "snd_renderbuffer" to the sound card
247 void SndSys_Submit (void)
249 unsigned int startoffset, factor, limit, nbframes;
253 snd_renderbuffer->startframe == snd_renderbuffer->endframe)
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)
262 written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], limit * factor);
263 if (written < 0 || (unsigned int)written < limit * factor)
270 SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], nbframes * factor);
278 Returns the number of sample frames consumed since the sound started
281 unsigned int SndSys_GetSoundTime (void)
283 struct count_info count;
285 unsigned int timediff;
287 if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1)
289 Con_Print ("SndSys_GetSoundTimeDiff: can't ioctl (SNDCTL_DSP_GETOPTR)\n");
292 new_osstime = count.bytes / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels);
294 if (new_osstime >= old_osstime)
295 timediff = new_osstime - old_osstime;
298 Con_Print ("SndSys_GetSoundTime: osstime wrapped\n");
302 old_osstime = new_osstime;
303 osssoundtime += timediff;
310 SndSys_LockRenderBuffer
312 Get the exclusive lock on "snd_renderbuffer"
315 qbool SndSys_LockRenderBuffer (void)
324 SndSys_UnlockRenderBuffer
326 Release the exclusive lock on "snd_renderbuffer"
329 void SndSys_UnlockRenderBuffer (void)
338 Send keyboard events originating from the sound system (e.g. MIDI)
341 void SndSys_SendKeyEvents(void)