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