ref: 372afde46e7defc9dd2d719a1732b8ace1fa096e
dir: /client/snd_mix.c/
/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // snd_mix.c -- portable code to mix sounds for snd_dma.c #include "client.h" #include "snd_loc.h" #define PAINTBUFFER_SIZE 2048 portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; int snd_scaletable[32][256]; int *snd_p, snd_linear_count, snd_vol; short *snd_out; void S_WriteLinearBlastStereo16 (void); #if !(defined __linux__ && defined __i386__) #if !id386 void S_WriteLinearBlastStereo16 (void) { int i; int val; for (i=0 ; i<snd_linear_count ; i+=2) { val = snd_p[i]>>8; if (val > 0x7fff) snd_out[i] = 0x7fff; else if (val < (short)0x8000) snd_out[i] = (short)0x8000; else snd_out[i] = val; val = snd_p[i+1]>>8; if (val > 0x7fff) snd_out[i+1] = 0x7fff; else if (val < (short)0x8000) snd_out[i+1] = (short)0x8000; else snd_out[i+1] = val; } } #else __declspec( naked ) void S_WriteLinearBlastStereo16 (void) { __asm { push edi push ebx mov ecx,ds:dword ptr[snd_linear_count] mov ebx,ds:dword ptr[snd_p] mov edi,ds:dword ptr[snd_out] LWLBLoopTop: mov eax,ds:dword ptr[-8+ebx+ecx*4] sar eax,8 cmp eax,07FFFh jg LClampHigh cmp eax,0FFFF8000h jnl LClampDone mov eax,0FFFF8000h jmp LClampDone LClampHigh: mov eax,07FFFh LClampDone: mov edx,ds:dword ptr[-4+ebx+ecx*4] sar edx,8 cmp edx,07FFFh jg LClampHigh2 cmp edx,0FFFF8000h jnl LClampDone2 mov edx,0FFFF8000h jmp LClampDone2 LClampHigh2: mov edx,07FFFh LClampDone2: shl edx,16 and eax,0FFFFh or edx,eax mov ds:dword ptr[-4+edi+ecx*2],edx sub ecx,2 jnz LWLBLoopTop pop ebx pop edi ret } } #endif #endif void S_TransferStereo16 (unsigned long *pbuf, int endtime) { int lpos; int lpaintedtime; snd_p = (int *) paintbuffer; lpaintedtime = paintedtime; while (lpaintedtime < endtime) { // handle recirculating buffer issues lpos = lpaintedtime & ((dma.samples>>1)-1); snd_out = (short *) pbuf + (lpos<<1); snd_linear_count = (dma.samples>>1) - lpos; if (lpaintedtime + snd_linear_count > endtime) snd_linear_count = endtime - lpaintedtime; snd_linear_count <<= 1; // write a linear blast of samples S_WriteLinearBlastStereo16 (); snd_p += snd_linear_count; lpaintedtime += (snd_linear_count>>1); } } /* =================== S_TransferPaintBuffer =================== */ void S_TransferPaintBuffer(int endtime) { int out_idx; int count; int out_mask; int *p; int step; int val; unsigned long *pbuf; pbuf = (unsigned long *)dma.buffer; if (s_testsound->value) { int i; int count; // write a fixed sine wave count = (endtime - paintedtime); for (i=0 ; i<count ; i++) paintbuffer[i].left = paintbuffer[i].right = sin((paintedtime+i)*0.1)*20000*256; } if (dma.samplebits == 16 && dma.channels == 2) { // optimized case S_TransferStereo16 (pbuf, endtime); } else { // general case p = (int *) paintbuffer; count = (endtime - paintedtime) * dma.channels; out_mask = dma.samples - 1; out_idx = paintedtime * dma.channels & out_mask; step = 3 - dma.channels; if (dma.samplebits == 16) { short *out = (short *) pbuf; while (count--) { val = *p >> 8; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = val; out_idx = (out_idx + 1) & out_mask; } } else if (dma.samplebits == 8) { unsigned char *out = (unsigned char *) pbuf; while (count--) { val = *p >> 8; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = (val>>8) + 128; out_idx = (out_idx + 1) & out_mask; } } } } /* =============================================================================== CHANNEL MIXING =============================================================================== */ void S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime, int offset); void S_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime, int offset); void S_PaintChannels(int endtime) { int i; int end; channel_t *ch; sfxcache_t *sc; int ltime, count; playsound_t *ps; snd_vol = s_volume->value*256; //Com_Printf ("%i to %i\n", paintedtime, endtime); while (paintedtime < endtime) { // if paintbuffer is smaller than DMA buffer end = endtime; if (endtime - paintedtime > PAINTBUFFER_SIZE) end = paintedtime + PAINTBUFFER_SIZE; // start any playsounds while (1) { ps = s_pendingplays.next; if (ps == &s_pendingplays) break; // no more pending sounds if (ps->begin <= paintedtime) { S_IssuePlaysound (ps); continue; } if (ps->begin < end) end = ps->begin; // stop here break; } // clear the paint buffer if (s_rawend < paintedtime) { // Com_Printf ("clear\n"); memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); } else { // copy from the streaming sound source int s; int stop; stop = (end < s_rawend) ? end : s_rawend; for (i=paintedtime ; i<stop ; i++) { s = i&(MAX_RAW_SAMPLES-1); paintbuffer[i-paintedtime] = s_rawsamples[s]; } // if (i != end) // Com_Printf ("partial stream\n"); // else // Com_Printf ("full stream\n"); for ( ; i<end ; i++) { paintbuffer[i-paintedtime].left = paintbuffer[i-paintedtime].right = 0; } } // paint in the channels. ch = channels; for (i=0; i<MAX_CHANNELS ; i++, ch++) { ltime = paintedtime; while (ltime < end) { if (!ch->sfx || (!ch->leftvol && !ch->rightvol) ) break; // max painting is to the end of the buffer count = end - ltime; // might be stopped by running out of data if (ch->end - ltime < count) count = ch->end - ltime; sc = S_LoadSound (ch->sfx); if (!sc) break; if (count > 0 && ch->sfx) { if (sc->width == 1)// FIXME; 8 bit asm is wrong now S_PaintChannelFrom8(ch, sc, count, ltime - paintedtime); else S_PaintChannelFrom16(ch, sc, count, ltime - paintedtime); ltime += count; } // if at end of loop, restart if (ltime >= ch->end) { if (ch->autosound) { // autolooping sounds always go back to start ch->pos = 0; ch->end = ltime + sc->length; } else if (sc->loopstart >= 0) { ch->pos = sc->loopstart; ch->end = ltime + sc->length - ch->pos; } else { // channel just stopped ch->sfx = NULL; } } } } // transfer out according to DMA format S_TransferPaintBuffer(end); paintedtime = end; } } void S_InitScaletable (void) { int i, j; int scale; s_volume->modified = false; for (i=0 ; i<32 ; i++) { scale = i * 8 * 256 * s_volume->value; for (j=0 ; j<256 ; j++) snd_scaletable[i][j] = ((signed char)j) * scale; } } #if !(defined __linux__ && defined __i386__) #if !id386 void S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset) { int data; int *lscale, *rscale; unsigned char *sfx; int i; portable_samplepair_t *samp; if (ch->leftvol > 255) ch->leftvol = 255; if (ch->rightvol > 255) ch->rightvol = 255; lscale = snd_scaletable[ ch->leftvol >> 11]; rscale = snd_scaletable[ ch->rightvol >> 11]; sfx = (signed char *)sc->data + ch->pos; samp = &paintbuffer[offset]; for (i=0 ; i<count ; i++, samp++) { data = sfx[i]; samp->left += lscale[data]; samp->right += rscale[data]; } ch->pos += count; } #else __declspec( naked ) void S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset) { __asm { push esi push edi push ebx push ebp mov ebx,ds:dword ptr[4+16+esp] mov esi,ds:dword ptr[8+16+esp] mov eax,ds:dword ptr[4+ebx] mov edx,ds:dword ptr[8+ebx] cmp eax,255 jna LLeftSet mov eax,255 LLeftSet: cmp edx,255 jna LRightSet mov edx,255 LRightSet: and eax,0F8h add esi,20 and edx,0F8h mov edi,ds:dword ptr[16+ebx] mov ecx,ds:dword ptr[12+16+esp] add esi,edi shl eax,7 add edi,ecx shl edx,7 mov ds:dword ptr[16+ebx],edi add eax,offset snd_scaletable add edx,offset snd_scaletable sub ebx,ebx mov bl,ds:byte ptr[-1+esi+ecx*1] test ecx,1 jz LMix8Loop mov edi,ds:dword ptr[eax+ebx*4] mov ebp,ds:dword ptr[edx+ebx*4] add edi,ds:dword ptr[paintbuffer+0-8+ecx*8] add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8] mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp mov bl,ds:byte ptr[-2+esi+ecx*1] dec ecx jz LDone LMix8Loop: mov edi,ds:dword ptr[eax+ebx*4] mov ebp,ds:dword ptr[edx+ebx*4] add edi,ds:dword ptr[paintbuffer+0-8+ecx*8] add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8] mov bl,ds:byte ptr[-2+esi+ecx*1] mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp mov edi,ds:dword ptr[eax+ebx*4] mov ebp,ds:dword ptr[edx+ebx*4] mov bl,ds:byte ptr[-3+esi+ecx*1] add edi,ds:dword ptr[paintbuffer+0-8*2+ecx*8] add ebp,ds:dword ptr[paintbuffer+4-8*2+ecx*8] mov ds:dword ptr[paintbuffer+0-8*2+ecx*8],edi mov ds:dword ptr[paintbuffer+4-8*2+ecx*8],ebp sub ecx,2 jnz LMix8Loop LDone: pop ebp pop ebx pop edi pop esi ret } } #endif #endif void S_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count, int offset) { int data; int left, right; int leftvol, rightvol; signed short *sfx; int i; portable_samplepair_t *samp; leftvol = ch->leftvol*snd_vol; rightvol = ch->rightvol*snd_vol; sfx = (signed short *)sc->data + ch->pos; samp = &paintbuffer[offset]; for (i=0 ; i<count ; i++, samp++) { data = sfx[i]; left = (data * leftvol)>>8; right = (data * rightvol)>>8; samp->left += left; samp->right += right; } ch->pos += count; }