• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // snd_mix.c -- portable code to mix sounds for snd_dma.c
21 
22 #include "quakedef.h"
23 
24 #ifdef _WIN32
25 #include "winquake.h"
26 #else
27 #define DWORD	unsigned long
28 #endif
29 
30 #define	PAINTBUFFER_SIZE	512
31 
32 typedef union {
33     portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
34     int intbuf[PAINTBUFFER_SIZE * sizeof(portable_samplepair_t) / sizeof(int)];
35 } portableOrInt_t;
36 
37 portableOrInt_t paintbuffer;
38 int		snd_scaletable[32][256];
39 int 	*snd_p, snd_linear_count, snd_vol;
40 short	*snd_out;
41 
42 void Snd_WriteLinearBlastStereo16 (void);
43 
44 extern void SNDDMA_ReportWrite(size_t lengthBytes);
45 #if	!id386
Snd_WriteLinearBlastStereo16(void)46 void Snd_WriteLinearBlastStereo16 (void)
47 {
48 	int		i;
49 	int		val;
50 
51 	for (i=0 ; i<snd_linear_count ; i+=2)
52 	{
53 		val = (snd_p[i]*snd_vol)>>8;
54 		if (val > 0x7fff)
55 			snd_out[i] = 0x7fff;
56 		else if (val < (short)0x8000)
57 			snd_out[i] = (short)0x8000;
58 		else
59 			snd_out[i] = val;
60 
61 		val = (snd_p[i+1]*snd_vol)>>8;
62 		if (val > 0x7fff)
63 			snd_out[i+1] = 0x7fff;
64 		else if (val < (short)0x8000)
65 			snd_out[i+1] = (short)0x8000;
66 		else
67 			snd_out[i+1] = val;
68 	}
69 	SNDDMA_ReportWrite(snd_linear_count << 1);
70 }
71 #endif
72 
S_TransferStereo16(int endtime)73 void S_TransferStereo16 (int endtime)
74 {
75 	int		lpos;
76 	int		lpaintedtime;
77 	DWORD	*pbuf;
78 #ifdef _WIN32
79 	int		reps;
80 	DWORD	dwSize,dwSize2;
81 	DWORD	*pbuf2;
82 	HRESULT	hresult;
83 #endif
84 
85 	snd_vol = (int)(volume.value*256);
86 
87 	snd_p = paintbuffer.intbuf;
88 	lpaintedtime = paintedtime;
89 
90 #ifdef _WIN32
91 	if (pDSBuf)
92 	{
93 		reps = 0;
94 
95 		while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize,
96 									   &pbuf2, &dwSize2, 0)) != DS_OK)
97 		{
98 			if (hresult != DSERR_BUFFERLOST)
99 			{
100 				Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n");
101 				S_Shutdown ();
102 				S_Startup ();
103 				return;
104 			}
105 
106 			if (++reps > 10000)
107 			{
108 				Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n");
109 				S_Shutdown ();
110 				S_Startup ();
111 				return;
112 			}
113 		}
114 	}
115 	else
116 #endif
117 	{
118 		pbuf = (DWORD *)shm->buffer;
119 	}
120 
121 	while (lpaintedtime < endtime)
122 	{
123 	// handle recirculating buffer issues
124 		lpos = lpaintedtime & ((shm->samples>>1)-1);
125 
126 		snd_out = (short *) pbuf + (lpos<<1);
127 
128 		snd_linear_count = (shm->samples>>1) - lpos;
129 		if (lpaintedtime + snd_linear_count > endtime)
130 			snd_linear_count = endtime - lpaintedtime;
131 
132 		snd_linear_count <<= 1;
133 
134 	// write a linear blast of samples
135 		Snd_WriteLinearBlastStereo16 ();
136 
137 		snd_p += snd_linear_count;
138 		lpaintedtime += (snd_linear_count>>1);
139 	}
140 
141 #ifdef _WIN32
142 	if (pDSBuf)
143 		pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
144 #endif
145 }
146 
S_TransferPaintBuffer(int endtime)147 void S_TransferPaintBuffer(int endtime)
148 {
149 	int 	out_idx;
150 	int 	count;
151 	int 	out_mask;
152 	int 	*p;
153 	int 	step;
154 	int		val;
155 	int		snd_vol;
156 	DWORD	*pbuf;
157 #ifdef _WIN32
158 	int		reps;
159 	DWORD	dwSize,dwSize2;
160 	DWORD	*pbuf2;
161 	HRESULT	hresult;
162 #endif
163 
164 	if (shm->samplebits == 16 && shm->channels == 2)
165 	{
166 		S_TransferStereo16 (endtime);
167 		return;
168 	}
169 
170 	p = paintbuffer.intbuf;
171 	count = (endtime - paintedtime) * shm->channels;
172 	out_mask = shm->samples - 1;
173 	out_idx = paintedtime * shm->channels & out_mask;
174 	step = 3 - shm->channels;
175 	snd_vol = (int)(volume.value*256);
176 
177 #ifdef _WIN32
178 	if (pDSBuf)
179 	{
180 		reps = 0;
181 
182 		while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize,
183 									   &pbuf2,&dwSize2, 0)) != DS_OK)
184 		{
185 			if (hresult != DSERR_BUFFERLOST)
186 			{
187 				Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n");
188 				S_Shutdown ();
189 				S_Startup ();
190 				return;
191 			}
192 
193 			if (++reps > 10000)
194 			{
195 				Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n");
196 				S_Shutdown ();
197 				S_Startup ();
198 				return;
199 			}
200 		}
201 	}
202 	else
203 #endif
204 	{
205 		pbuf = (DWORD *)shm->buffer;
206 	}
207 
208 	if (shm->samplebits == 16)
209 	{
210 		short *out = (short *) pbuf;
211 		while (count--)
212 		{
213 			val = (*p * snd_vol) >> 8;
214 			p+= step;
215 			if (val > 0x7fff)
216 				val = 0x7fff;
217 			else if (val < (short)0x8000)
218 				val = (short)0x8000;
219 			out[out_idx] = val;
220 			out_idx = (out_idx + 1) & out_mask;
221 		}
222 	}
223 	else if (shm->samplebits == 8)
224 	{
225 		unsigned char *out = (unsigned char *) pbuf;
226 		while (count--)
227 		{
228 			val = (*p * snd_vol) >> 8;
229 			p+= step;
230 			if (val > 0x7fff)
231 				val = 0x7fff;
232 			else if (val < (short)0x8000)
233 				val = (short)0x8000;
234 			out[out_idx] = (val>>8) + 128;
235 			out_idx = (out_idx + 1) & out_mask;
236 		}
237 	}
238 
239 #ifdef _WIN32
240 	if (pDSBuf) {
241 		DWORD dwNewpos, dwWrite;
242 		int il = paintedtime;
243 		int ir = endtime - paintedtime;
244 
245 		ir += il;
246 
247 		pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
248 
249 		pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite);
250 
251 //		if ((dwNewpos >= il) && (dwNewpos <= ir))
252 //			Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos);
253 	}
254 #endif
255 }
256 
257 
258 /*
259 ===============================================================================
260 
261 CHANNEL MIXING
262 
263 ===============================================================================
264 */
265 
266 void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime);
267 void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime);
268 
S_PaintChannels(int endtime)269 void S_PaintChannels(int endtime)
270 {
271 	int 	i;
272 	int 	end;
273 	channel_t *ch;
274 	sfxcache_t	*sc;
275 	int		ltime, count;
276 
277 	while (paintedtime < endtime)
278 	{
279 	// if paintbuffer is smaller than DMA buffer
280 		end = endtime;
281 		if (endtime - paintedtime > PAINTBUFFER_SIZE)
282 			end = paintedtime + PAINTBUFFER_SIZE;
283 
284 	// clear the paint buffer
285 		Q_memset(paintbuffer.paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t));
286 
287 	// paint in the channels.
288 		ch = channels;
289 		for (i=0; i<total_channels ; i++, ch++)
290 		{
291 			if (!ch->sfx)
292 				continue;
293 			if (!ch->leftvol && !ch->rightvol)
294 				continue;
295 			sc = S_LoadSound (ch->sfx);
296 			if (!sc)
297 				continue;
298 
299 			ltime = paintedtime;
300 
301 			while (ltime < end)
302 			{	// paint up to end
303 				if (ch->end < end)
304 					count = ch->end - ltime;
305 				else
306 					count = end - ltime;
307 
308 				if (count > 0)
309 				{
310 					if (sc->width == 1)
311 						SND_PaintChannelFrom8(ch, sc, count);
312 					else
313 						SND_PaintChannelFrom16(ch, sc, count);
314 
315 					ltime += count;
316 				}
317 
318 			// if at end of loop, restart
319 				if (ltime >= ch->end)
320 				{
321 					if (sc->loopstart >= 0)
322 					{
323 						ch->pos = sc->loopstart;
324 						ch->end = ltime + sc->length - ch->pos;
325 					}
326 					else
327 					{	// channel just stopped
328 						ch->sfx = NULL;
329 						break;
330 					}
331 				}
332 			}
333 
334 		}
335 
336 	// transfer out according to DMA format
337 		S_TransferPaintBuffer(end);
338 		paintedtime = end;
339 	}
340 }
341 
SND_InitScaletable(void)342 void SND_InitScaletable (void)
343 {
344 	int		i, j;
345 
346 	for (i=0 ; i<32 ; i++)
347 		for (j=0 ; j<256 ; j++)
348 			snd_scaletable[i][j] = ((signed char)j) * i * 8;
349 }
350 
351 
352 #if	!id386
353 
SND_PaintChannelFrom8(channel_t * ch,sfxcache_t * sc,int count)354 void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count)
355 {
356 	int 	data;
357 	int		*lscale, *rscale;
358 	unsigned char *sfx;
359 	int		i;
360 
361 	if (ch->leftvol > 255)
362 		ch->leftvol = 255;
363 	if (ch->rightvol > 255)
364 		ch->rightvol = 255;
365 
366 	lscale = snd_scaletable[ch->leftvol >> 3];
367 	rscale = snd_scaletable[ch->rightvol >> 3];
368 	sfx = sc->data.uc + ch->pos;
369 
370 	for (i=0 ; i<count ; i++)
371 	{
372 		data = sfx[i];
373 		paintbuffer.paintbuffer[i].left += lscale[data];
374 		paintbuffer.paintbuffer[i].right += rscale[data];
375 	}
376 
377 	ch->pos += count;
378 }
379 
380 #endif	// !id386
381 
382 
SND_PaintChannelFrom16(channel_t * ch,sfxcache_t * sc,int count)383 void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count)
384 {
385 	int data;
386 	int left, right;
387 	int leftvol, rightvol;
388 	signed short *sfx;
389 	int	i;
390 
391 	leftvol = ch->leftvol;
392 	rightvol = ch->rightvol;
393 	sfx = sc->data.ss + ch->pos;
394 
395 	for (i=0 ; i<count ; i++)
396 	{
397 		data = sfx[i];
398 		left = (data * leftvol) >> 8;
399 		right = (data * rightvol) >> 8;
400 		paintbuffer.paintbuffer[i].left += left;
401 		paintbuffer.paintbuffer[i].right += right;
402 	}
403 
404 	ch->pos += count;
405 }
406 
407