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