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