1 /*
2 Copyright (C) 1997-2001 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 "client.h"
23 #include "snd_loc.h"
24 
25 #define	PAINTBUFFER_SIZE	8192 //2048
26 portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
27 int		snd_scaletable[32][256];
28 int 	*snd_p, snd_linear_count, snd_vol;
29 int16	*snd_out;
30 
31 void EXPORT S_WriteLinearBlastStereo16 (void);
32 
33 #if !defined _WIN32 || defined _M_AMD64
S_WriteLinearBlastStereo16(void)34 void EXPORT S_WriteLinearBlastStereo16 (void)
35 {
36 	int		i;
37 	int		val;
38 
39 	for (i=0 ; i<snd_linear_count ; i+=2)
40 	{
41 		val = snd_p[i]>>8;
42 		if (val > 0x7fff)
43 			snd_out[i] = 0x7fff;
44 		else if (val < (int16)0x8000)
45 			snd_out[i] = (int16)0x8000;
46 		else
47 			snd_out[i] = val;
48 
49 		val = snd_p[i+1]>>8;
50 		if (val > 0x7fff)
51 			snd_out[i+1] = 0x7fff;
52 		else if (val < (int16)0x8000)
53 			snd_out[i+1] = (int16)0x8000;
54 		else
55 			snd_out[i+1] = val;
56 	}
57 }
58 #else
S_WriteLinearBlastStereo16(void)59 __declspec ( naked ) void __cdecl S_WriteLinearBlastStereo16 (void)
60 {
61 	__asm {
62 
63  push edi
64  push ebx
65  mov ecx,ds:dword ptr[snd_linear_count]
66  mov ebx,ds:dword ptr[snd_p]
67  mov edi,ds:dword ptr[snd_out]
68 LWLBLoopTop:
69  mov eax,ds:dword ptr[-8+ebx+ecx*4]
70  sar eax,8
71  cmp eax,07FFFh
72  jg LClampHigh
73  cmp eax,0FFFF8000h
74  jnl LClampDone
75  mov eax,0FFFF8000h
76  jmp LClampDone
77 LClampHigh:
78  mov eax,07FFFh
79 LClampDone:
80  mov edx,ds:dword ptr[-4+ebx+ecx*4]
81  sar edx,8
82  cmp edx,07FFFh
83  jg LClampHigh2
84  cmp edx,0FFFF8000h
85  jnl LClampDone2
86  mov edx,0FFFF8000h
87  jmp LClampDone2
88 LClampHigh2:
89  mov edx,07FFFh
90 LClampDone2:
91  shl edx,16
92  and eax,0FFFFh
93  or edx,eax
94  mov ds:dword ptr[-4+edi+ecx*2],edx
95  sub ecx,2
96  jnz LWLBLoopTop
97  pop ebx
98  pop edi
99  ret
100 	}
101 }
102 
103 #endif
104 
S_TransferStereo16(uint32 * pbuf,int endtime)105 void S_TransferStereo16 (uint32 *pbuf, int endtime)
106 {
107 	int		lpos;
108 	int		lpaintedtime;
109 
110 	snd_p = (int *) paintbuffer;
111 	lpaintedtime = paintedtime;
112 
113 	while (lpaintedtime < endtime)
114 	{
115 	// handle recirculating buffer issues
116 		lpos = lpaintedtime & ((dma.samples>>1)-1);
117 
118 		snd_out = (int16 *) pbuf + (lpos<<1);
119 
120 		snd_linear_count = (dma.samples>>1) - lpos;
121 		if (lpaintedtime + snd_linear_count > endtime)
122 			snd_linear_count = endtime - lpaintedtime;
123 
124 		snd_linear_count <<= 1;
125 
126 	// write a linear blast of samples
127 		S_WriteLinearBlastStereo16 ();
128 
129 		snd_p += snd_linear_count;
130 		lpaintedtime += (snd_linear_count>>1);
131 	}
132 }
133 
134 /*
135 ===================
136 S_TransferPaintBuffer
137 
138 ===================
139 */
S_TransferPaintBuffer(int endtime)140 void S_TransferPaintBuffer(int endtime)
141 {
142 	int 	out_idx;
143 	int 	count;
144 	int 	out_mask;
145 	int 	*p;
146 	int 	step;
147 	int		val;
148 	uint32	*pbuf;
149 
150 	pbuf = (uint32 *)dma.buffer;
151 
152 	if (s_testsound->intvalue)
153 	{
154 		int		i;
155 		int		count;
156 
157 		// write a fixed sine wave
158 		count = (endtime - paintedtime);
159 		for (i=0 ; i<count ; i++)
160 			paintbuffer[i].left = paintbuffer[i].right = (int)((float)sin((paintedtime+i)*0.1f)*20000*256);
161 	}
162 
163 
164 	if (dma.samplebits == 16 && dma.channels == 2)
165 	{	// optimized case
166 		S_TransferStereo16 (pbuf, endtime);
167 	}
168 	else
169 	{	// general case
170 		p = (int *) paintbuffer;
171 		count = (endtime - paintedtime) * dma.channels;
172 		out_mask = dma.samples - 1;
173 		out_idx = paintedtime * dma.channels & out_mask;
174 		step = 3 - dma.channels;
175 
176 		if (dma.samplebits == 16)
177 		{
178 			int16 *out = (int16 *) pbuf;
179 			while (count--)
180 			{
181 				val = p[0] >> 8;
182 				p+= step;
183 				if (val > 0x7fff)
184 					val = 0x7fff;
185 				else if (val < (int16)0x8000)
186 					val = (int16)0x8000;
187 				out[out_idx] = val;
188 				out_idx = (out_idx + 1) & out_mask;
189 			}
190 		}
191 		else if (dma.samplebits == 8)
192 		{
193 			unsigned char *out = (unsigned char *) pbuf;
194 			while (count--)
195 			{
196 				val = p[0] >> 8;
197 				p+= step;
198 				if (val > 0x7fff)
199 					val = 0x7fff;
200 				else if (val < (int16)0x8000)
201 					val = (int16)0x8000;
202 				out[out_idx] = (val>>8) + 128;
203 				out_idx = (out_idx + 1) & out_mask;
204 			}
205 		}
206 	}
207 }
208 
209 
210 /*
211 ===============================================================================
212 
213 CHANNEL MIXING
214 
215 ===============================================================================
216 */
217 
218 void __cdecl S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime, int offset);
219 void __cdecl S_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime, int offset);
220 
S_PaintChannels(int endtime)221 void S_PaintChannels(int endtime)
222 {
223 	int 	i;
224 	int 	end;
225 	channel_t *ch;
226 	sfxcache_t	*sc;
227 	int		ltime, count;
228 	playsound_t	*ps;
229 
230 	snd_vol = (int)(s_volume->value*256);
231 
232 //Com_Printf ("%i to %i\n", paintedtime, endtime);
233 	while (paintedtime < endtime)
234 	{
235 	// if paintbuffer is smaller than DMA buffer
236 		end = endtime;
237 		if (endtime - paintedtime > PAINTBUFFER_SIZE)
238 			end = paintedtime + PAINTBUFFER_SIZE;
239 
240 		// start any playsounds
241 		for (;;)
242 		{
243 			ps = s_pendingplays.next;
244 			if (ps == &s_pendingplays)
245 				break;	// no more pending sounds
246 			if (ps->begin <= paintedtime)
247 			{
248 				S_IssuePlaysound (ps);
249 				continue;
250 			}
251 
252 			if (ps->begin < end)
253 				end = ps->begin;		// stop here
254 			break;
255 		}
256 
257 	// clear the paint buffer
258 		if (s_rawend < paintedtime)
259 		{
260 //			Com_Printf ("clear\n");
261 			memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t));
262 		}
263 		else
264 		{	// copy from the streaming sound source
265 			int		s;
266 			int		stop;
267 
268 			stop = (end < s_rawend) ? end : s_rawend;
269 
270 			for (i=paintedtime ; i<stop ; i++)
271 			{
272 				s = i&(MAX_RAW_SAMPLES-1);
273 				paintbuffer[i-paintedtime] = s_rawsamples[s];
274 			}
275 //		if (i != end)
276 //			Com_Printf ("partial stream\n");
277 //		else
278 //			Com_Printf ("full stream\n");
279 			for ( ; i<end ; i++)
280 			{
281 				paintbuffer[i-paintedtime].left =
282 				paintbuffer[i-paintedtime].right = 0;
283 			}
284 		}
285 
286 
287 	// paint in the channels.
288 		ch = channels;
289 		for (i=0; i<MAX_CHANNELS ; i++, ch++)
290 		{
291 			ltime = paintedtime;
292 
293 			while (ltime < end)
294 			{
295 				if (!ch->sfx || (!ch->leftvol && !ch->rightvol) )
296 					break;
297 
298 				// max painting is to the end of the buffer
299 				count = end - ltime;
300 
301 				// might be stopped by running out of data
302 				if (ch->end - ltime < count)
303 					count = ch->end - ltime;
304 
305 				sc = S_LoadSound (ch->sfx);
306 				if (!sc)
307 					break;
308 
309 				if (count > 0 && ch->sfx)
310 				{
311 					if (sc->width == 1)// FIXME; 8 bit asm is wrong now
312 						S_PaintChannelFrom8(ch, sc, count,  ltime - paintedtime);
313 					else
314 						S_PaintChannelFrom16(ch, sc, count, ltime - paintedtime);
315 
316 					ltime += count;
317 				}
318 
319 			// if at end of loop, restart
320 				if (ltime >= ch->end)
321 				{
322 					if (ch->autosound)
323 					{	// autolooping sounds always go back to start
324 						ch->pos = 0;
325 						ch->end = ltime + sc->length;
326 					}
327 					else if (sc->loopstart >= 0)
328 					{
329 						ch->pos = sc->loopstart;
330 						ch->end = ltime + sc->length - ch->pos;
331 					}
332 					else
333 					{	// channel just stopped
334 						ch->sfx = NULL;
335 					}
336 				}
337 			}
338 
339 		}
340 
341 	// transfer out according to DMA format
342 		S_TransferPaintBuffer(end);
343 		paintedtime = end;
344 	}
345 }
346 
S_InitScaletable(void)347 void S_InitScaletable (void)
348 {
349 	int		i, j;
350 	int		scale;
351 
352 	if (s_volume->value > 2.0f)
353 		Cvar_Set ("s_volume", "2");
354 	else if (s_volume->value < 0)
355 		Cvar_Set  ("s_volume", "0");
356 
357 	s_volume->modified = false;
358 	for (i=0 ; i<32 ; i++)
359 	{
360 		scale = (int)(i * 8 * 256 * s_volume->value);
361 		for (j=0 ; j<256 ; j++)
362 			snd_scaletable[i][j] = ((signed char)j) * scale;
363 	}
364 }
365 
366 #if !defined _WIN32 || defined _M_AMD64
S_PaintChannelFrom8(channel_t * ch,sfxcache_t * sc,int count,int offset)367 void EXPORT S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset)
368 {
369 	int 	data;
370 	int		*lscale, *rscale;
371 	unsigned char *sfx;
372 	int		i;
373 	portable_samplepair_t	*samp;
374 
375 	if (ch->leftvol > 255)
376 		ch->leftvol = 255;
377 	if (ch->rightvol > 255)
378 		ch->rightvol = 255;
379 
380 	//ZOID-- >>11 has been changed to >>3, >>11 didn't make much sense
381 	//as it would always be zero.
382 	lscale = snd_scaletable[ ch->leftvol >> 3];
383 	rscale = snd_scaletable[ ch->rightvol >> 3];
384 	sfx = sc->data + ch->pos;
385 
386 	samp = &paintbuffer[offset];
387 
388 	for (i=0 ; i<count ; i++, samp++)
389 	{
390 		data = sfx[i];
391 		samp->left += lscale[data];
392 		samp->right += rscale[data];
393 	}
394 
395 	ch->pos += count;
396 }
397 
398 #else
399 
S_PaintChannelFrom8(channel_t * ch,sfxcache_t * sc,int count,int offset)400 __declspec( naked ) void __cdecl S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset)
401 {
402 	__asm {
403  push esi
404  push edi
405  push ebx
406  push ebp
407  mov ebx,ds:dword ptr[4+16+esp]
408  mov esi,ds:dword ptr[8+16+esp]
409  mov eax,ds:dword ptr[4+ebx]
410  mov edx,ds:dword ptr[8+ebx]
411  cmp eax,255
412  jna LLeftSet
413  mov eax,255
414 LLeftSet:
415  cmp edx,255
416  jna LRightSet
417  mov edx,255
418 LRightSet:
419  and eax,0F8h
420  add esi,20
421  and edx,0F8h
422  mov edi,ds:dword ptr[16+ebx]
423  mov ecx,ds:dword ptr[12+16+esp]
424  add esi,edi
425  shl eax,7
426  add edi,ecx
427  shl edx,7
428  mov ds:dword ptr[16+ebx],edi
429  add eax,offset snd_scaletable
430  add edx,offset snd_scaletable
431  sub ebx,ebx
432 
433  mov bl,ds:byte ptr[-1+esi+ecx*1]
434  test ecx,1
435  jz LMix8Loop
436  mov edi,ds:dword ptr[eax+ebx*4]
437  mov ebp,ds:dword ptr[edx+ebx*4]
438  add edi,ds:dword ptr[paintbuffer+0-8+ecx*8]
439  add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8]
440  mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi
441  mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp
442  mov bl,ds:byte ptr[-2+esi+ecx*1]
443  dec ecx
444  jz LDone
445 LMix8Loop:
446  mov edi,ds:dword ptr[eax+ebx*4]
447  mov ebp,ds:dword ptr[edx+ebx*4]
448  add edi,ds:dword ptr[paintbuffer+0-8+ecx*8]
449  add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8]
450  mov bl,ds:byte ptr[-2+esi+ecx*1]
451  mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi
452  mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp
453  mov edi,ds:dword ptr[eax+ebx*4]
454  mov ebp,ds:dword ptr[edx+ebx*4]
455  mov bl,ds:byte ptr[-3+esi+ecx*1]
456  add edi,ds:dword ptr[paintbuffer+0-8*2+ecx*8]
457  add ebp,ds:dword ptr[paintbuffer+4-8*2+ecx*8]
458  mov ds:dword ptr[paintbuffer+0-8*2+ecx*8],edi
459  mov ds:dword ptr[paintbuffer+4-8*2+ecx*8],ebp
460  sub ecx,2
461  jnz LMix8Loop
462 LDone:
463  pop ebp
464  pop ebx
465  pop edi
466  pop esi
467  ret
468 	}
469 }
470 
471 #endif
472 
S_PaintChannelFrom16(channel_t * ch,sfxcache_t * sc,int count,int offset)473 void __cdecl S_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count, int offset)
474 {
475 	int data;
476 	int left, right;
477 	int leftvol, rightvol;
478 	int16 *sfx;
479 	int	i;
480 	portable_samplepair_t	*samp;
481 
482 	leftvol = ch->leftvol*snd_vol;
483 	rightvol = ch->rightvol*snd_vol;
484 	sfx = (int16 *)sc->data + ch->pos;
485 
486 	samp = &paintbuffer[offset];
487 	for (i=0 ; i<count ; i++, samp++)
488 	{
489 		data = sfx[i];
490 		left = (data * leftvol)>>8;
491 		right = (data * rightvol)>>8;
492 		samp->left += left;
493 		samp->right += right;
494 	}
495 
496 	ch->pos += count;
497 }
498 
499