snd_mix.c (10358B)
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 lscale = snd_scaletable[ ch->leftvol >> 11]; 382 rscale = snd_scaletable[ ch->rightvol >> 11]; 383 sfx = (signed char *)sc->data + ch->pos; 384 385 samp = &paintbuffer[offset]; 386 387 for (i=0 ; i<count ; i++, samp++) 388 { 389 data = sfx[i]; 390 samp->left += lscale[data]; 391 samp->right += rscale[data]; 392 } 393 394 ch->pos += count; 395 } 396 397 #else 398 399 __declspec( naked ) void S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset) 400 { 401 __asm { 402 push esi 403 push edi 404 push ebx 405 push ebp 406 mov ebx,ds:dword ptr[4+16+esp] 407 mov esi,ds:dword ptr[8+16+esp] 408 mov eax,ds:dword ptr[4+ebx] 409 mov edx,ds:dword ptr[8+ebx] 410 cmp eax,255 411 jna LLeftSet 412 mov eax,255 413 LLeftSet: 414 cmp edx,255 415 jna LRightSet 416 mov edx,255 417 LRightSet: 418 and eax,0F8h 419 add esi,20 420 and edx,0F8h 421 mov edi,ds:dword ptr[16+ebx] 422 mov ecx,ds:dword ptr[12+16+esp] 423 add esi,edi 424 shl eax,7 425 add edi,ecx 426 shl edx,7 427 mov ds:dword ptr[16+ebx],edi 428 add eax,offset snd_scaletable 429 add edx,offset snd_scaletable 430 sub ebx,ebx 431 mov bl,ds:byte ptr[-1+esi+ecx*1] 432 test ecx,1 433 jz LMix8Loop 434 mov edi,ds:dword ptr[eax+ebx*4] 435 mov ebp,ds:dword ptr[edx+ebx*4] 436 add edi,ds:dword ptr[paintbuffer+0-8+ecx*8] 437 add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8] 438 mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi 439 mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp 440 mov bl,ds:byte ptr[-2+esi+ecx*1] 441 dec ecx 442 jz LDone 443 LMix8Loop: 444 mov edi,ds:dword ptr[eax+ebx*4] 445 mov ebp,ds:dword ptr[edx+ebx*4] 446 add edi,ds:dword ptr[paintbuffer+0-8+ecx*8] 447 add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8] 448 mov bl,ds:byte ptr[-2+esi+ecx*1] 449 mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi 450 mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp 451 mov edi,ds:dword ptr[eax+ebx*4] 452 mov ebp,ds:dword ptr[edx+ebx*4] 453 mov bl,ds:byte ptr[-3+esi+ecx*1] 454 add edi,ds:dword ptr[paintbuffer+0-8*2+ecx*8] 455 add ebp,ds:dword ptr[paintbuffer+4-8*2+ecx*8] 456 mov ds:dword ptr[paintbuffer+0-8*2+ecx*8],edi 457 mov ds:dword ptr[paintbuffer+4-8*2+ecx*8],ebp 458 sub ecx,2 459 jnz LMix8Loop 460 LDone: 461 pop ebp 462 pop ebx 463 pop edi 464 pop esi 465 ret 466 } 467 } 468 469 #endif 470 #endif 471 472 void S_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count, int offset) 473 { 474 int data; 475 int left, right; 476 int leftvol, rightvol; 477 signed short *sfx; 478 int i; 479 portable_samplepair_t *samp; 480 481 leftvol = ch->leftvol*snd_vol; 482 rightvol = ch->rightvol*snd_vol; 483 sfx = (signed short *)sc->data + ch->pos; 484 485 samp = &paintbuffer[offset]; 486 for (i=0 ; i<count ; i++, samp++) 487 { 488 data = sfx[i]; 489 left = (data * leftvol)>>8; 490 right = (data * rightvol)>>8; 491 samp->left += left; 492 samp->right += right; 493 } 494 495 ch->pos += count; 496 } 497