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 //
21 // snd_dma.c
22 // Main control for any streaming sound output device
23 //
24 
25 #include "snd_local.h"
26 
27 typedef struct sfxSamplePair_s {
28 	int				left;
29 	int				right;
30 } sfxSamplePair_t;
31 
32 audioDMA_t				snd_audioDMA;
33 
34 // Audio channels
35 static channel_t		snd_dmaOutChannels[MAX_CHANNELS];
36 
37 // Raw sampling
38 #define MAX_RAW_SAMPLES	8192
39 static int				snd_dmaRawEnd;
40 static sfxSamplePair_t	snd_dmaRawSamples[MAX_RAW_SAMPLES];
41 
42 // Buffer painting
43 #define SND_PBUFFER		2048
44 static sfxSamplePair_t	snd_dmaPaintBuffer[SND_PBUFFER];
45 static int				snd_dmaScaleTable[32][256];
46 static int				*snd_dmaMixPointer;
47 static int				snd_dmaLinearCount;
48 static int16			*snd_dmaBufferOutput;
49 
50 static int				snd_dmaSoundTime;	// sample PAIRS
51 int						snd_dmaPaintedTime;	// sample PAIRS
52 
53 // Orientation
54 static vec3_t			snd_dmaOrigin;
55 static vec3_t			snd_dmaRightVec;
56 
57 /*
58 ================
59 DMASnd_ScaleTableInit
60 ================
61 */
DMASnd_ScaleTableInit(void)62 static void DMASnd_ScaleTableInit (void)
63 {
64 	int		i, j;
65 	int		scale;
66 
67 	s_volume->modified = qFalse;
68 	for (i=0 ; i<32 ; i++) {
69 		scale = i * 8 * 256 * s_volume->floatVal;
70 		for (j=0 ; j<256 ; j++) {
71 			snd_dmaScaleTable[i][j] = ((signed char)j) * scale;
72 		}
73 	}
74 }
75 
76 /*
77 ===============================================================================
78 
79 	BUFFER WRITING
80 
81 ===============================================================================
82 */
83 
84 /*
85 ================
86 DMASnd_WriteLinearBlastStereo16
87 ================
88 */
DMASnd_WriteLinearBlastStereo16(void)89 static void DMASnd_WriteLinearBlastStereo16 (void)
90 {
91 	int		i;
92 	int		val;
93 
94 	for (i=0 ; i<snd_dmaLinearCount ; i+=2) {
95 		val = snd_dmaMixPointer[i]>>8;
96 		if (val > 0x7fff)
97 			snd_dmaBufferOutput[i] = 0x7fff;
98 		else if (val < (int16)0x8000)
99 			snd_dmaBufferOutput[i] = (int16)0x8000;
100 		else
101 			snd_dmaBufferOutput[i] = val;
102 
103 		val = snd_dmaMixPointer[i+1]>>8;
104 		if (val > 0x7fff)
105 			snd_dmaBufferOutput[i+1] = 0x7fff;
106 		else if (val < (int16)0x8000)
107 			snd_dmaBufferOutput[i+1] = (int16)0x8000;
108 		else
109 			snd_dmaBufferOutput[i+1] = val;
110 	}
111 }
112 
113 
114 /*
115 ================
116 DMASnd_TransferStereo16
117 ================
118 */
DMASnd_TransferStereo16(uint32 * pbuf,int endTime)119 static void DMASnd_TransferStereo16 (uint32 *pbuf, int endTime)
120 {
121 	int		lpos;
122 	int		lpaintedtime;
123 
124 	snd_dmaMixPointer = (int *) snd_dmaPaintBuffer;
125 	lpaintedtime = snd_dmaPaintedTime;
126 
127 	while (lpaintedtime < endTime) {
128 		// Handle recirculating buffer issues
129 		lpos = lpaintedtime & ((snd_audioDMA.samples>>1)-1);
130 
131 		snd_dmaBufferOutput = (int16 *) pbuf + (lpos<<1);
132 		snd_dmaLinearCount = (snd_audioDMA.samples>>1) - lpos;
133 		if (lpaintedtime + snd_dmaLinearCount > endTime)
134 			snd_dmaLinearCount = endTime - lpaintedtime;
135 
136 		snd_dmaLinearCount <<= 1;
137 
138 		// Write a linear blast of samples
139 		DMASnd_WriteLinearBlastStereo16 ();
140 
141 		snd_dmaMixPointer += snd_dmaLinearCount;
142 		lpaintedtime += (snd_dmaLinearCount>>1);
143 	}
144 }
145 
146 
147 /*
148 ===================
149 DMASnd_TransferPaintBuffer
150 ===================
151 */
DMASnd_TransferPaintBuffer(int endTime)152 static void DMASnd_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 	uint32	*pbuf;
161 
162 	pbuf = (uint32 *)snd_audioDMA.buffer;
163 
164 	if (s_testsound->intVal) {
165 		int		i;
166 		int		count;
167 
168 		// Write a fixed sine wave
169 		count = (endTime - snd_dmaPaintedTime);
170 		for (i=0 ; i<count ; i++)
171 			snd_dmaPaintBuffer[i].left = snd_dmaPaintBuffer[i].right = sin ((snd_dmaPaintedTime+i)*0.1)*20000*256;
172 	}
173 
174 	if (snd_audioDMA.sampleBits == 16 && snd_audioDMA.channels == 2) {
175 		// Optimized case
176 		DMASnd_TransferStereo16 (pbuf, endTime);
177 	}
178 	else {
179 		// General case
180 		p = (int *) snd_dmaPaintBuffer;
181 		count = (endTime - snd_dmaPaintedTime) * snd_audioDMA.channels;
182 		out_mask = snd_audioDMA.samples - 1;
183 		out_idx = snd_dmaPaintedTime * snd_audioDMA.channels & out_mask;
184 		step = 3 - snd_audioDMA.channels;
185 
186 		if (snd_audioDMA.sampleBits == 16) {
187 			int16 *out = (int16 *) pbuf;
188 			while (count--) {
189 				val = *p >> 8;
190 				p += step;
191 				if (val > 0x7fff)
192 					val = 0x7fff;
193 				else if (val < (int16)0x8000)
194 					val = (int16)0x8000;
195 				out[out_idx] = val;
196 				out_idx = (out_idx + 1) & out_mask;
197 			}
198 		}
199 		else if (snd_audioDMA.sampleBits == 8) {
200 			byte	*out = (byte *) pbuf;
201 			while (count--) {
202 				val = *p >> 8;
203 				p += step;
204 				if (val > 0x7fff)
205 					val = 0x7fff;
206 				else if (val < (int16)0x8000)
207 					val = (int16)0x8000;
208 				out[out_idx] = (val>>8) + 128;
209 				out_idx = (out_idx + 1) & out_mask;
210 			}
211 		}
212 	}
213 }
214 
215 /*
216 ===============================================================================
217 
218 	SPATIALIZATION
219 
220 ===============================================================================
221 */
222 
223 /*
224 =================
225 DMASnd_SpatializeOrigin
226 
227 Used for spatializing channels and autosounds
228 =================
229 */
DMASnd_SpatializeOrigin(vec3_t origin,float masterVol,float distMult,int * leftVol,int * rightVol)230 static void DMASnd_SpatializeOrigin (vec3_t origin, float masterVol, float distMult, int *leftVol, int *rightVol)
231 {
232 	float		dot, dist;
233 	float		leftScale, rightScale, scale;
234 	vec3_t		sourceVec;
235 
236 	if (Com_ClientState () != CA_ACTIVE) {
237 		*leftVol = *rightVol = 255;
238 		return;
239 	}
240 
241 	// Calculate stereo seperation and distance attenuation
242 	Vec3Subtract (origin, snd_dmaOrigin, sourceVec);
243 
244 	dist = VectorNormalizef (sourceVec, sourceVec) - SOUND_FULLVOLUME;
245 	if (dist < 0)
246 		dist = 0;			// close enough to be at full volume
247 	dist *= distMult;		// different attenuation levels
248 
249 	dot = DotProduct (snd_dmaRightVec, sourceVec);
250 
251 	if (snd_audioDMA.channels == 1 || !distMult) {
252 		// No attenuation = no spatialization
253 		rightScale = 1.0f;
254 		leftScale = 1.0f;
255 	}
256 	else {
257 		rightScale = 0.5f * (1.0f + dot);
258 		leftScale = 0.5f * (1.0f - dot);
259 	}
260 
261 	// Add in distance effect
262 	scale = (1.0 - dist) * rightScale;
263 	*rightVol = Q_rint (masterVol * scale);
264 	if (*rightVol < 0)
265 		*rightVol = 0;
266 
267 	scale = (1.0 - dist) * leftScale;
268 	*leftVol = Q_rint (masterVol * scale);
269 	if (*leftVol < 0)
270 		*leftVol = 0;
271 }
272 
273 
274 /*
275 =================
276 DMASnd_SpatializeChannel
277 =================
278 */
DMASnd_SpatializeChannel(channel_t * ch)279 static void DMASnd_SpatializeChannel (channel_t *ch)
280 {
281 	vec3_t		origin, velocity;
282 
283 	switch (ch->psType) {
284 	case PSND_FIXED:
285 		Vec3Copy (ch->origin, origin);
286 		break;
287 
288 	case PSND_ENTITY:
289 		CL_CGModule_GetEntitySoundOrigin (ch->entNum, origin, velocity);
290 		break;
291 
292 	case PSND_LOCAL:
293 		// Anything coming from the view entity will always be full volume
294 		ch->leftVol = ch->masterVol;
295 		ch->rightVol = ch->masterVol;
296 		return;
297 	}
298 
299 	// Spatialize fixed/entity sounds
300 	DMASnd_SpatializeOrigin (origin, ch->masterVol, ch->distMult, &ch->leftVol, &ch->rightVol);
301 }
302 
303 /*
304 ===============================================================================
305 
306 	CHANNELS
307 
308 ===============================================================================
309 */
310 
311 /*
312 =================
313 DMASnd_PickChannel
314 =================
315 */
DMASnd_PickChannel(int entNum,entChannel_t entChannel)316 static channel_t *DMASnd_PickChannel (int entNum, entChannel_t entChannel)
317 {
318 	int			i;
319 	int			firstToDie;
320 	int			lifeLeft;
321 	channel_t	*ch;
322 
323 	firstToDie = -1;
324 	lifeLeft = 0x7fffffff;
325 
326 	// Check for replacement sound, or find the best one to replace
327 	for (i=0, ch=snd_dmaOutChannels ; i<MAX_CHANNELS ; ch++, i++) {
328 		// Channel 0 never overrides
329 		if (entChannel != 0 && ch->entNum == entNum && ch->entChannel == entChannel) {
330 			// Always override sound from same entity
331 			firstToDie = i;
332 			break;
333 		}
334 
335 		// Don't let monster sounds override player sounds
336 		if (ch->entNum == cl.playerNum+1 && entNum != cl.playerNum+1 && ch->sfx)
337 			continue;
338 
339 		// Replace the oldest sound
340 		if (ch->endTime - snd_dmaPaintedTime < lifeLeft) {
341 			lifeLeft = ch->endTime - snd_dmaPaintedTime;
342 			firstToDie = i;
343 		}
344    }
345 
346 	if (firstToDie == -1)
347 		return NULL;
348 
349 	ch = &snd_dmaOutChannels[firstToDie];
350 	memset (ch, 0, sizeof (channel_t));
351 	return ch;
352 }
353 
354 /*
355 ===============================================================================
356 
357 	PLAYSOUNDS
358 
359 ===============================================================================
360 */
361 
362 /*
363 ===============
364 DMASnd_IssuePlaysound
365 
366 Take the next playsound and begin it on the channel. This is never
367 called directly by Snd_Play*, but only by the update loop.
368 ===============
369 */
DMASnd_IssuePlaysounds(int endTime)370 static void DMASnd_IssuePlaysounds (int endTime)
371 {
372 	channel_t	*ch;
373 	sfxCache_t	*sc;
374 	playSound_t	*ps;
375 
376 	for ( ; ; ) {
377 		ps = snd_pendingPlays.next;
378 		if (ps == &snd_pendingPlays)
379 			break;	// No more pending sounds
380 		if (ps->beginTime > snd_dmaPaintedTime) {
381 			if (ps->beginTime < endTime)
382 				endTime = ps->beginTime;	// Stop here
383 			break;
384 		}
385 
386 		if (s_show->intVal)
387 			Com_Printf (0, "Issue %i\n", ps->beginTime);
388 
389 		// Pick a channel to play on
390 		ch = DMASnd_PickChannel (ps->entNum, ps->entChannel);
391 		if (!ch) {
392 			Snd_FreePlaysound (ps);
393 			return;
394 		}
395 
396 		// Spatialize
397 		if (ps->attenuation == ATTN_STATIC)
398 			ch->distMult = ps->attenuation * 0.001f;
399 		else
400 			ch->distMult = ps->attenuation * 0.0005f;
401 
402 		ch->masterVol = ps->volume;
403 		ch->entNum = ps->entNum;
404 		ch->entChannel = ps->entChannel;
405 		ch->sfx = ps->sfx;
406 		Vec3Copy (ps->origin, ch->origin);
407 		ch->psType = ps->type;
408 
409 		DMASnd_SpatializeChannel (ch);
410 
411 		ch->position = 0;
412 		sc = Snd_LoadSound (ch->sfx);
413 		ch->endTime = snd_dmaPaintedTime + sc->length;
414 
415 		// Free the playsound
416 		Snd_FreePlaysound (ps);
417 	}
418 }
419 
420 /*
421 ===============================================================================
422 
423 	SOUND PLAYING
424 
425 ===============================================================================
426 */
427 
428 /*
429 ==================
430 DMASnd_ClearBuffer
431 ==================
432 */
DMASnd_ClearBuffer(void)433 static void DMASnd_ClearBuffer (void)
434 {
435 	int		clear;
436 
437 	// Clear the buffers
438 	snd_dmaRawEnd = 0;
439 	if (snd_audioDMA.sampleBits == 8)
440 		clear = 0x80;
441 	else
442 		clear = 0;
443 
444 	SndImp_BeginPainting ();
445 	if (snd_audioDMA.buffer)
446 		memset (snd_audioDMA.buffer, clear, snd_audioDMA.samples * snd_audioDMA.sampleBits/8);
447 	SndImp_Submit ();
448 }
449 
450 
451 /*
452 ==================
453 DMASnd_StopAllSounds
454 ==================
455 */
DMASnd_StopAllSounds(void)456 void DMASnd_StopAllSounds (void)
457 {
458 	// Clear all the channels
459 	memset (snd_dmaOutChannels, 0, sizeof (snd_dmaOutChannels));
460 
461 	// Clear the buffers
462 	DMASnd_ClearBuffer ();
463 }
464 
465 /*
466 ===============================================================================
467 
468 	CHANNEL MIXING
469 
470 ===============================================================================
471 */
472 
473 /*
474 ==================
475 DMASnd_AddLoopSounds
476 
477 Entities with an ent->sound field will generated looped sounds that are automatically
478 started, stopped, and merged together as the entities are sent to the client
479 ==================
480 */
DMASnd_AddLoopSounds(void)481 static void DMASnd_AddLoopSounds (void)
482 {
483 	int				i, j;
484 	int				sounds[MAX_CS_EDICTS];
485 	int				left, right;
486 	int				leftTotal, rightTotal;
487 	channel_t		*ch;
488 	sfx_t			*sfx;
489 	sfxCache_t		*sc;
490 	int				num;
491 	entityState_t	*ent;
492 	vec3_t			origin, velocity;
493 
494 	if (cl_paused->intVal || Com_ClientState () != CA_ACTIVE || !cls.soundPrepped)
495 		return;
496 
497 	// Build a sound list
498 	for (i=0 ; i<cl.frame.numEntities ; i++) {
499 		num = (cl.frame.parseEntities + i)&(MAX_PARSEENTITIES_MASK);
500 		ent = &cl_parseEntities[num];
501 		sounds[i] = ent->sound;
502 	}
503 
504 	// Add sounds from that list
505 	for (i=0 ; i<cl.frame.numEntities ; i++) {
506 		if (!sounds[i])
507 			continue;
508 
509 		if (!cl.soundCfgStrings[sounds[i]] && cl.configStrings[CS_SOUNDS+sounds[i]][0])
510 			cl.soundCfgStrings[sounds[i]] = Snd_RegisterSound (cl.configStrings[CS_SOUNDS+sounds[i]]);
511 
512 		sfx = cl.soundCfgStrings[sounds[i]];
513 		if (!sfx)
514 			continue;	// Bad sound effect
515 
516 		sc = sfx->cache;
517 		if (!sc)
518 			continue;
519 
520 		num = (cl.frame.parseEntities + i) & MAX_PARSEENTITIES_MASK;
521 		ent = &cl_parseEntities[num];
522 
523 		// Get the entity sound origin
524 		CL_CGModule_GetEntitySoundOrigin (ent->number, origin, velocity);
525 
526 		// Find the total contribution of all sounds of this type
527 		DMASnd_SpatializeOrigin (origin, 255.0f, SOUND_LOOPATTENUATE, &leftTotal, &rightTotal);
528 		for (j=i+1 ; j<cl.frame.numEntities ; j++) {
529 			if (sounds[j] != sounds[i])
530 				continue;
531 			sounds[j] = 0; // FIXME: this is kinda weird...
532 			// It will literally cut off audio entirely in certain situations, but without it
533 			// sounds can become fucking blaring if abundant enough.
534 
535 			num = (cl.frame.parseEntities + j)&(MAX_PARSEENTITIES_MASK);
536 			ent = &cl_parseEntities[num];
537 
538 			DMASnd_SpatializeOrigin (origin, 255.0f, SOUND_LOOPATTENUATE, &left, &right);
539 			leftTotal += left;
540 			rightTotal += right;
541 		}
542 
543 		if (leftTotal == 0 && rightTotal == 0)
544 			continue;	// Not audible
545 
546 		// Allocate a channel
547 		ch = DMASnd_PickChannel (0, 0);
548 		if (!ch)
549 			return;
550 
551 		// Clamp high
552 		if (leftTotal > 255)
553 			leftTotal = 255;
554 		if (rightTotal > 255)
555 			rightTotal = 255;
556 
557 		ch->leftVol = leftTotal;
558 		ch->rightVol = rightTotal;
559 		ch->autoSound = qTrue;	// Remove next frame
560 		ch->sfx = sfx;
561 		ch->position = snd_dmaPaintedTime % sc->length;
562 		ch->endTime = snd_dmaPaintedTime + sc->length - ch->position;
563 	}
564 }
565 
566 
567 /*
568 ================
569 DMASnd_PaintChannelFrom8
570 ================
571 */
DMASnd_PaintChannelFrom8(channel_t * ch,sfxCache_t * sc,int count,int offset)572 static void DMASnd_PaintChannelFrom8 (channel_t *ch, sfxCache_t *sc, int count, int offset)
573 {
574 	int		data;
575 	int		*lScale, *rScale;
576 	byte	*sfx;
577 	int		i;
578 	sfxSamplePair_t	*samp;
579 
580 	// Clamp
581 	if (ch->leftVol > 255)
582 		ch->leftVol = 255;
583 	if (ch->rightVol > 255)
584 		ch->rightVol = 255;
585 
586 	// Left/right scale
587 	lScale = snd_dmaScaleTable[ch->leftVol >> 3];
588 	rScale = snd_dmaScaleTable[ch->rightVol >> 3];
589 	sfx = (byte *)sc->data + ch->position;
590 
591 	samp = &snd_dmaPaintBuffer[offset];
592 	for (i=0 ; i<count ; i++, samp++) {
593 		data = sfx[i];
594 		samp->left += lScale[data];
595 		samp->right += rScale[data];
596 	}
597 
598 	ch->position += count;
599 }
600 
601 
602 /*
603 ================
604 DMASnd_PaintChannelFrom16
605 ================
606 */
DMASnd_PaintChannelFrom16(channel_t * ch,sfxCache_t * sc,int count,int offset)607 static void DMASnd_PaintChannelFrom16 (channel_t *ch, sfxCache_t *sc, int count, int offset)
608 {
609 	int		data, i;
610 	int		left, right;
611 	int		leftVol, rightVol;
612 	signed short	*sfx;
613 	sfxSamplePair_t	*samp;
614 
615 	leftVol = ch->leftVol * (s_volume->floatVal*256);
616 	rightVol = ch->rightVol * (s_volume->floatVal*256);
617 	sfx = (signed short *)sc->data + ch->position;
618 
619 	samp = &snd_dmaPaintBuffer[offset];
620 	for (i=0 ; i<count ; i++, samp++) {
621 		data = sfx[i];
622 		left = (data * leftVol)>>8;
623 		right = (data * rightVol)>>8;
624 		samp->left += left;
625 		samp->right += right;
626 	}
627 
628 	ch->position += count;
629 }
630 
631 
632 /*
633 ================
634 DMASnd_PaintChannels
635 ================
636 */
DMASnd_PaintChannels(int endTime)637 static void DMASnd_PaintChannels (int endTime)
638 {
639 	channel_t	*ch;
640 	sfxCache_t	*sc;
641 	int			lTime, count;
642 	int			end, i;
643 
644 	while (snd_dmaPaintedTime < endTime) {
645 		// If snd_dmaPaintBuffer is smaller than DMA buffer
646 		end = endTime;
647 		if (endTime - snd_dmaPaintedTime > SND_PBUFFER)
648 			end = snd_dmaPaintedTime + SND_PBUFFER;
649 
650 		// Start any playsounds
651 		DMASnd_IssuePlaysounds (end);
652 
653 		// Clear the paint buffer
654 		if (snd_dmaRawEnd < snd_dmaPaintedTime) {
655 			memset (snd_dmaPaintBuffer, 0, (end - snd_dmaPaintedTime) * sizeof (sfxSamplePair_t));
656 		}
657 		else {
658 			// Copy from the streaming sound source
659 			int		stop, s;
660 
661 			stop = (end < snd_dmaRawEnd) ? end : snd_dmaRawEnd;
662 
663 			for (i=snd_dmaPaintedTime ; i<stop ; i++) {
664 				s = i & (MAX_RAW_SAMPLES-1);
665 				snd_dmaPaintBuffer[i-snd_dmaPaintedTime] = snd_dmaRawSamples[s];
666 			}
667 
668 			for ( ; i<end ; i++) {
669 				snd_dmaPaintBuffer[i-snd_dmaPaintedTime].left =
670 				snd_dmaPaintBuffer[i-snd_dmaPaintedTime].right = 0;
671 			}
672 		}
673 
674 		// Paint in the channels
675 		for (i=0, ch=snd_dmaOutChannels ; i<MAX_CHANNELS ; ch++, i++) {
676 			lTime = snd_dmaPaintedTime;
677 
678 			while (lTime < end) {
679 				if (!ch->sfx || (!ch->leftVol && !ch->rightVol))
680 					break;
681 
682 				// Max painting is to the end of the buffer
683 				count = end - lTime;
684 
685 				// Might be stopped by running out of data
686 				if (ch->endTime - lTime < count)
687 					count = ch->endTime - lTime;
688 
689 				sc = Snd_LoadSound (ch->sfx);
690 				if (!sc)
691 					break;
692 
693 				if (count > 0 && ch->sfx) {
694 					if (sc->width == 1)
695 						DMASnd_PaintChannelFrom8 (ch, sc, count, lTime - snd_dmaPaintedTime);
696 					else
697 						DMASnd_PaintChannelFrom16 (ch, sc, count, lTime - snd_dmaPaintedTime);
698 
699 					lTime += count;
700 				}
701 
702 				// If at end of loop, restart
703 				if (lTime >= ch->endTime) {
704 					if (ch->autoSound) {
705 						// Autolooping sounds always go back to start
706 						ch->position = 0;
707 						ch->endTime = lTime + sc->length;
708 					}
709 					else if (sc->loopStart >= 0) {
710 						ch->position = sc->loopStart;
711 						ch->endTime = lTime + sc->length - ch->position;
712 					}
713 					else {
714 						// Channel just stopped
715 						ch->sfx = NULL;
716 					}
717 				}
718 			}
719 
720 		}
721 
722 		// Transfer out according to DMA format
723 		DMASnd_TransferPaintBuffer (end);
724 		snd_dmaPaintedTime = end;
725 	}
726 }
727 
728 
729 /*
730 ============
731 DMASnd_RawSamples
732 
733 Cinematic streaming and voice over network
734 ============
735 */
DMASnd_RawSamples(int samples,int rate,int width,int channels,byte * data)736 void DMASnd_RawSamples (int samples, int rate, int width, int channels, byte *data)
737 {
738 	int		i;
739 	int		src, dst;
740 	float	scale;
741 
742 	if (snd_dmaRawEnd < snd_dmaPaintedTime)
743 		snd_dmaRawEnd = snd_dmaPaintedTime;
744 	scale = (float)rate / snd_audioDMA.speed;
745 
746 	switch (channels) {
747 	case 1:
748 		switch (width) {
749 		case 1:
750 			for (i=0 ; ; i++) {
751 				src = i*scale;
752 				if (src >= samples)
753 					break;
754 				dst = snd_dmaRawEnd & (MAX_RAW_SAMPLES-1);
755 				snd_dmaRawEnd++;
756 				snd_dmaRawSamples[dst].left = (((byte *)data)[src]-128) << 16;
757 				snd_dmaRawSamples[dst].right = (((byte *)data)[src]-128) << 16;
758 			}
759 			break;
760 
761 		case 2:
762 			for (i=0 ; ; i++) {
763 				src = i*scale;
764 				if (src >= samples)
765 					break;
766 				dst = snd_dmaRawEnd & (MAX_RAW_SAMPLES-1);
767 				snd_dmaRawEnd++;
768 				snd_dmaRawSamples[dst].left = LittleShort(((int16 *)data)[src]) << 8;
769 				snd_dmaRawSamples[dst].right = LittleShort(((int16 *)data)[src]) << 8;
770 			}
771 			break;
772 		}
773 		break;
774 
775 	case 2:
776 		switch (width) {
777 		case 1:
778 			for (i=0 ; ; i++) {
779 				src = i*scale;
780 				if (src >= samples)
781 					break;
782 				dst = snd_dmaRawEnd & (MAX_RAW_SAMPLES-1);
783 				snd_dmaRawEnd++;
784 				snd_dmaRawSamples[dst].left = ((char *)data)[src*2] << 16;
785 				snd_dmaRawSamples[dst].right = ((char *)data)[src*2+1] << 16;
786 			}
787 			break;
788 
789 		case 2:
790 			if (scale == 1.0) {
791 				for (i=0 ; i<samples ; i++) {
792 					dst = snd_dmaRawEnd & (MAX_RAW_SAMPLES-1);
793 					snd_dmaRawEnd++;
794 					snd_dmaRawSamples[dst].left = LittleShort(((int16 *)data)[i*2]) << 8;
795 					snd_dmaRawSamples[dst].right = LittleShort(((int16 *)data)[i*2+1]) << 8;
796 				}
797 			}
798 			else {
799 				for (i=0 ; ; i++) {
800 					src = i*scale;
801 					if (src >= samples)
802 						break;
803 
804 					dst = snd_dmaRawEnd & (MAX_RAW_SAMPLES-1);
805 					snd_dmaRawEnd++;
806 					snd_dmaRawSamples[dst].left = LittleShort(((int16 *)data)[src*2]) << 8;
807 					snd_dmaRawSamples[dst].right = LittleShort(((int16 *)data)[src*2+1]) << 8;
808 				}
809 			}
810 			break;
811 		}
812 		break;
813 	}
814 }
815 
816 
817 /*
818 ============
819 DMASnd_Update
820 
821 Called once each time through the main loop
822 ============
823 */
DMASnd_Update(refDef_t * rd)824 void DMASnd_Update (refDef_t *rd)
825 {
826 	int			total, i;
827 	uint32		endTime, samples;
828 	channel_t	*ch;
829 	int			samplePos;
830 	static int	oldSamplePos;
831 	static int	buffers;
832 	int			fullSamples;
833 
834 	if (rd) {
835 		Vec3Copy (rd->viewOrigin, snd_dmaOrigin);
836 		Vec3Copy (rd->rightVec, snd_dmaRightVec);
837 	}
838 	else {
839 		Vec3Clear (snd_dmaOrigin);
840 		Vec3Clear (snd_dmaRightVec);
841 	}
842 
843 	// Don't play sounds while the screen is disabled
844 	if (cls.disableScreen || !snd_isActive) {
845 		DMASnd_ClearBuffer ();
846 		return;
847 	}
848 
849 	// Rebuild scale tables if volume is modified
850 	if (s_volume->modified)
851 		DMASnd_ScaleTableInit ();
852 
853 	// Update spatialization for dynamic sounds
854 	for (i=0, ch=snd_dmaOutChannels ; i<MAX_CHANNELS ; ch++, i++) {
855 		if (!ch->sfx)
856 			continue;
857 
858 		if (ch->autoSound) {
859 			// Autosounds are regenerated fresh each frame
860 			memset (ch, 0, sizeof (channel_t));
861 			continue;
862 		}
863 
864 		// Respatialize channel
865 		DMASnd_SpatializeChannel (ch);
866 		if (!ch->leftVol && !ch->rightVol) {
867 			memset (ch, 0, sizeof (channel_t));
868 			continue;
869 		}
870 	}
871 
872 	// Add loopsounds
873 	DMASnd_AddLoopSounds ();
874 
875 	// Debugging output
876 	if (s_show->intVal) {
877 		total = 0;
878 		for (i=0, ch=snd_dmaOutChannels ; i<MAX_CHANNELS ; ch++, i++) {
879 			if (ch->sfx && (ch->leftVol || ch->rightVol)) {
880 				Com_Printf (0, "%3i %3i %s\n", ch->leftVol, ch->rightVol, ch->sfx->name);
881 				total++;
882 			}
883 		}
884 
885 		Com_Printf (0, "----(%i)---- painted: %i\n", total, snd_dmaPaintedTime);
886 	}
887 
888 	// Mix some sound
889 	SndImp_BeginPainting ();
890 	if (!snd_audioDMA.buffer)
891 		return;
892 
893 	// Update DMA time
894 	fullSamples = snd_audioDMA.samples / snd_audioDMA.channels;
895 
896 	/*
897 	** It is possible to miscount buffers if it has wrapped twice between
898 	** calls to Snd_Update. Oh well
899 	*/
900 	samplePos = SndImp_GetDMAPos ();
901 	if (samplePos < oldSamplePos) {
902 		buffers++;	// Buffer wrapped
903 
904 		if (snd_dmaPaintedTime > 0x40000000) {
905 			// Time to chop things off to avoid 32 bit limits
906 			buffers = 0;
907 			snd_dmaPaintedTime = fullSamples;
908 			DMASnd_StopAllSounds ();
909 		}
910 	}
911 
912 	oldSamplePos = samplePos;
913 	snd_dmaSoundTime = buffers*fullSamples + samplePos/snd_audioDMA.channels;
914 
915 	// Check to make sure that we haven't overshot
916 	if (snd_dmaPaintedTime < snd_dmaSoundTime) {
917 		Com_DevPrintf (PRNT_WARNING, "Snd_Update: overflow\n");
918 		snd_dmaPaintedTime = snd_dmaSoundTime;
919 	}
920 
921 	// Mix ahead of current position
922 	endTime = snd_dmaSoundTime + s_mixahead->floatVal * snd_audioDMA.speed;
923 
924 	// Mix to an even submission block size
925 	endTime = (endTime + snd_audioDMA.submissionChunk-1) & ~(snd_audioDMA.submissionChunk-1);
926 	samples = snd_audioDMA.samples >> (snd_audioDMA.channels-1);
927 	if (endTime - snd_dmaSoundTime > samples)
928 		endTime = snd_dmaSoundTime + samples;
929 
930 	DMASnd_PaintChannels (endTime);
931 	SndImp_Submit ();
932 }
933 
934 /*
935 ==============================================================================
936 
937 	INIT / SHUTDOWN
938 
939 ==============================================================================
940 */
941 
942 /*
943 ================
944 DMASnd_Init
945 ================
946 */
DMASnd_Init(void)947 qBool DMASnd_Init (void)
948 {
949 	if (!SndImp_Init ())
950 		return qFalse;
951 
952 	DMASnd_ScaleTableInit ();
953 
954 	snd_dmaSoundTime = 0;
955 	snd_dmaPaintedTime = 0;
956 
957 	return qTrue;
958 }
959 
960 
961 /*
962 ================
963 DMASnd_Shutdown
964 ================
965 */
DMASnd_Shutdown(void)966 void DMASnd_Shutdown (void)
967 {
968 	SndImp_Shutdown ();
969 
970 	snd_dmaSoundTime = 0;
971 	snd_dmaPaintedTime = 0;
972 }
973