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