Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

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