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