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 Quake III Arena source code; 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 "client.h"
25 #include "snd_local.h"
26 #if idppc_altivec && !defined(MACOS_X)
27 #include <altivec.h>
28 #endif
29 
30 static portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
31 static int snd_vol;
32 
33 int*     snd_p;
34 int      snd_linear_count;
35 short*   snd_out;
36 
37 #if	!id386                                        // if configured not to use asm
38 
S_WriteLinearBlastStereo16(void)39 void S_WriteLinearBlastStereo16 (void)
40 {
41 	int		i;
42 	int		val;
43 
44 	for (i=0 ; i<snd_linear_count ; i+=2)
45 	{
46 		val = snd_p[i]>>8;
47 		if (val > 0x7fff)
48 			snd_out[i] = 0x7fff;
49 		else if (val < -32768)
50 			snd_out[i] = -32768;
51 		else
52 			snd_out[i] = val;
53 
54 		val = snd_p[i+1]>>8;
55 		if (val > 0x7fff)
56 			snd_out[i+1] = 0x7fff;
57 		else if (val < -32768)
58 			snd_out[i+1] = -32768;
59 		else
60 			snd_out[i+1] = val;
61 	}
62 }
63 #elif defined(__GNUC__)
64 // uses snd_mixa.s
65 void S_WriteLinearBlastStereo16 (void);
66 #else
67 
S_WriteLinearBlastStereo16(void)68 __declspec( naked ) void S_WriteLinearBlastStereo16 (void)
69 {
70 	__asm {
71 
72  push edi
73  push ebx
74  mov ecx,ds:dword ptr[snd_linear_count]
75  mov ebx,ds:dword ptr[snd_p]
76  mov edi,ds:dword ptr[snd_out]
77 LWLBLoopTop:
78  mov eax,ds:dword ptr[-8+ebx+ecx*4]
79  sar eax,8
80  cmp eax,07FFFh
81  jg LClampHigh
82  cmp eax,0FFFF8000h
83  jnl LClampDone
84  mov eax,0FFFF8000h
85  jmp LClampDone
86 LClampHigh:
87  mov eax,07FFFh
88 LClampDone:
89  mov edx,ds:dword ptr[-4+ebx+ecx*4]
90  sar edx,8
91  cmp edx,07FFFh
92  jg LClampHigh2
93  cmp edx,0FFFF8000h
94  jnl LClampDone2
95  mov edx,0FFFF8000h
96  jmp LClampDone2
97 LClampHigh2:
98  mov edx,07FFFh
99 LClampDone2:
100  shl edx,16
101  and eax,0FFFFh
102  or edx,eax
103  mov ds:dword ptr[-4+edi+ecx*2],edx
104  sub ecx,2
105  jnz LWLBLoopTop
106  pop ebx
107  pop edi
108  ret
109 	}
110 }
111 
112 #endif
113 
S_TransferStereo16(unsigned long * pbuf,int endtime)114 void S_TransferStereo16 (unsigned long *pbuf, int endtime)
115 {
116 	int		lpos;
117 	int		ls_paintedtime;
118 
119 	snd_p = (int *) paintbuffer;
120 	ls_paintedtime = s_paintedtime;
121 
122 	while (ls_paintedtime < endtime)
123 	{
124 	// handle recirculating buffer issues
125 		lpos = ls_paintedtime & ((dma.samples>>1)-1);
126 
127 		snd_out = (short *) pbuf + (lpos<<1);
128 
129 		snd_linear_count = (dma.samples>>1) - lpos;
130 		if (ls_paintedtime + snd_linear_count > endtime)
131 			snd_linear_count = endtime - ls_paintedtime;
132 
133 		snd_linear_count <<= 1;
134 
135 	// write a linear blast of samples
136 		S_WriteLinearBlastStereo16 ();
137 
138 		snd_p += snd_linear_count;
139 		ls_paintedtime += (snd_linear_count>>1);
140 
141 		if( CL_VideoRecording( ) )
142 			CL_WriteAVIAudioFrame( (byte *)snd_out, snd_linear_count << 1 );
143 	}
144 }
145 
146 /*
147 ===================
148 S_TransferPaintBuffer
149 
150 ===================
151 */
S_TransferPaintBuffer(int endtime)152 void S_TransferPaintBuffer(int endtime)
153 {
154 	int 	out_idx;
155 	int 	count;
156 	int 	out_mask;
157 	int 	*p;
158 	int 	step;
159 	int		val;
160 	unsigned long *pbuf;
161 
162 	pbuf = (unsigned long *)dma.buffer;
163 
164 
165 	if ( s_testsound->integer ) {
166 		int		i;
167 		int		count;
168 
169 		// write a fixed sine wave
170 		count = (endtime - s_paintedtime);
171 		for (i=0 ; i<count ; i++)
172 			paintbuffer[i].left = paintbuffer[i].right = sin((s_paintedtime+i)*0.1)*20000*256;
173 	}
174 
175 
176 	if (dma.samplebits == 16 && dma.channels == 2)
177 	{	// optimized case
178 		S_TransferStereo16 (pbuf, endtime);
179 	}
180 	else
181 	{	// general case
182 		p = (int *) paintbuffer;
183 		count = (endtime - s_paintedtime) * dma.channels;
184 		out_mask = dma.samples - 1;
185 		out_idx = s_paintedtime * dma.channels & out_mask;
186 		step = 3 - dma.channels;
187 
188 		if (dma.samplebits == 16)
189 		{
190 			short *out = (short *) pbuf;
191 			while (count--)
192 			{
193 				val = *p >> 8;
194 				p+= step;
195 				if (val > 0x7fff)
196 					val = 0x7fff;
197 				else if (val < -32768)
198 					val = -32768;
199 				out[out_idx] = val;
200 				out_idx = (out_idx + 1) & out_mask;
201 			}
202 		}
203 		else if (dma.samplebits == 8)
204 		{
205 			unsigned char *out = (unsigned char *) pbuf;
206 			while (count--)
207 			{
208 				val = *p >> 8;
209 				p+= step;
210 				if (val > 0x7fff)
211 					val = 0x7fff;
212 				else if (val < -32768)
213 					val = -32768;
214 				out[out_idx] = (val>>8) + 128;
215 				out_idx = (out_idx + 1) & out_mask;
216 			}
217 		}
218 	}
219 }
220 
221 
222 /*
223 ===============================================================================
224 
225 CHANNEL MIXING
226 
227 ===============================================================================
228 */
229 
230 #if idppc_altivec
S_PaintChannelFrom16_altivec(channel_t * ch,const sfx_t * sc,int count,int sampleOffset,int bufferOffset)231 static void S_PaintChannelFrom16_altivec( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
232 	int						data, aoff, boff;
233 	int						leftvol, rightvol;
234 	int						i, j;
235 	portable_samplepair_t	*samp;
236 	sndBuffer				*chunk;
237 	short					*samples;
238 	float					ooff, fdata, fdiv, fleftvol, frightvol;
239 
240 	samp = &paintbuffer[ bufferOffset ];
241 
242 	if (ch->doppler) {
243 		sampleOffset = sampleOffset*ch->oldDopplerScale;
244 	}
245 
246 	chunk = sc->soundData;
247 	while (sampleOffset>=SND_CHUNK_SIZE) {
248 		chunk = chunk->next;
249 		sampleOffset -= SND_CHUNK_SIZE;
250 		if (!chunk) {
251 			chunk = sc->soundData;
252 		}
253 	}
254 
255 	if (!ch->doppler || ch->dopplerScale==1.0f) {
256 		vector signed short volume_vec;
257 		vector unsigned int volume_shift;
258 		int vectorCount, samplesLeft, chunkSamplesLeft;
259 		leftvol = ch->leftvol*snd_vol;
260 		rightvol = ch->rightvol*snd_vol;
261 		samples = chunk->sndChunk;
262 		((short *)&volume_vec)[0] = leftvol;
263 		((short *)&volume_vec)[1] = leftvol;
264 		((short *)&volume_vec)[4] = leftvol;
265 		((short *)&volume_vec)[5] = leftvol;
266 		((short *)&volume_vec)[2] = rightvol;
267 		((short *)&volume_vec)[3] = rightvol;
268 		((short *)&volume_vec)[6] = rightvol;
269 		((short *)&volume_vec)[7] = rightvol;
270 		volume_shift = vec_splat_u32(8);
271 		i = 0;
272 
273 		while(i < count) {
274 			/* Try to align destination to 16-byte boundary */
275 			while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) {
276 				data  = samples[sampleOffset++];
277 				samp[i].left += (data * leftvol)>>8;
278 				samp[i].right += (data * rightvol)>>8;
279 
280 				if (sampleOffset == SND_CHUNK_SIZE) {
281 					chunk = chunk->next;
282 					samples = chunk->sndChunk;
283 					sampleOffset = 0;
284 				}
285 				i++;
286 			}
287 			/* Destination is now aligned.  Process as many 8-sample
288 			   chunks as we can before we run out of room from the current
289 			   sound chunk.  We do 8 per loop to avoid extra source data reads. */
290 			samplesLeft = count - i;
291 			chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset;
292 			if(samplesLeft > chunkSamplesLeft)
293 				samplesLeft = chunkSamplesLeft;
294 
295 			vectorCount = samplesLeft / 8;
296 
297 			if(vectorCount)
298 			{
299 				vector unsigned char tmp;
300 				vector short s0, s1, sampleData0, sampleData1;
301 				vector signed int merge0, merge1;
302 				vector signed int d0, d1, d2, d3;
303 				vector unsigned char samplePermute0 =
304 					VECCONST_UINT8(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7);
305 				vector unsigned char samplePermute1 =
306 					VECCONST_UINT8(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 		fleftvol = ch->leftvol*snd_vol;
369 		frightvol = ch->rightvol*snd_vol;
370 
371 		ooff = sampleOffset;
372 		samples = chunk->sndChunk;
373 
374 		for ( i=0 ; i<count ; i++ ) {
375 
376 			aoff = ooff;
377 			ooff = ooff + ch->dopplerScale;
378 			boff = ooff;
379 			fdata = 0;
380 			for (j=aoff; j<boff; j++) {
381 				if (j == SND_CHUNK_SIZE) {
382 					chunk = chunk->next;
383 					if (!chunk) {
384 						chunk = sc->soundData;
385 					}
386 					samples = chunk->sndChunk;
387 					ooff -= SND_CHUNK_SIZE;
388 				}
389 				fdata  += samples[j&(SND_CHUNK_SIZE-1)];
390 			}
391 			fdiv = 256 * (boff-aoff);
392 			samp[i].left += (fdata * fleftvol)/fdiv;
393 			samp[i].right += (fdata * frightvol)/fdiv;
394 		}
395 	}
396 }
397 #endif
398 
S_PaintChannelFrom16_scalar(channel_t * ch,const sfx_t * sc,int count,int sampleOffset,int bufferOffset)399 static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
400 	int						data, aoff, boff;
401 	int						leftvol, rightvol;
402 	int						i, j;
403 	portable_samplepair_t	*samp;
404 	sndBuffer				*chunk;
405 	short					*samples;
406 	float					ooff, fdata, fdiv, fleftvol, frightvol;
407 
408 	samp = &paintbuffer[ bufferOffset ];
409 
410 	if (ch->doppler) {
411 		sampleOffset = sampleOffset*ch->oldDopplerScale;
412 	}
413 
414 	chunk = sc->soundData;
415 	while (sampleOffset>=SND_CHUNK_SIZE) {
416 		chunk = chunk->next;
417 		sampleOffset -= SND_CHUNK_SIZE;
418 		if (!chunk) {
419 			chunk = sc->soundData;
420 		}
421 	}
422 
423 	if (!ch->doppler || ch->dopplerScale==1.0f) {
424 		leftvol = ch->leftvol*snd_vol;
425 		rightvol = ch->rightvol*snd_vol;
426 		samples = chunk->sndChunk;
427 		for ( i=0 ; i<count ; i++ ) {
428 			data  = samples[sampleOffset++];
429 			samp[i].left += (data * leftvol)>>8;
430 			samp[i].right += (data * rightvol)>>8;
431 
432 			if (sampleOffset == SND_CHUNK_SIZE) {
433 				chunk = chunk->next;
434 				samples = chunk->sndChunk;
435 				sampleOffset = 0;
436 			}
437 		}
438 	} else {
439 		fleftvol = ch->leftvol*snd_vol;
440 		frightvol = ch->rightvol*snd_vol;
441 
442 		ooff = sampleOffset;
443 		samples = chunk->sndChunk;
444 
445 
446 
447 
448 		for ( i=0 ; i<count ; i++ ) {
449 
450 			aoff = ooff;
451 			ooff = ooff + ch->dopplerScale;
452 			boff = ooff;
453 			fdata = 0;
454 			for (j=aoff; j<boff; j++) {
455 				if (j == SND_CHUNK_SIZE) {
456 					chunk = chunk->next;
457 					if (!chunk) {
458 						chunk = sc->soundData;
459 					}
460 					samples = chunk->sndChunk;
461 					ooff -= SND_CHUNK_SIZE;
462 				}
463 				fdata  += samples[j&(SND_CHUNK_SIZE-1)];
464 			}
465 			fdiv = 256 * (boff-aoff);
466 			samp[i].left += (fdata * fleftvol)/fdiv;
467 			samp[i].right += (fdata * frightvol)/fdiv;
468 		}
469 	}
470 }
471 
S_PaintChannelFrom16(channel_t * ch,const sfx_t * sc,int count,int sampleOffset,int bufferOffset)472 static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
473 #if idppc_altivec
474 	if (com_altivec->integer) {
475 		// must be in a seperate function or G3 systems will crash.
476 		S_PaintChannelFrom16_altivec( ch, sc, count, sampleOffset, bufferOffset );
477 		return;
478 	}
479 #endif
480 	S_PaintChannelFrom16_scalar( ch, sc, count, sampleOffset, bufferOffset );
481 }
482 
S_PaintChannelFromWavelet(channel_t * ch,sfx_t * sc,int count,int sampleOffset,int bufferOffset)483 void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
484 	int						data;
485 	int						leftvol, rightvol;
486 	int						i;
487 	portable_samplepair_t	*samp;
488 	sndBuffer				*chunk;
489 	short					*samples;
490 
491 	leftvol = ch->leftvol*snd_vol;
492 	rightvol = ch->rightvol*snd_vol;
493 
494 	i = 0;
495 	samp = &paintbuffer[ bufferOffset ];
496 	chunk = sc->soundData;
497 	while (sampleOffset>=(SND_CHUNK_SIZE_FLOAT*4)) {
498 		chunk = chunk->next;
499 		sampleOffset -= (SND_CHUNK_SIZE_FLOAT*4);
500 		i++;
501 	}
502 
503 	if (i!=sfxScratchIndex || sfxScratchPointer != sc) {
504 		S_AdpcmGetSamples( chunk, sfxScratchBuffer );
505 		sfxScratchIndex = i;
506 		sfxScratchPointer = sc;
507 	}
508 
509 	samples = sfxScratchBuffer;
510 
511 	for ( i=0 ; i<count ; i++ ) {
512 		data  = samples[sampleOffset++];
513 		samp[i].left += (data * leftvol)>>8;
514 		samp[i].right += (data * rightvol)>>8;
515 
516 		if (sampleOffset == SND_CHUNK_SIZE*2) {
517 			chunk = chunk->next;
518 			decodeWavelet(chunk, sfxScratchBuffer);
519 			sfxScratchIndex++;
520 			sampleOffset = 0;
521 		}
522 	}
523 }
524 
S_PaintChannelFromADPCM(channel_t * ch,sfx_t * sc,int count,int sampleOffset,int bufferOffset)525 void S_PaintChannelFromADPCM( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
526 	int						data;
527 	int						leftvol, rightvol;
528 	int						i;
529 	portable_samplepair_t	*samp;
530 	sndBuffer				*chunk;
531 	short					*samples;
532 
533 	leftvol = ch->leftvol*snd_vol;
534 	rightvol = ch->rightvol*snd_vol;
535 
536 	i = 0;
537 	samp = &paintbuffer[ bufferOffset ];
538 	chunk = sc->soundData;
539 
540 	if (ch->doppler) {
541 		sampleOffset = sampleOffset*ch->oldDopplerScale;
542 	}
543 
544 	while (sampleOffset>=(SND_CHUNK_SIZE*4)) {
545 		chunk = chunk->next;
546 		sampleOffset -= (SND_CHUNK_SIZE*4);
547 		i++;
548 	}
549 
550 	if (i!=sfxScratchIndex || sfxScratchPointer != sc) {
551 		S_AdpcmGetSamples( chunk, sfxScratchBuffer );
552 		sfxScratchIndex = i;
553 		sfxScratchPointer = sc;
554 	}
555 
556 	samples = sfxScratchBuffer;
557 
558 	for ( i=0 ; i<count ; i++ ) {
559 		data  = samples[sampleOffset++];
560 		samp[i].left += (data * leftvol)>>8;
561 		samp[i].right += (data * rightvol)>>8;
562 
563 		if (sampleOffset == SND_CHUNK_SIZE*4) {
564 			chunk = chunk->next;
565 			S_AdpcmGetSamples( chunk, sfxScratchBuffer);
566 			sampleOffset = 0;
567 			sfxScratchIndex++;
568 		}
569 	}
570 }
571 
S_PaintChannelFromMuLaw(channel_t * ch,sfx_t * sc,int count,int sampleOffset,int bufferOffset)572 void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
573 	int						data;
574 	int						leftvol, rightvol;
575 	int						i;
576 	portable_samplepair_t	*samp;
577 	sndBuffer				*chunk;
578 	byte					*samples;
579 	float					ooff;
580 
581 	leftvol = ch->leftvol*snd_vol;
582 	rightvol = ch->rightvol*snd_vol;
583 
584 	samp = &paintbuffer[ bufferOffset ];
585 	chunk = sc->soundData;
586 	while (sampleOffset>=(SND_CHUNK_SIZE*2)) {
587 		chunk = chunk->next;
588 		sampleOffset -= (SND_CHUNK_SIZE*2);
589 		if (!chunk) {
590 			chunk = sc->soundData;
591 		}
592 	}
593 
594 	if (!ch->doppler) {
595 		samples = (byte *)chunk->sndChunk + sampleOffset;
596 		for ( i=0 ; i<count ; i++ ) {
597 			data  = mulawToShort[*samples];
598 			samp[i].left += (data * leftvol)>>8;
599 			samp[i].right += (data * rightvol)>>8;
600 			samples++;
601 			if (samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) {
602 				chunk = chunk->next;
603 				samples = (byte *)chunk->sndChunk;
604 			}
605 		}
606 	} else {
607 		ooff = sampleOffset;
608 		samples = (byte *)chunk->sndChunk;
609 		for ( i=0 ; i<count ; i++ ) {
610 			data  = mulawToShort[samples[(int)(ooff)]];
611 			ooff = ooff + ch->dopplerScale;
612 			samp[i].left += (data * leftvol)>>8;
613 			samp[i].right += (data * rightvol)>>8;
614 			if (ooff >= SND_CHUNK_SIZE*2) {
615 				chunk = chunk->next;
616 				if (!chunk) {
617 					chunk = sc->soundData;
618 				}
619 				samples = (byte *)chunk->sndChunk;
620 				ooff = 0.0;
621 			}
622 		}
623 	}
624 }
625 
626 /*
627 ===================
628 S_PaintChannels
629 ===================
630 */
S_PaintChannels(int endtime)631 void S_PaintChannels( int endtime ) {
632 	int 	i;
633 	int 	end;
634 	channel_t *ch;
635 	sfx_t	*sc;
636 	int		ltime, count;
637 	int		sampleOffset;
638 
639 
640 	snd_vol = s_volume->value*255;
641 
642 //Com_Printf ("%i to %i\n", s_paintedtime, endtime);
643 	while ( s_paintedtime < endtime ) {
644 		// if paintbuffer is smaller than DMA buffer
645 		// we may need to fill it multiple times
646 		end = endtime;
647 		if ( endtime - s_paintedtime > PAINTBUFFER_SIZE ) {
648 			end = s_paintedtime + PAINTBUFFER_SIZE;
649 		}
650 
651 		// clear the paint buffer to either music or zeros
652 		if ( s_rawend < s_paintedtime ) {
653 			if ( s_rawend ) {
654 				//Com_DPrintf ("background sound underrun\n");
655 			}
656 			Com_Memset(paintbuffer, 0, (end - s_paintedtime) * sizeof(portable_samplepair_t));
657 		} else {
658 			// copy from the streaming sound source
659 			int		s;
660 			int		stop;
661 
662 			stop = (end < s_rawend) ? end : s_rawend;
663 
664 			for ( i = s_paintedtime ; i < stop ; i++ ) {
665 				s = i&(MAX_RAW_SAMPLES-1);
666 				paintbuffer[i-s_paintedtime] = s_rawsamples[s];
667 			}
668 //		if (i != end)
669 //			Com_Printf ("partial stream\n");
670 //		else
671 //			Com_Printf ("full stream\n");
672 			for ( ; i < end ; i++ ) {
673 				paintbuffer[i-s_paintedtime].left =
674 				paintbuffer[i-s_paintedtime].right = 0;
675 			}
676 		}
677 
678 		// paint in the channels.
679 		ch = s_channels;
680 		for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) {
681 			if ( !ch->thesfx || (ch->leftvol<0.25 && ch->rightvol<0.25 )) {
682 				continue;
683 			}
684 
685 			ltime = s_paintedtime;
686 			sc = ch->thesfx;
687 
688 			sampleOffset = ltime - ch->startSample;
689 			count = end - ltime;
690 			if ( sampleOffset + count > sc->soundLength ) {
691 				count = sc->soundLength - sampleOffset;
692 			}
693 
694 			if ( count > 0 ) {
695 				if( sc->soundCompressionMethod == 1) {
696 					S_PaintChannelFromADPCM		(ch, sc, count, sampleOffset, ltime - s_paintedtime);
697 				} else if( sc->soundCompressionMethod == 2) {
698 					S_PaintChannelFromWavelet	(ch, sc, count, sampleOffset, ltime - s_paintedtime);
699 				} else if( sc->soundCompressionMethod == 3) {
700 					S_PaintChannelFromMuLaw	(ch, sc, count, sampleOffset, ltime - s_paintedtime);
701 				} else {
702 					S_PaintChannelFrom16		(ch, sc, count, sampleOffset, ltime - s_paintedtime);
703 				}
704 			}
705 		}
706 
707 		// paint in the looped channels.
708 		ch = loop_channels;
709 		for ( i = 0; i < numLoopChannels ; i++, ch++ ) {
710 			if ( !ch->thesfx || (!ch->leftvol && !ch->rightvol )) {
711 				continue;
712 			}
713 
714 			ltime = s_paintedtime;
715 			sc = ch->thesfx;
716 
717 			if (sc->soundData==NULL || sc->soundLength==0) {
718 				continue;
719 			}
720 			// we might have to make two passes if it
721 			// is a looping sound effect and the end of
722 			// the sample is hit
723 			do {
724 				sampleOffset = (ltime % sc->soundLength);
725 
726 				count = end - ltime;
727 				if ( sampleOffset + count > sc->soundLength ) {
728 					count = sc->soundLength - sampleOffset;
729 				}
730 
731 				if ( count > 0 ) {
732 					if( sc->soundCompressionMethod == 1) {
733 						S_PaintChannelFromADPCM		(ch, sc, count, sampleOffset, ltime - s_paintedtime);
734 					} else if( sc->soundCompressionMethod == 2) {
735 						S_PaintChannelFromWavelet	(ch, sc, count, sampleOffset, ltime - s_paintedtime);
736 					} else if( sc->soundCompressionMethod == 3) {
737 						S_PaintChannelFromMuLaw		(ch, sc, count, sampleOffset, ltime - s_paintedtime);
738 					} else {
739 						S_PaintChannelFrom16		(ch, sc, count, sampleOffset, ltime - s_paintedtime);
740 					}
741 					ltime += count;
742 				}
743 			} while ( ltime < end);
744 		}
745 
746 		// transfer out according to DMA format
747 		S_TransferPaintBuffer( end );
748 		s_paintedtime = end;
749 	}
750 }
751