1 #include <unistd.h>
2 #include <fcntl.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/ioctl.h>
6 #include <sys/mman.h>
7 #include <sys/shm.h>
8 #include <sys/wait.h>
9 #ifdef __linux__
10 #include <linux/soundcard.h>
11 #else
12 #include <sys/soundcard.h>
13 #endif
14 #include <stdio.h>
15
16 #include "../client/client.h"
17 #include "../client/snd_loc.h"
18
19 int audio_fd;
20 int snd_inited;
21
22 cvar_t *sndbits;
23 cvar_t *sndspeed;
24 cvar_t *sndchannels;
25 cvar_t *snddevice;
26
27 static int tryrates[] = { 11025, 22051, 44100, 8000 };
28
SNDDMA_Init(int firsttime)29 qboolean SNDDMA_Init(int firsttime)
30 {
31
32 int rc;
33 int fmt;
34 int tmp;
35 int i;
36
37 struct audio_buf_info info;
38 int caps;
39
40 if (snd_inited)
41 return false;
42
43 if (!snddevice) {
44 sndbits = Cvar_Get("sndbits", "16", CVAR_ARCHIVE);
45 sndspeed = Cvar_Get("sndspeed", "0", CVAR_ARCHIVE);
46 sndchannels = Cvar_Get("sndchannels", "2", CVAR_ARCHIVE);
47 snddevice = Cvar_Get("snddevice", "/dev/dsp", CVAR_ARCHIVE);
48 }
49
50 // open /dev/dsp, confirm capability to mmap, and get size of dma buffer
51
52 if (!audio_fd)
53 {
54
55 audio_fd = open(snddevice->string, O_RDWR);
56
57 if (audio_fd < 0)
58 {
59 perror(snddevice->string);
60 Com_Printf("Could not open %s\n", LOG_CLIENT, snddevice->string);
61 return false;
62 }
63 }
64
65 rc = ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
66 if (rc < 0)
67 {
68 perror(snddevice->string);
69 Com_Printf("Could not reset %s\n", LOG_CLIENT, snddevice->string);
70 close(audio_fd);
71 return false;
72 }
73
74 if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1)
75 {
76 perror(snddevice->string);
77 Com_Printf("Sound driver too old\n", LOG_CLIENT);
78 close(audio_fd);
79 return false;
80 }
81
82 if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP))
83 {
84 Com_Printf("Sorry but your soundcard can't do this\n", LOG_CLIENT);
85 close(audio_fd);
86 return false;
87 }
88
89 // set sample bits & speed
90
91 dma.samplebits = (int)sndbits->value;
92 if (dma.samplebits != 16 && dma.samplebits != 8)
93 {
94 ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt);
95
96 if (fmt & AFMT_S16_LE)
97 dma.samplebits = 16;
98 else if (fmt & AFMT_U8)
99 dma.samplebits = 8;
100 }
101
102 dma.speed = (int)sndspeed->value;
103 if (!dma.speed) {
104 for (i=0 ; i<sizeof(tryrates)/4 ; i++)
105 if (!ioctl(audio_fd, SNDCTL_DSP_SPEED, &tryrates[i])) break;
106 dma.speed = tryrates[i];
107 }
108
109 dma.channels = (int)sndchannels->value;
110 if (dma.channels < 1 || dma.channels > 2)
111 dma.channels = 2;
112
113 tmp = 0;
114 if (dma.channels == 2)
115 tmp = 1;
116 rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
117 if (rc < 0)
118 {
119 perror(snddevice->string);
120 Com_Printf("Could not set %s to stereo=%d", LOG_CLIENT, snddevice->string, dma.channels);
121 close(audio_fd);
122 return false;
123 }
124 if (tmp)
125 dma.channels = 2;
126 else
127 dma.channels = 1;
128
129 rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &dma.speed);
130 if (rc < 0)
131 {
132 perror(snddevice->string);
133 Com_Printf("Could not set %s speed to %d", LOG_CLIENT, snddevice->string, dma.speed);
134 close(audio_fd);
135 return false;
136 }
137
138 if (dma.samplebits == 16)
139 {
140 rc = AFMT_S16_LE;
141 rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
142 if (rc < 0)
143 {
144 perror(snddevice->string);
145 Com_Printf("Could not support 16-bit data. Try 8-bit.\n", LOG_CLIENT);
146 close(audio_fd);
147 return false;
148 }
149 }
150 else if (dma.samplebits == 8)
151 {
152 rc = AFMT_U8;
153 rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
154 if (rc < 0)
155 {
156 perror(snddevice->string);
157 Com_Printf("Could not support 8-bit data.\n", LOG_CLIENT);
158 close(audio_fd);
159 return false;
160 }
161 }
162 else
163 {
164 perror(snddevice->string);
165 Com_Printf("%d-bit sound not supported.", LOG_CLIENT, dma.samplebits);
166 close(audio_fd);
167 return false;
168 }
169
170 if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1)
171 {
172 perror("GETOSPACE");
173 Com_Printf("Um, can't do GETOSPACE?\n", LOG_CLIENT);
174 close(audio_fd);
175 return 0;
176 }
177
178 dma.samples = info.fragstotal * info.fragsize / (dma.samplebits/8);
179 dma.submission_chunk = 1;
180
181 // memory map the dma buffer
182
183 if (!dma.buffer)
184 dma.buffer = (unsigned char *) mmap(NULL, info.fragstotal
185 * info.fragsize, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0);
186 if (!dma.buffer)
187 {
188 perror(snddevice->string);
189 Com_Printf("Could not mmap %s\n", LOG_CLIENT, snddevice->string);
190 close(audio_fd);
191 return false;
192 }
193
194 // toggle the trigger & start her up
195
196 tmp = 0;
197 rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
198 if (rc < 0)
199 {
200 perror(snddevice->string);
201 Com_Printf("Could not toggle.\n", LOG_CLIENT);
202 close(audio_fd);
203 return false;
204 }
205 tmp = PCM_ENABLE_OUTPUT;
206 rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
207 if (rc < 0)
208 {
209 perror(snddevice->string);
210 Com_Printf("Could not toggle.\n", LOG_CLIENT);
211 close(audio_fd);
212 return false;
213 }
214
215 dma.samplepos = 0;
216
217 snd_inited = 1;
218 return true;
219 }
220
SNDDMA_GetDMAPos(void)221 int SNDDMA_GetDMAPos(void)
222 {
223
224 struct count_info count;
225
226 if (!snd_inited) return 0;
227
228 if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1)
229 {
230 perror(snddevice->string);
231 Com_Printf("Uh, sound dead.\n", LOG_CLIENT);
232 close(audio_fd);
233 snd_inited = 0;
234 return 0;
235 }
236 // dma.samplepos = (count.bytes / (dma.samplebits / 8)) & (dma.samples-1);
237 // fprintf(stderr, "%d \r", count.ptr);
238 dma.samplepos = count.ptr / (dma.samplebits / 8);
239
240 return dma.samplepos;
241
242 }
243
SNDDMA_Shutdown(void)244 void SNDDMA_Shutdown(void)
245 {
246 #if 0
247 if (snd_inited)
248 {
249 close(audio_fd);
250 snd_inited = 0;
251 }
252 #endif
253 }
254
255 /*
256 ==============
257 SNDDMA_Submit
258
259 Send sound to device if buffer isn't really the dma buffer
260 ===============
261 */
SNDDMA_Submit(void)262 void SNDDMA_Submit(void)
263 {
264 }
265
SNDDMA_BeginPainting(void)266 void SNDDMA_BeginPainting (void)
267 {
268 }
269
270