Lots of minor fixes and improvements to the sound engine, plus a few more important...
[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 #include <unistd.h>
21 #include <fcntl.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
25 #include <sys/mman.h>
26 #include <sys/shm.h>
27 #include <sys/wait.h>
28 #include <sys/soundcard.h>
29 #include <stdio.h>
30 #include "quakedef.h"
31 #include "snd_main.h"
32
33 int audio_fd;
34 int snd_inited;
35
36 static int tryrates[] = {44100, 22050, 11025, 8000};
37
38 qboolean SNDDMA_Init(void)
39 {
40         int rc;
41         int fmt;
42         int tmp;
43         int i;
44         char *s;
45         struct audio_buf_info info;
46         int caps;
47         int format16bit;
48
49 #if BYTE_ORDER == BIG_ENDIAN
50         format16bit = AFMT_S16_BE;
51 #else
52         format16bit = AFMT_S16_LE;
53 #endif
54         snd_inited = 0;
55
56         // open /dev/dsp, confirm capability to mmap, and get size of dma buffer
57     audio_fd = open("/dev/dsp", O_RDWR);
58         if (audio_fd < 0)
59         {
60                 perror("/dev/dsp");
61                 Con_Print("Could not open /dev/dsp\n");
62                 return 0;
63         }
64
65         if (ioctl(audio_fd, SNDCTL_DSP_RESET, 0) < 0)
66         {
67                 perror("/dev/dsp");
68                 Con_Print("Could not reset /dev/dsp\n");
69                 close(audio_fd);
70                 return 0;
71         }
72
73         if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1)
74         {
75                 perror("/dev/dsp");
76                 Con_Print("Sound driver too old\n");
77                 close(audio_fd);
78                 return 0;
79         }
80
81         if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP))
82         {
83                 Con_Print("Sorry but your soundcard can't do this\n");
84                 close(audio_fd);
85                 return 0;
86         }
87
88         if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1)
89         {
90                 perror("GETOSPACE");
91                 Con_Print("Um, can't do GETOSPACE?\n");
92                 close(audio_fd);
93                 return 0;
94         }
95
96         // set sample bits & speed
97         s = getenv("QUAKE_SOUND_SAMPLEBITS");
98         if (s)
99                 shm->format.width = atoi(s) / 8;
100 // COMMANDLINEOPTION: Linux OSS Sound: -sndbits <bits> chooses 8 bit or 16 bit sound output
101         else if ((i = COM_CheckParm("-sndbits")) != 0)
102                 shm->format.width = atoi(com_argv[i+1]) / 8;
103
104         if (shm->format.width != 2 && shm->format.width != 1)
105         {
106                 ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt);
107                 if (fmt & format16bit)
108                         shm->format.width = 2;
109                 else if (fmt & AFMT_U8)
110                         shm->format.width = 1;
111     }
112
113         s = getenv("QUAKE_SOUND_SPEED");
114         if (s)
115                 shm->format.speed = atoi(s);
116 // COMMANDLINEOPTION: Linux OSS Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
117         else if ((i = COM_CheckParm("-sndspeed")) != 0)
118                 shm->format.speed = atoi(com_argv[i+1]);
119         else
120         {
121                 for (i = 0;i < (int) sizeof(tryrates) / 4;i++)
122                         if (!ioctl(audio_fd, SNDCTL_DSP_SPEED, &tryrates[i]))
123                                 break;
124
125                 shm->format.speed = tryrates[i];
126     }
127
128         s = getenv("QUAKE_SOUND_CHANNELS");
129         if (s)
130                 shm->format.channels = atoi(s);
131 // COMMANDLINEOPTION: Linux OSS Sound: -sndmono sets sound output to mono
132         else if ((i = COM_CheckParm("-sndmono")) != 0)
133                 shm->format.channels = 1;
134 // COMMANDLINEOPTION: Linux OSS Sound: -sndstereo sets sound output to stereo
135         else if ((i = COM_CheckParm("-sndstereo")) != 0)
136                 shm->format.channels = 2;
137         else
138                 shm->format.channels = 2;
139
140         shm->samples = info.fragstotal * info.fragsize / shm->format.width;
141
142         // memory map the dma buffer
143         shm->bufferlength = info.fragstotal * info.fragsize;
144         shm->buffer = (unsigned char *) mmap(NULL, shm->bufferlength, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0);
145         if (!shm->buffer || shm->buffer == (unsigned char *)-1)
146         {
147                 perror("/dev/dsp");
148                 Con_Print("Could not mmap /dev/dsp\n");
149                 close(audio_fd);
150                 return 0;
151         }
152
153         tmp = 0;
154         if (shm->format.channels == 2)
155                 tmp = 1;
156
157         rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
158         if (rc < 0)
159         {
160                 perror("/dev/dsp");
161                 Con_Printf("Could not set /dev/dsp to stereo=%d\n", shm->format.channels);
162                 close(audio_fd);
163                 return 0;
164         }
165         if (tmp)
166                 shm->format.channels = 2;
167         else
168                 shm->format.channels = 1;
169
170         rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &shm->format.speed);
171         if (rc < 0)
172         {
173                 perror("/dev/dsp");
174                 Con_Printf("Could not set /dev/dsp speed to %d\n", shm->format.speed);
175                 close(audio_fd);
176                 return 0;
177         }
178
179         if (shm->format.width == 2)
180         {
181                 rc = format16bit;
182                 rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
183                 if (rc < 0)
184                 {
185                         perror("/dev/dsp");
186                         Con_Print("Could not support 16-bit data.  Try 8-bit.\n");
187                         close(audio_fd);
188                         return 0;
189                 }
190         }
191         else if (shm->format.width == 1)
192         {
193                 rc = AFMT_U8;
194                 rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
195                 if (rc < 0)
196                 {
197                         perror("/dev/dsp");
198                         Con_Print("Could not support 8-bit data.\n");
199                         close(audio_fd);
200                         return 0;
201                 }
202         }
203         else
204         {
205                 perror("/dev/dsp");
206                 Con_Printf("%d-bit sound not supported.\n", shm->format.width * 8);
207                 close(audio_fd);
208                 return 0;
209         }
210
211         // toggle the trigger & start her up
212         tmp = 0;
213         rc  = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
214         if (rc < 0)
215         {
216                 perror("/dev/dsp");
217                 Con_Print("Could not toggle.\n");
218                 close(audio_fd);
219                 return 0;
220         }
221         tmp = PCM_ENABLE_OUTPUT;
222         rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
223         if (rc < 0)
224         {
225                 perror("/dev/dsp");
226                 Con_Print("Could not toggle.\n");
227                 close(audio_fd);
228                 return 0;
229         }
230
231         shm->samplepos = 0;
232
233         snd_inited = 1;
234         return 1;
235 }
236
237 int SNDDMA_GetDMAPos(void)
238 {
239
240         struct count_info count;
241
242         if (!snd_inited) return 0;
243
244         if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1)
245         {
246                 perror("/dev/dsp");
247                 Con_Print("Uh, sound dead.\n");
248                 close(audio_fd);
249                 snd_inited = 0;
250                 return 0;
251         }
252         shm->samplepos = count.ptr / shm->format.width;
253
254         return shm->samplepos;
255 }
256
257 void SNDDMA_Shutdown(void)
258 {
259         int tmp;
260         if (snd_inited)
261         {
262                 // unmap the memory
263                 munmap(shm->buffer, shm->bufferlength);
264                 // stop the sound
265                 tmp = 0;
266                 ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
267                 ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
268                 // close the device
269                 close(audio_fd);
270                 audio_fd = -1;
271                 snd_inited = 0;
272         }
273 }
274
275 /*
276 ==============
277 SNDDMA_Submit
278
279 Send sound to device if buffer isn't really the dma buffer
280 ===============
281 */
282 void SNDDMA_Submit(void)
283 {
284 }
285
286 void *S_LockBuffer(void)
287 {
288         return shm->buffer;
289 }
290
291 void S_UnlockBuffer(void)
292 {
293 }
294