snd_mix.c (17274B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 // snd_mix.c -- portable code to mix sounds for snd_dma.c 23 24 #include "snd_local.h" 25 26 static portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; 27 static int snd_vol; 28 29 // bk001119 - these not static, required by unix/snd_mixa.s 30 int* snd_p; 31 int snd_linear_count; 32 short* snd_out; 33 34 #if !( (defined __linux__ || defined __FreeBSD__ ) && (defined __i386__) ) // rb010123 35 #if !id386 36 37 void S_WriteLinearBlastStereo16 (void) 38 { 39 int i; 40 int val; 41 42 for (i=0 ; i<snd_linear_count ; i+=2) 43 { 44 val = snd_p[i]>>8; 45 if (val > 0x7fff) 46 snd_out[i] = 0x7fff; 47 else if (val < -32768) 48 snd_out[i] = -32768; 49 else 50 snd_out[i] = val; 51 52 val = snd_p[i+1]>>8; 53 if (val > 0x7fff) 54 snd_out[i+1] = 0x7fff; 55 else if (val < -32768) 56 snd_out[i+1] = -32768; 57 else 58 snd_out[i+1] = val; 59 } 60 } 61 #else 62 63 __declspec( naked ) void S_WriteLinearBlastStereo16 (void) 64 { 65 __asm { 66 67 push edi 68 push ebx 69 mov ecx,ds:dword ptr[snd_linear_count] 70 mov ebx,ds:dword ptr[snd_p] 71 mov edi,ds:dword ptr[snd_out] 72 LWLBLoopTop: 73 mov eax,ds:dword ptr[-8+ebx+ecx*4] 74 sar eax,8 75 cmp eax,07FFFh 76 jg LClampHigh 77 cmp eax,0FFFF8000h 78 jnl LClampDone 79 mov eax,0FFFF8000h 80 jmp LClampDone 81 LClampHigh: 82 mov eax,07FFFh 83 LClampDone: 84 mov edx,ds:dword ptr[-4+ebx+ecx*4] 85 sar edx,8 86 cmp edx,07FFFh 87 jg LClampHigh2 88 cmp edx,0FFFF8000h 89 jnl LClampDone2 90 mov edx,0FFFF8000h 91 jmp LClampDone2 92 LClampHigh2: 93 mov edx,07FFFh 94 LClampDone2: 95 shl edx,16 96 and eax,0FFFFh 97 or edx,eax 98 mov ds:dword ptr[-4+edi+ecx*2],edx 99 sub ecx,2 100 jnz LWLBLoopTop 101 pop ebx 102 pop edi 103 ret 104 } 105 } 106 107 #endif 108 #else 109 // forward declare, implementation somewhere else 110 void S_WriteLinearBlastStereo16 (void); 111 #endif 112 113 void S_TransferStereo16 (unsigned long *pbuf, int endtime) 114 { 115 int lpos; 116 int ls_paintedtime; 117 118 snd_p = (int *) paintbuffer; 119 ls_paintedtime = s_paintedtime; 120 121 while (ls_paintedtime < endtime) 122 { 123 // handle recirculating buffer issues 124 lpos = ls_paintedtime & ((dma.samples>>1)-1); 125 126 snd_out = (short *) pbuf + (lpos<<1); 127 128 snd_linear_count = (dma.samples>>1) - lpos; 129 if (ls_paintedtime + snd_linear_count > endtime) 130 snd_linear_count = endtime - ls_paintedtime; 131 132 snd_linear_count <<= 1; 133 134 // write a linear blast of samples 135 S_WriteLinearBlastStereo16 (); 136 137 snd_p += snd_linear_count; 138 ls_paintedtime += (snd_linear_count>>1); 139 } 140 } 141 142 /* 143 =================== 144 S_TransferPaintBuffer 145 146 =================== 147 */ 148 void S_TransferPaintBuffer(int endtime) 149 { 150 int out_idx; 151 int count; 152 int out_mask; 153 int *p; 154 int step; 155 int val; 156 unsigned long *pbuf; 157 158 pbuf = (unsigned long *)dma.buffer; 159 160 161 if ( s_testsound->integer ) { 162 int i; 163 int count; 164 165 // write a fixed sine wave 166 count = (endtime - s_paintedtime); 167 for (i=0 ; i<count ; i++) 168 paintbuffer[i].left = paintbuffer[i].right = sin((s_paintedtime+i)*0.1)*20000*256; 169 } 170 171 172 if (dma.samplebits == 16 && dma.channels == 2) 173 { // optimized case 174 S_TransferStereo16 (pbuf, endtime); 175 } 176 else 177 { // general case 178 p = (int *) paintbuffer; 179 count = (endtime - s_paintedtime) * dma.channels; 180 out_mask = dma.samples - 1; 181 out_idx = s_paintedtime * dma.channels & out_mask; 182 step = 3 - dma.channels; 183 184 if (dma.samplebits == 16) 185 { 186 short *out = (short *) pbuf; 187 while (count--) 188 { 189 val = *p >> 8; 190 p+= step; 191 if (val > 0x7fff) 192 val = 0x7fff; 193 else if (val < -32768) 194 val = -32768; 195 out[out_idx] = val; 196 out_idx = (out_idx + 1) & out_mask; 197 } 198 } 199 else if (dma.samplebits == 8) 200 { 201 unsigned char *out = (unsigned char *) pbuf; 202 while (count--) 203 { 204 val = *p >> 8; 205 p+= step; 206 if (val > 0x7fff) 207 val = 0x7fff; 208 else if (val < -32768) 209 val = -32768; 210 out[out_idx] = (val>>8) + 128; 211 out_idx = (out_idx + 1) & out_mask; 212 } 213 } 214 } 215 } 216 217 218 /* 219 =============================================================================== 220 221 CHANNEL MIXING 222 223 =============================================================================== 224 */ 225 226 static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { 227 int data, aoff, boff; 228 int leftvol, rightvol; 229 int i, j; 230 portable_samplepair_t *samp; 231 sndBuffer *chunk; 232 short *samples; 233 float ooff, fdata, fdiv, fleftvol, frightvol; 234 235 samp = &paintbuffer[ bufferOffset ]; 236 237 if (ch->doppler) { 238 sampleOffset = sampleOffset*ch->oldDopplerScale; 239 } 240 241 chunk = sc->soundData; 242 while (sampleOffset>=SND_CHUNK_SIZE) { 243 chunk = chunk->next; 244 sampleOffset -= SND_CHUNK_SIZE; 245 if (!chunk) { 246 chunk = sc->soundData; 247 } 248 } 249 250 if (!ch->doppler || ch->dopplerScale==1.0f) { 251 #if idppc_altivec 252 vector signed short volume_vec; 253 vector unsigned int volume_shift; 254 int vectorCount, samplesLeft, chunkSamplesLeft; 255 #endif 256 leftvol = ch->leftvol*snd_vol; 257 rightvol = ch->rightvol*snd_vol; 258 samples = chunk->sndChunk; 259 #if idppc_altivec 260 ((short *)&volume_vec)[0] = leftvol; 261 ((short *)&volume_vec)[1] = leftvol; 262 ((short *)&volume_vec)[4] = leftvol; 263 ((short *)&volume_vec)[5] = leftvol; 264 ((short *)&volume_vec)[2] = rightvol; 265 ((short *)&volume_vec)[3] = rightvol; 266 ((short *)&volume_vec)[6] = rightvol; 267 ((short *)&volume_vec)[7] = rightvol; 268 volume_shift = vec_splat_u32(8); 269 i = 0; 270 271 while(i < count) { 272 /* Try to align destination to 16-byte boundary */ 273 while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) { 274 data = samples[sampleOffset++]; 275 samp[i].left += (data * leftvol)>>8; 276 samp[i].right += (data * rightvol)>>8; 277 278 if (sampleOffset == SND_CHUNK_SIZE) { 279 chunk = chunk->next; 280 samples = chunk->sndChunk; 281 sampleOffset = 0; 282 } 283 i++; 284 } 285 /* Destination is now aligned. Process as many 8-sample 286 chunks as we can before we run out of room from the current 287 sound chunk. We do 8 per loop to avoid extra source data reads. */ 288 samplesLeft = count - i; 289 chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset; 290 if(samplesLeft > chunkSamplesLeft) 291 samplesLeft = chunkSamplesLeft; 292 293 vectorCount = samplesLeft / 8; 294 295 if(vectorCount) 296 { 297 vector unsigned char tmp; 298 vector short s0, s1, sampleData0, sampleData1; 299 vector short samples0, samples1; 300 vector signed int left0, right0; 301 vector signed int merge0, merge1; 302 vector signed int d0, d1, d2, d3; 303 vector unsigned char samplePermute0 = 304 (vector unsigned char)(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7); 305 vector unsigned char samplePermute1 = 306 (vector unsigned char)(8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15); 307 vector unsigned char loadPermute0, loadPermute1; 308 309 // Rather than permute the vectors after we load them to do the sample 310 // replication and rearrangement, we permute the alignment vector so 311 // we do everything in one step below and avoid data shuffling. 312 tmp = vec_lvsl(0,&samples[sampleOffset]); 313 loadPermute0 = vec_perm(tmp,tmp,samplePermute0); 314 loadPermute1 = vec_perm(tmp,tmp,samplePermute1); 315 316 s0 = *(vector short *)&samples[sampleOffset]; 317 while(vectorCount) 318 { 319 /* Load up source (16-bit) sample data */ 320 s1 = *(vector short *)&samples[sampleOffset+7]; 321 322 /* Load up destination sample data */ 323 d0 = *(vector signed int *)&samp[i]; 324 d1 = *(vector signed int *)&samp[i+2]; 325 d2 = *(vector signed int *)&samp[i+4]; 326 d3 = *(vector signed int *)&samp[i+6]; 327 328 sampleData0 = vec_perm(s0,s1,loadPermute0); 329 sampleData1 = vec_perm(s0,s1,loadPermute1); 330 331 merge0 = vec_mule(sampleData0,volume_vec); 332 merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */ 333 334 merge1 = vec_mulo(sampleData0,volume_vec); 335 merge1 = vec_sra(merge1,volume_shift); 336 337 d0 = vec_add(merge0,d0); 338 d1 = vec_add(merge1,d1); 339 340 merge0 = vec_mule(sampleData1,volume_vec); 341 merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */ 342 343 merge1 = vec_mulo(sampleData1,volume_vec); 344 merge1 = vec_sra(merge1,volume_shift); 345 346 d2 = vec_add(merge0,d2); 347 d3 = vec_add(merge1,d3); 348 349 /* Store destination sample data */ 350 *(vector signed int *)&samp[i] = d0; 351 *(vector signed int *)&samp[i+2] = d1; 352 *(vector signed int *)&samp[i+4] = d2; 353 *(vector signed int *)&samp[i+6] = d3; 354 355 i += 8; 356 vectorCount--; 357 s0 = s1; 358 sampleOffset += 8; 359 } 360 if (sampleOffset == SND_CHUNK_SIZE) { 361 chunk = chunk->next; 362 samples = chunk->sndChunk; 363 sampleOffset = 0; 364 } 365 } 366 } 367 #else 368 for ( i=0 ; i<count ; i++ ) { 369 data = samples[sampleOffset++]; 370 samp[i].left += (data * leftvol)>>8; 371 samp[i].right += (data * rightvol)>>8; 372 373 if (sampleOffset == SND_CHUNK_SIZE) { 374 chunk = chunk->next; 375 samples = chunk->sndChunk; 376 sampleOffset = 0; 377 } 378 } 379 #endif 380 } else { 381 fleftvol = ch->leftvol*snd_vol; 382 frightvol = ch->rightvol*snd_vol; 383 384 ooff = sampleOffset; 385 samples = chunk->sndChunk; 386 387 388 389 390 for ( i=0 ; i<count ; i++ ) { 391 392 aoff = ooff; 393 ooff = ooff + ch->dopplerScale; 394 boff = ooff; 395 fdata = 0; 396 for (j=aoff; j<boff; j++) { 397 if (j == SND_CHUNK_SIZE) { 398 chunk = chunk->next; 399 if (!chunk) { 400 chunk = sc->soundData; 401 } 402 samples = chunk->sndChunk; 403 ooff -= SND_CHUNK_SIZE; 404 } 405 fdata += samples[j&(SND_CHUNK_SIZE-1)]; 406 } 407 fdiv = 256 * (boff-aoff); 408 samp[i].left += (fdata * fleftvol)/fdiv; 409 samp[i].right += (fdata * frightvol)/fdiv; 410 } 411 } 412 } 413 414 void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { 415 int data; 416 int leftvol, rightvol; 417 int i; 418 portable_samplepair_t *samp; 419 sndBuffer *chunk; 420 short *samples; 421 422 leftvol = ch->leftvol*snd_vol; 423 rightvol = ch->rightvol*snd_vol; 424 425 i = 0; 426 samp = &paintbuffer[ bufferOffset ]; 427 chunk = sc->soundData; 428 while (sampleOffset>=(SND_CHUNK_SIZE_FLOAT*4)) { 429 chunk = chunk->next; 430 sampleOffset -= (SND_CHUNK_SIZE_FLOAT*4); 431 i++; 432 } 433 434 if (i!=sfxScratchIndex || sfxScratchPointer != sc) { 435 S_AdpcmGetSamples( chunk, sfxScratchBuffer ); 436 sfxScratchIndex = i; 437 sfxScratchPointer = sc; 438 } 439 440 samples = sfxScratchBuffer; 441 442 for ( i=0 ; i<count ; i++ ) { 443 data = samples[sampleOffset++]; 444 samp[i].left += (data * leftvol)>>8; 445 samp[i].right += (data * rightvol)>>8; 446 447 if (sampleOffset == SND_CHUNK_SIZE*2) { 448 chunk = chunk->next; 449 decodeWavelet(chunk, sfxScratchBuffer); 450 sfxScratchIndex++; 451 sampleOffset = 0; 452 } 453 } 454 } 455 456 void S_PaintChannelFromADPCM( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { 457 int data; 458 int leftvol, rightvol; 459 int i; 460 portable_samplepair_t *samp; 461 sndBuffer *chunk; 462 short *samples; 463 464 leftvol = ch->leftvol*snd_vol; 465 rightvol = ch->rightvol*snd_vol; 466 467 i = 0; 468 samp = &paintbuffer[ bufferOffset ]; 469 chunk = sc->soundData; 470 471 if (ch->doppler) { 472 sampleOffset = sampleOffset*ch->oldDopplerScale; 473 } 474 475 while (sampleOffset>=(SND_CHUNK_SIZE*4)) { 476 chunk = chunk->next; 477 sampleOffset -= (SND_CHUNK_SIZE*4); 478 i++; 479 } 480 481 if (i!=sfxScratchIndex || sfxScratchPointer != sc) { 482 S_AdpcmGetSamples( chunk, sfxScratchBuffer ); 483 sfxScratchIndex = i; 484 sfxScratchPointer = sc; 485 } 486 487 samples = sfxScratchBuffer; 488 489 for ( i=0 ; i<count ; i++ ) { 490 data = samples[sampleOffset++]; 491 samp[i].left += (data * leftvol)>>8; 492 samp[i].right += (data * rightvol)>>8; 493 494 if (sampleOffset == SND_CHUNK_SIZE*4) { 495 chunk = chunk->next; 496 S_AdpcmGetSamples( chunk, sfxScratchBuffer); 497 sampleOffset = 0; 498 sfxScratchIndex++; 499 } 500 } 501 } 502 503 void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { 504 int data; 505 int leftvol, rightvol; 506 int i; 507 portable_samplepair_t *samp; 508 sndBuffer *chunk; 509 byte *samples; 510 float ooff; 511 512 leftvol = ch->leftvol*snd_vol; 513 rightvol = ch->rightvol*snd_vol; 514 515 samp = &paintbuffer[ bufferOffset ]; 516 chunk = sc->soundData; 517 while (sampleOffset>=(SND_CHUNK_SIZE*2)) { 518 chunk = chunk->next; 519 sampleOffset -= (SND_CHUNK_SIZE*2); 520 if (!chunk) { 521 chunk = sc->soundData; 522 } 523 } 524 525 if (!ch->doppler) { 526 samples = (byte *)chunk->sndChunk + sampleOffset; 527 for ( i=0 ; i<count ; i++ ) { 528 data = mulawToShort[*samples]; 529 samp[i].left += (data * leftvol)>>8; 530 samp[i].right += (data * rightvol)>>8; 531 samples++; 532 if (samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) { 533 chunk = chunk->next; 534 samples = (byte *)chunk->sndChunk; 535 } 536 } 537 } else { 538 ooff = sampleOffset; 539 samples = (byte *)chunk->sndChunk; 540 for ( i=0 ; i<count ; i++ ) { 541 data = mulawToShort[samples[(int)(ooff)]]; 542 ooff = ooff + ch->dopplerScale; 543 samp[i].left += (data * leftvol)>>8; 544 samp[i].right += (data * rightvol)>>8; 545 if (ooff >= SND_CHUNK_SIZE*2) { 546 chunk = chunk->next; 547 if (!chunk) { 548 chunk = sc->soundData; 549 } 550 samples = (byte *)chunk->sndChunk; 551 ooff = 0.0; 552 } 553 } 554 } 555 } 556 557 /* 558 =================== 559 S_PaintChannels 560 =================== 561 */ 562 void S_PaintChannels( int endtime ) { 563 int i; 564 int end; 565 channel_t *ch; 566 sfx_t *sc; 567 int ltime, count; 568 int sampleOffset; 569 570 571 snd_vol = s_volume->value*255; 572 573 //Com_Printf ("%i to %i\n", s_paintedtime, endtime); 574 while ( s_paintedtime < endtime ) { 575 // if paintbuffer is smaller than DMA buffer 576 // we may need to fill it multiple times 577 end = endtime; 578 if ( endtime - s_paintedtime > PAINTBUFFER_SIZE ) { 579 end = s_paintedtime + PAINTBUFFER_SIZE; 580 } 581 582 // clear the paint buffer to either music or zeros 583 if ( s_rawend < s_paintedtime ) { 584 if ( s_rawend ) { 585 //Com_DPrintf ("background sound underrun\n"); 586 } 587 Com_Memset(paintbuffer, 0, (end - s_paintedtime) * sizeof(portable_samplepair_t)); 588 } else { 589 // copy from the streaming sound source 590 int s; 591 int stop; 592 593 stop = (end < s_rawend) ? end : s_rawend; 594 595 for ( i = s_paintedtime ; i < stop ; i++ ) { 596 s = i&(MAX_RAW_SAMPLES-1); 597 paintbuffer[i-s_paintedtime] = s_rawsamples[s]; 598 } 599 // if (i != end) 600 // Com_Printf ("partial stream\n"); 601 // else 602 // Com_Printf ("full stream\n"); 603 for ( ; i < end ; i++ ) { 604 paintbuffer[i-s_paintedtime].left = 605 paintbuffer[i-s_paintedtime].right = 0; 606 } 607 } 608 609 // paint in the channels. 610 ch = s_channels; 611 for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) { 612 if ( !ch->thesfx || (ch->leftvol<0.25 && ch->rightvol<0.25 )) { 613 continue; 614 } 615 616 ltime = s_paintedtime; 617 sc = ch->thesfx; 618 619 sampleOffset = ltime - ch->startSample; 620 count = end - ltime; 621 if ( sampleOffset + count > sc->soundLength ) { 622 count = sc->soundLength - sampleOffset; 623 } 624 625 if ( count > 0 ) { 626 if( sc->soundCompressionMethod == 1) { 627 S_PaintChannelFromADPCM (ch, sc, count, sampleOffset, ltime - s_paintedtime); 628 } else if( sc->soundCompressionMethod == 2) { 629 S_PaintChannelFromWavelet (ch, sc, count, sampleOffset, ltime - s_paintedtime); 630 } else if( sc->soundCompressionMethod == 3) { 631 S_PaintChannelFromMuLaw (ch, sc, count, sampleOffset, ltime - s_paintedtime); 632 } else { 633 S_PaintChannelFrom16 (ch, sc, count, sampleOffset, ltime - s_paintedtime); 634 } 635 } 636 } 637 638 // paint in the looped channels. 639 ch = loop_channels; 640 for ( i = 0; i < numLoopChannels ; i++, ch++ ) { 641 if ( !ch->thesfx || (!ch->leftvol && !ch->rightvol )) { 642 continue; 643 } 644 645 ltime = s_paintedtime; 646 sc = ch->thesfx; 647 648 if (sc->soundData==NULL || sc->soundLength==0) { 649 continue; 650 } 651 // we might have to make two passes if it 652 // is a looping sound effect and the end of 653 // the sample is hit 654 do { 655 sampleOffset = (ltime % sc->soundLength); 656 657 count = end - ltime; 658 if ( sampleOffset + count > sc->soundLength ) { 659 count = sc->soundLength - sampleOffset; 660 } 661 662 if ( count > 0 ) { 663 if( sc->soundCompressionMethod == 1) { 664 S_PaintChannelFromADPCM (ch, sc, count, sampleOffset, ltime - s_paintedtime); 665 } else if( sc->soundCompressionMethod == 2) { 666 S_PaintChannelFromWavelet (ch, sc, count, sampleOffset, ltime - s_paintedtime); 667 } else if( sc->soundCompressionMethod == 3) { 668 S_PaintChannelFromMuLaw (ch, sc, count, sampleOffset, ltime - s_paintedtime); 669 } else { 670 S_PaintChannelFrom16 (ch, sc, count, sampleOffset, ltime - s_paintedtime); 671 } 672 ltime += count; 673 } 674 } while ( ltime < end); 675 } 676 677 // transfer out according to DMA format 678 S_TransferPaintBuffer( end ); 679 s_paintedtime = end; 680 } 681 }