1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: s_sound.c,v 1.3 1998/01/05 16:26:08 pekangas Exp $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 //
18 // DESCRIPTION: none
19 //
20 //-----------------------------------------------------------------------------
21
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #ifdef _WIN32
26 #include <io.h>
27 #endif
28 #include <fcntl.h>
29
30 #include "i_system.h"
31 #include "i_sound.h"
32 #include "i_music.h"
33 #include "i_cd.h"
34 #include "s_sound.h"
35 #include "s_sndseq.h"
36 #include "s_playlist.h"
37 #include "c_dispatch.h"
38 #include "m_random.h"
39 #include "w_wad.h"
40 #include "doomdef.h"
41 #include "p_local.h"
42 #include "doomstat.h"
43 #include "cmdlib.h"
44 #include "v_video.h"
45 #include "v_text.h"
46 #include "a_sharedglobal.h"
47 #include "gstrings.h"
48 #include "gi.h"
49 #include "templates.h"
50 #include "timidity/timidity.h"
51 #include "g_level.h"
52 #include "po_man.h"
53 #include "farchive.h"
54
55 // MACROS ------------------------------------------------------------------
56
57 #ifdef NeXT
58 // NeXT doesn't need a binary flag in open call
59 #define O_BINARY 0
60 #endif
61
62 #ifndef O_BINARY
63 #define O_BINARY 0
64 #endif
65
66 #ifndef FIXED2FLOAT
67 #define FIXED2FLOAT(f) (((float)(f))/(float)65536)
68 #endif
69
70 #define NORM_PITCH 128
71 #define NORM_PRIORITY 64
72 #define NORM_SEP 0
73
74 #define S_PITCH_PERTURB 1
75 #define S_STEREO_SWING 0.75
76
77 // TYPES -------------------------------------------------------------------
78
79 struct MusPlayingInfo
80 {
81 FString name;
82 MusInfo *handle;
83 int baseorder;
84 bool loop;
85 };
86
87 enum
88 {
89 SOURCE_None, // Sound is always on top of the listener.
90 SOURCE_Actor, // Sound is coming from an actor.
91 SOURCE_Sector, // Sound is coming from a sector.
92 SOURCE_Polyobj, // Sound is coming from a polyobject.
93 SOURCE_Unattached, // Sound is not attached to any particular emitter.
94 };
95
96 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
97
98 extern float S_GetMusicVolume (const char *music);
99
100 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
101
102 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
103
104 static bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, AActor *actor, int channel);
105 static bool S_IsChannelUsed(AActor *actor, int channel, int *seen);
106 static void S_ActivatePlayList(bool goBack);
107 static void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel);
108 static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, const FPolyObj *poly,
109 const float pt[3], int channel, int chanflags, FVector3 *pos, FVector3 *vel);
110 static void CalcSectorSoundOrg(const sector_t *sec, int channum, fixed_t *x, fixed_t *y, fixed_t *z);
111 static void CalcPolyobjSoundOrg(const FPolyObj *poly, fixed_t *x, fixed_t *y, fixed_t *z);
112 static FSoundChan *S_StartSound(AActor *mover, const sector_t *sec, const FPolyObj *poly,
113 const FVector3 *pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo *rolloff);
114 static void S_SetListener(SoundListener &listener, AActor *listenactor);
115
116 // PRIVATE DATA DEFINITIONS ------------------------------------------------
117
118 static bool SoundPaused; // whether sound is paused
119 static bool MusicPaused; // whether music is paused
120 static MusPlayingInfo mus_playing; // music currently being played
121 static FString LastSong; // last music that was played
122 static FPlayList *PlayList;
123 static int RestartEvictionsAt; // do not restart evicted channels before this level.time
124
125 // PUBLIC DATA DEFINITIONS -------------------------------------------------
126
127 int sfx_empty;
128
129 FSoundChan *Channels;
130 FSoundChan *FreeChannels;
131
132 FRolloffInfo S_Rolloff;
133 BYTE *S_SoundCurve;
134 int S_SoundCurveSize;
135
136 FBoolCVar noisedebug ("noise", false, 0); // [RH] Print sound debugging info?
137 CVAR (Int, snd_channels, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // number of channels available
138 CVAR (Bool, snd_flipstereo, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
139
140 // CODE --------------------------------------------------------------------
141
142 //==========================================================================
143 //
144 // S_NoiseDebug
145 //
146 // [RH] Print sound debug info. Called by status bar.
147 //==========================================================================
148
S_NoiseDebug(void)149 void S_NoiseDebug (void)
150 {
151 FSoundChan *chan;
152 FVector3 listener;
153 FVector3 origin;
154 int y, color;
155
156 y = 32 * CleanYfac;
157 screen->DrawText (SmallFont, CR_YELLOW, 0, y, "*** SOUND DEBUG INFO ***", TAG_DONE);
158 y += 8;
159
160 screen->DrawText (SmallFont, CR_GOLD, 0, y, "name", TAG_DONE);
161 screen->DrawText (SmallFont, CR_GOLD, 70, y, "x", TAG_DONE);
162 screen->DrawText (SmallFont, CR_GOLD, 120, y, "y", TAG_DONE);
163 screen->DrawText (SmallFont, CR_GOLD, 170, y, "z", TAG_DONE);
164 screen->DrawText (SmallFont, CR_GOLD, 220, y, "vol", TAG_DONE);
165 screen->DrawText (SmallFont, CR_GOLD, 260, y, "dist", TAG_DONE);
166 screen->DrawText (SmallFont, CR_GOLD, 300, y, "chan", TAG_DONE);
167 screen->DrawText (SmallFont, CR_GOLD, 340, y, "pri", TAG_DONE);
168 screen->DrawText (SmallFont, CR_GOLD, 380, y, "flags", TAG_DONE);
169 screen->DrawText (SmallFont, CR_GOLD, 460, y, "aud", TAG_DONE);
170 screen->DrawText (SmallFont, CR_GOLD, 520, y, "pos", TAG_DONE);
171 y += 8;
172
173 if (Channels == NULL)
174 {
175 return;
176 }
177
178
179 listener.X = FIXED2FLOAT(players[consoleplayer].camera->SoundX());
180 listener.Y = FIXED2FLOAT(players[consoleplayer].camera->SoundZ());
181 listener.Z = FIXED2FLOAT(players[consoleplayer].camera->SoundY());
182
183 // Display the oldest channel first.
184 for (chan = Channels; chan->NextChan != NULL; chan = chan->NextChan)
185 { }
186 while (y < SCREENHEIGHT - 16)
187 {
188 char temp[32];
189
190 CalcPosVel(chan, &origin, NULL);
191 color = (chan->ChanFlags & CHAN_LOOP) ? CR_BROWN : CR_GREY;
192
193 // Name
194 Wads.GetLumpName (temp, S_sfx[chan->SoundID].lumpnum);
195 temp[8] = 0;
196 screen->DrawText (SmallFont, color, 0, y, temp, TAG_DONE);
197
198 if (!(chan->ChanFlags & CHAN_IS3D))
199 {
200 screen->DrawText(SmallFont, color, 70, y, "---", TAG_DONE); // X
201 screen->DrawText(SmallFont, color, 120, y, "---", TAG_DONE); // Y
202 screen->DrawText(SmallFont, color, 170, y, "---", TAG_DONE); // Z
203 screen->DrawText(SmallFont, color, 260, y, "---", TAG_DONE); // Distance
204 }
205 else
206 {
207 // X coordinate
208 mysnprintf(temp, countof(temp), "%.0f", origin.X);
209 screen->DrawText(SmallFont, color, 70, y, temp, TAG_DONE);
210
211 // Y coordinate
212 mysnprintf(temp, countof(temp), "%.0f", origin.Z);
213 screen->DrawText(SmallFont, color, 120, y, temp, TAG_DONE);
214
215 // Z coordinate
216 mysnprintf(temp, countof(temp), "%.0f", origin.Y);
217 screen->DrawText(SmallFont, color, 170, y, temp, TAG_DONE);
218
219 // Distance
220 if (chan->DistanceScale > 0)
221 {
222 mysnprintf(temp, countof(temp), "%.0f", (origin - listener).Length());
223 screen->DrawText(SmallFont, color, 260, y, temp, TAG_DONE);
224 }
225 else
226 {
227 screen->DrawText(SmallFont, color, 260, y, "---", TAG_DONE);
228 }
229 }
230
231 // Volume
232 mysnprintf(temp, countof(temp), "%.2g", chan->Volume);
233 screen->DrawText(SmallFont, color, 220, y, temp, TAG_DONE);
234
235 // Channel
236 mysnprintf(temp, countof(temp), "%d", chan->EntChannel);
237 screen->DrawText(SmallFont, color, 300, y, temp, TAG_DONE);
238
239 // Priority
240 mysnprintf(temp, countof(temp), "%d", chan->Priority);
241 screen->DrawText(SmallFont, color, 340, y, temp, TAG_DONE);
242
243 // Flags
244 mysnprintf(temp, countof(temp), "%s3%sZ%sU%sM%sN%sA%sL%sE%sV",
245 (chan->ChanFlags & CHAN_IS3D) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK,
246 (chan->ChanFlags & CHAN_LISTENERZ) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK,
247 (chan->ChanFlags & CHAN_UI) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK,
248 (chan->ChanFlags & CHAN_MAYBE_LOCAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK,
249 (chan->ChanFlags & CHAN_NOPAUSE) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK,
250 (chan->ChanFlags & CHAN_AREA) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK,
251 (chan->ChanFlags & CHAN_LOOP) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK,
252 (chan->ChanFlags & CHAN_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK,
253 (chan->ChanFlags & CHAN_VIRTUAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK);
254 screen->DrawText(SmallFont, color, 380, y, temp, TAG_DONE);
255
256 // Audibility
257 mysnprintf(temp, countof(temp), "%.4f", GSnd->GetAudibility(chan));
258 screen->DrawText(SmallFont, color, 460, y, temp, TAG_DONE);
259
260 // Position
261 mysnprintf(temp, countof(temp), "%u", GSnd->GetPosition(chan));
262 screen->DrawText(SmallFont, color, 520, y, temp, TAG_DONE);
263
264
265 y += 8;
266 if (chan->PrevChan == &Channels)
267 {
268 break;
269 }
270 chan = (FSoundChan *)((size_t)chan->PrevChan - myoffsetof(FSoundChan, NextChan));
271 }
272 V_SetBorderNeedRefresh();
273 }
274
275 static FString LastLocalSndInfo;
276 static FString LastLocalSndSeq;
277 void S_AddLocalSndInfo(int lump);
278
279 //==========================================================================
280 //
281 // S_Init
282 //
283 // Initializes sound stuff, including volume. Sets channels, SFX and
284 // music volume, allocates channel buffer, and sets S_sfx lookup.
285 //==========================================================================
286
S_Init()287 void S_Init ()
288 {
289 int curvelump;
290
291 atterm (S_Shutdown);
292
293 // remove old data (S_Init can be called multiple times!)
294 if (S_SoundCurve != NULL)
295 {
296 delete[] S_SoundCurve;
297 S_SoundCurve = NULL;
298 }
299
300 // Heretic and Hexen have sound curve lookup tables. Doom does not.
301 curvelump = Wads.CheckNumForName ("SNDCURVE");
302 if (curvelump >= 0)
303 {
304 S_SoundCurveSize = Wads.LumpLength (curvelump);
305 S_SoundCurve = new BYTE[S_SoundCurveSize];
306 Wads.ReadLump(curvelump, S_SoundCurve);
307 }
308
309 // Free all channels for use.
310 while (Channels != NULL)
311 {
312 S_ReturnChannel(Channels);
313 }
314
315 // no sounds are playing, and they are not paused
316 MusicPaused = false;
317 }
318
319 //==========================================================================
320 //
321 // S_InitData
322 //
323 //==========================================================================
324
S_InitData()325 void S_InitData ()
326 {
327 LastLocalSndInfo = LastLocalSndSeq = "";
328 S_ParseSndInfo (false);
329 S_ParseSndSeq (-1);
330 }
331
332 //==========================================================================
333 //
334 // S_Shutdown
335 //
336 //==========================================================================
337
S_Shutdown()338 void S_Shutdown ()
339 {
340 FSoundChan *chan, *next;
341
342 chan = Channels;
343 while (chan != NULL)
344 {
345 next = chan->NextChan;
346 S_StopChannel(chan);
347 chan = next;
348 }
349
350 GSnd->UpdateSounds();
351 for (chan = FreeChannels; chan != NULL; chan = next)
352 {
353 next = chan->NextChan;
354 delete chan;
355 }
356 FreeChannels = NULL;
357
358 if (S_SoundCurve != NULL)
359 {
360 delete[] S_SoundCurve;
361 S_SoundCurve = NULL;
362 }
363 if (PlayList != NULL)
364 {
365 delete PlayList;
366 PlayList = NULL;
367 }
368 S_StopMusic (true);
369 mus_playing.name = "";
370 LastSong = "";
371 }
372
373 //==========================================================================
374 //
375 // S_Start
376 //
377 // Per level startup code. Kills playing sounds at start of level
378 // and starts new music.
379 //==========================================================================
380
S_Start()381 void S_Start ()
382 {
383 if (GSnd)
384 {
385 // kill all playing sounds at start of level (trust me - a good idea)
386 S_StopAllChannels();
387
388 // Check for local sound definitions. Only reload if they differ
389 // from the previous ones.
390 FString LocalSndInfo;
391 FString LocalSndSeq;
392
393 // To be certain better check whether level is valid!
394 if (level.info)
395 {
396 LocalSndInfo = level.info->SoundInfo;
397 }
398
399 if (level.info)
400 {
401 LocalSndSeq = level.info->SndSeq;
402 }
403
404 bool parse_ss = false;
405
406 // This level uses a different local SNDINFO
407 if (LastLocalSndInfo.CompareNoCase(LocalSndInfo) != 0 || !level.info)
408 {
409 // First delete the old sound list
410 for(unsigned i = 1; i < S_sfx.Size(); i++)
411 {
412 S_UnloadSound(&S_sfx[i]);
413 }
414
415 // Parse the global SNDINFO
416 S_ParseSndInfo(true);
417
418 if (*LocalSndInfo)
419 {
420 // Now parse the local SNDINFO
421 int j = Wads.CheckNumForFullName(LocalSndInfo, true);
422 if (j>=0) S_AddLocalSndInfo(j);
423 }
424
425 // Also reload the SNDSEQ if the SNDINFO was replaced!
426 parse_ss = true;
427 }
428 else if (LastLocalSndSeq.CompareNoCase(LocalSndSeq) != 0)
429 {
430 parse_ss = true;
431 }
432
433 if (parse_ss)
434 {
435 S_ParseSndSeq(*LocalSndSeq? Wads.CheckNumForFullName(LocalSndSeq, true) : -1);
436 }
437
438 LastLocalSndInfo = LocalSndInfo;
439 LastLocalSndSeq = LocalSndSeq;
440 }
441
442 // stop the old music if it has been paused.
443 // This ensures that the new music is started from the beginning
444 // if it's the same as the last one and it has been paused.
445 if (MusicPaused) S_StopMusic(true);
446
447 // start new music for the level
448 MusicPaused = false;
449
450 // Don't start the music if loading a savegame, because the music is stored there.
451 // Don't start the music if revisiting a level in a hub for the same reason.
452 if (!savegamerestore && (level.info == NULL || level.info->snapshot == NULL || !level.info->isValid()))
453 {
454 if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid))
455 S_ChangeMusic (level.Music, level.musicorder);
456 }
457 }
458
459 //==========================================================================
460 //
461 // S_PrecacheLevel
462 //
463 // Like R_PrecacheLevel, but for sounds.
464 //
465 //==========================================================================
466
S_PrecacheLevel()467 void S_PrecacheLevel ()
468 {
469 unsigned int i;
470
471 if (GSnd)
472 {
473 for (i = 0; i < S_sfx.Size(); ++i)
474 {
475 S_sfx[i].bUsed = false;
476 }
477
478 AActor *actor;
479 TThinkerIterator<AActor> iterator;
480
481 // Precache all sounds known to be used by the currently spawned actors.
482 while ( (actor = iterator.Next()) != NULL )
483 {
484 actor->MarkPrecacheSounds();
485 }
486 // Precache all extra sounds requested by this map.
487 for (i = 0; i < level.info->PrecacheSounds.Size(); ++i)
488 {
489 level.info->PrecacheSounds[i].MarkUsed();
490 }
491 // Don't unload sounds that are playing right now.
492 for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
493 {
494 chan->SoundID.MarkUsed();
495 }
496
497 for (i = 1; i < S_sfx.Size(); ++i)
498 {
499 if (S_sfx[i].bUsed)
500 {
501 S_CacheSound (&S_sfx[i]);
502 }
503 }
504 for (i = 1; i < S_sfx.Size(); ++i)
505 {
506 if (!S_sfx[i].bUsed && S_sfx[i].link == sfxinfo_t::NO_LINK)
507 {
508 S_UnloadSound (&S_sfx[i]);
509 }
510 }
511 }
512 }
513
514 //==========================================================================
515 //
516 // S_CacheSound
517 //
518 //==========================================================================
519
S_CacheSound(sfxinfo_t * sfx)520 void S_CacheSound (sfxinfo_t *sfx)
521 {
522 if (GSnd)
523 {
524 if (sfx->bPlayerReserve)
525 {
526 return;
527 }
528 else if (sfx->bRandomHeader)
529 {
530 S_CacheRandomSound (sfx);
531 }
532 else
533 {
534 while (sfx->link != sfxinfo_t::NO_LINK)
535 {
536 sfx = &S_sfx[sfx->link];
537 }
538 sfx->bUsed = true;
539 S_LoadSound (sfx);
540 }
541 }
542 }
543
544 //==========================================================================
545 //
546 // S_UnloadSound
547 //
548 //==========================================================================
549
S_UnloadSound(sfxinfo_t * sfx)550 void S_UnloadSound (sfxinfo_t *sfx)
551 {
552 if (sfx->data.isValid())
553 {
554 GSnd->UnloadSound(sfx->data);
555 sfx->data.Clear();
556 DPrintf("Unloaded sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]);
557 }
558 }
559
560 //==========================================================================
561 //
562 // S_GetChannel
563 //
564 // Returns a free channel for the system sound interface.
565 //
566 //==========================================================================
567
S_GetChannel(void * syschan)568 FISoundChannel *S_GetChannel(void *syschan)
569 {
570 FSoundChan *chan;
571
572 if (FreeChannels != NULL)
573 {
574 chan = FreeChannels;
575 S_UnlinkChannel(chan);
576 }
577 else
578 {
579 chan = new FSoundChan;
580 memset(chan, 0, sizeof(*chan));
581 }
582 S_LinkChannel(chan, &Channels);
583 chan->SysChannel = syschan;
584 return chan;
585 }
586
587 //==========================================================================
588 //
589 // S_ReturnChannel
590 //
591 // Returns a channel to the free pool.
592 //
593 //==========================================================================
594
S_ReturnChannel(FSoundChan * chan)595 void S_ReturnChannel(FSoundChan *chan)
596 {
597 S_UnlinkChannel(chan);
598 memset(chan, 0, sizeof(*chan));
599 S_LinkChannel(chan, &FreeChannels);
600 }
601
602 //==========================================================================
603 //
604 // S_UnlinkChannel
605 //
606 //==========================================================================
607
S_UnlinkChannel(FSoundChan * chan)608 void S_UnlinkChannel(FSoundChan *chan)
609 {
610 *(chan->PrevChan) = chan->NextChan;
611 if (chan->NextChan != NULL)
612 {
613 chan->NextChan->PrevChan = chan->PrevChan;
614 }
615 }
616
617 //==========================================================================
618 //
619 // S_LinkChannel
620 //
621 //==========================================================================
622
S_LinkChannel(FSoundChan * chan,FSoundChan ** head)623 void S_LinkChannel(FSoundChan *chan, FSoundChan **head)
624 {
625 chan->NextChan = *head;
626 if (chan->NextChan != NULL)
627 {
628 chan->NextChan->PrevChan = &chan->NextChan;
629 }
630 *head = chan;
631 chan->PrevChan = head;
632 }
633
634 // [RH] Split S_StartSoundAtVolume into multiple parts so that sounds can
635 // be specified both by id and by name. Also borrowed some stuff from
636 // Hexen and parameters from Quake.
637
638 //==========================================================================
639 //
640 // CalcPosVel
641 //
642 // Retrieves a sound's position and velocity for 3D sounds. This version
643 // is for an already playing sound.
644 //
645 //=========================================================================
646
CalcPosVel(FSoundChan * chan,FVector3 * pos,FVector3 * vel)647 static void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel)
648 {
649 CalcPosVel(chan->SourceType, chan->Actor, chan->Sector, chan->Poly, chan->Point,
650 chan->EntChannel, chan->ChanFlags, pos, vel);
651 }
652
653 //=========================================================================
654 //
655 // CalcPosVel
656 //
657 // This version is for sounds that haven't started yet so have no channel.
658 //
659 //=========================================================================
660
CalcPosVel(int type,const AActor * actor,const sector_t * sector,const FPolyObj * poly,const float pt[3],int channum,int chanflags,FVector3 * pos,FVector3 * vel)661 static void CalcPosVel(int type, const AActor *actor, const sector_t *sector,
662 const FPolyObj *poly, const float pt[3], int channum, int chanflags, FVector3 *pos, FVector3 *vel)
663 {
664 if (pos != NULL)
665 {
666 fixed_t x, y, z;
667
668 if (players[consoleplayer].camera != NULL)
669 {
670 x = players[consoleplayer].camera->SoundX();
671 y = players[consoleplayer].camera->SoundZ();
672 z = players[consoleplayer].camera->SoundY();
673 }
674 else
675 {
676 z = y = x = 0;
677 }
678
679 // [BL] Moved this case out of the switch statement to make code easier
680 // on static analysis.
681 if(type == SOURCE_Unattached)
682 {
683 pos->X = pt[0];
684 pos->Y = !(chanflags & CHAN_LISTENERZ) ? pt[1] : FIXED2FLOAT(y);
685 pos->Z = pt[2];
686 }
687 else
688 {
689 switch (type)
690 {
691 case SOURCE_None:
692 default:
693 break;
694
695 case SOURCE_Actor:
696 //assert(actor != NULL);
697 if (actor != NULL)
698 {
699 x = actor->SoundX();
700 y = actor->SoundZ();
701 z = actor->SoundY();
702 }
703 break;
704
705 case SOURCE_Sector:
706 assert(sector != NULL);
707 if (sector != NULL)
708 {
709 if (chanflags & CHAN_AREA)
710 {
711 CalcSectorSoundOrg(sector, channum, &x, &z, &y);
712 }
713 else
714 {
715 x = sector->soundorg[0];
716 z = sector->soundorg[1];
717 chanflags |= CHAN_LISTENERZ;
718 }
719 }
720 break;
721
722 case SOURCE_Polyobj:
723 assert(poly != NULL);
724 CalcPolyobjSoundOrg(poly, &x, &z, &y);
725 break;
726 }
727
728 if ((chanflags & CHAN_LISTENERZ) && players[consoleplayer].camera != NULL)
729 {
730 y = players[consoleplayer].camera != NULL ? players[consoleplayer].camera->SoundZ() : 0;
731 }
732 pos->X = FIXED2FLOAT(x);
733 pos->Y = FIXED2FLOAT(y);
734 pos->Z = FIXED2FLOAT(z);
735 }
736 }
737 if (vel != NULL)
738 {
739 // Only actors maintain velocity information.
740 if (type == SOURCE_Actor && actor != NULL)
741 {
742 vel->X = FIXED2FLOAT(actor->velx) * TICRATE;
743 vel->Y = FIXED2FLOAT(actor->velz) * TICRATE;
744 vel->Z = FIXED2FLOAT(actor->vely) * TICRATE;
745 }
746 else
747 {
748 vel->Zero();
749 }
750 }
751 }
752
753 //==========================================================================
754 //
755 // CalcSectorSoundOrg
756 //
757 // Returns the perceived sound origin for a sector. If the listener is
758 // inside the sector, then the origin is their location. Otherwise, the
759 // origin is from the nearest wall on the sector.
760 //
761 //==========================================================================
762
CalcSectorSoundOrg(const sector_t * sec,int channum,fixed_t * x,fixed_t * y,fixed_t * z)763 static void CalcSectorSoundOrg(const sector_t *sec, int channum, fixed_t *x, fixed_t *y, fixed_t *z)
764 {
765 if (!(i_compatflags & COMPATF_SECTORSOUNDS))
766 {
767 // Are we inside the sector? If yes, the closest point is the one we're on.
768 if (P_PointInSector(*x, *y) == sec)
769 {
770 *x = players[consoleplayer].camera->SoundX();
771 *y = players[consoleplayer].camera->SoundY();
772 }
773 else
774 {
775 // Find the closest point on the sector's boundary lines and use
776 // that as the perceived origin of the sound.
777 sec->ClosestPoint(*x, *y, *x, *y);
778 }
779 }
780 else
781 {
782 *x = sec->soundorg[0];
783 *y = sec->soundorg[1];
784 }
785
786 // Set sound vertical position based on channel.
787 if (channum == CHAN_FLOOR)
788 {
789 *z = MIN(sec->floorplane.ZatPoint(*x, *y), *z);
790 }
791 else if (channum == CHAN_CEILING)
792 {
793 *z = MAX(sec->ceilingplane.ZatPoint(*x, *y), *z);
794 }
795 else if (channum == CHAN_INTERIOR)
796 {
797 *z = clamp(*z, sec->floorplane.ZatPoint(*x, *y), sec->ceilingplane.ZatPoint(*x, *y));
798 }
799 }
800
801 //==========================================================================
802 //
803 // CalcPolySoundOrg
804 //
805 // Returns the perceived sound origin for a polyobject. This is similar to
806 // CalcSectorSoundOrg, except there is no special case for being "inside"
807 // a polyobject, so the sound literally comes from the polyobject's walls.
808 // Vertical position of the sound always comes from the visible wall.
809 //
810 //==========================================================================
811
CalcPolyobjSoundOrg(const FPolyObj * poly,fixed_t * x,fixed_t * y,fixed_t * z)812 static void CalcPolyobjSoundOrg(const FPolyObj *poly, fixed_t *x, fixed_t *y, fixed_t *z)
813 {
814 side_t *side;
815 sector_t *sec;
816
817 poly->ClosestPoint(*x, *y, *x, *y, &side);
818 sec = side->sector;
819 *z = clamp(*z, sec->floorplane.ZatPoint(*x, *y), sec->ceilingplane.ZatPoint(*x, *y));
820 }
821
822 //==========================================================================
823 //
824 // S_StartSound
825 //
826 // 0 attenuation means full volume over whole level.
827 // 0 < attenuation means to scale the distance by that amount when
828 // calculating volume.
829 //
830 //==========================================================================
831
S_StartSound(AActor * actor,const sector_t * sec,const FPolyObj * poly,const FVector3 * pt,int channel,FSoundID sound_id,float volume,float attenuation,FRolloffInfo * forcedrolloff=NULL)832 static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyObj *poly,
833 const FVector3 *pt, int channel, FSoundID sound_id, float volume, float attenuation,
834 FRolloffInfo *forcedrolloff=NULL)
835 {
836 sfxinfo_t *sfx;
837 int chanflags;
838 int basepriority;
839 int org_id;
840 int pitch;
841 FSoundChan *chan;
842 FVector3 pos, vel;
843 FRolloffInfo *rolloff;
844
845 if (sound_id <= 0 || volume <= 0 || nosfx || nosound )
846 return NULL;
847
848 int type;
849
850 if (actor != NULL)
851 {
852 type = SOURCE_Actor;
853 }
854 else if (sec != NULL)
855 {
856 type = SOURCE_Sector;
857 }
858 else if (poly != NULL)
859 {
860 type = SOURCE_Polyobj;
861 }
862 else if (pt != NULL)
863 {
864 type = SOURCE_Unattached;
865 }
866 else
867 {
868 type = SOURCE_None;
869 }
870
871 org_id = sound_id;
872 chanflags = channel & ~7;
873 channel &= 7;
874
875 CalcPosVel(type, actor, sec, poly, &pt->X, channel, chanflags, &pos, &vel);
876
877 if (i_compatflags & COMPATF_MAGICSILENCE)
878 { // For people who just can't play without a silent BFG.
879 channel = CHAN_WEAPON;
880 }
881 else if ((chanflags & CHAN_MAYBE_LOCAL) && (i_compatflags & COMPATF_SILENTPICKUP))
882 {
883 if (actor != NULL && actor != players[consoleplayer].camera)
884 {
885 return NULL;
886 }
887 }
888
889 sfx = &S_sfx[sound_id];
890
891 // Scale volume according to SNDINFO data.
892 volume = MIN(volume * sfx->Volume, 1.f);
893 if (volume <= 0)
894 return NULL;
895
896 // When resolving a link we do not want to get the NearLimit of
897 // the referenced sound so some additional checks are required
898 int near_limit = sfx->NearLimit;
899 float limit_range = sfx->LimitRange;
900 rolloff = &sfx->Rolloff;
901
902 // Resolve player sounds, random sounds, and aliases
903 while (sfx->link != sfxinfo_t::NO_LINK)
904 {
905 if (sfx->bPlayerReserve)
906 {
907 sound_id = FSoundID(S_FindSkinnedSound (actor, sound_id));
908 near_limit = S_sfx[sound_id].NearLimit;
909 limit_range = S_sfx[sound_id].LimitRange;
910 rolloff = &S_sfx[sound_id].Rolloff;
911 }
912 else if (sfx->bRandomHeader)
913 {
914 // Random sounds attenuate based on the original (random) sound as well as the chosen one.
915 attenuation *= sfx->Attenuation;
916 sound_id = FSoundID(S_PickReplacement (sound_id));
917 if (near_limit < 0)
918 {
919 near_limit = S_sfx[sound_id].NearLimit;
920 limit_range = S_sfx[sound_id].LimitRange;
921 }
922 if (rolloff->MinDistance == 0)
923 {
924 rolloff = &S_sfx[sound_id].Rolloff;
925 }
926 }
927 else
928 {
929 sound_id = FSoundID(sfx->link);
930 if (near_limit < 0)
931 {
932 near_limit = S_sfx[sound_id].NearLimit;
933 limit_range = S_sfx[sound_id].LimitRange;
934 }
935 if (rolloff->MinDistance == 0)
936 {
937 rolloff = &S_sfx[sound_id].Rolloff;
938 }
939 }
940 sfx = &S_sfx[sound_id];
941 }
942
943 // Attenuate the attenuation based on the sound.
944 attenuation *= sfx->Attenuation;
945
946 // The passed rolloff overrides any sound-specific rolloff.
947 if (forcedrolloff != NULL && forcedrolloff->MinDistance != 0)
948 {
949 rolloff = forcedrolloff;
950 }
951
952 // If no valid rolloff was set, use the global default.
953 if (rolloff->MinDistance == 0)
954 {
955 rolloff = &S_Rolloff;
956 }
957
958 // If this is a singular sound, don't play it if it's already playing.
959 if (sfx->bSingular && S_CheckSingular(sound_id))
960 {
961 chanflags |= CHAN_EVICTED;
962 }
963
964 // If the sound is unpositioned or comes from the listener, it is
965 // never limited.
966 if (type == SOURCE_None || actor == players[consoleplayer].camera)
967 {
968 near_limit = 0;
969 }
970
971 // If this sound doesn't like playing near itself, don't play it if
972 // that's what would happen.
973 if (near_limit > 0 && S_CheckSoundLimit(sfx, pos, near_limit, limit_range, actor, channel))
974 {
975 chanflags |= CHAN_EVICTED;
976 }
977
978 // If the sound is blocked and not looped, return now. If the sound
979 // is blocked and looped, pretend to play it so that it can
980 // eventually play for real.
981 if ((chanflags & (CHAN_EVICTED | CHAN_LOOP)) == CHAN_EVICTED)
982 {
983 return NULL;
984 }
985
986 // Make sure the sound is loaded.
987 sfx = S_LoadSound(sfx);
988
989 // The empty sound never plays.
990 if (sfx->lumpnum == sfx_empty)
991 {
992 return NULL;
993 }
994
995 // Select priority.
996 if (type == SOURCE_None || actor == players[consoleplayer].camera)
997 {
998 basepriority = 80;
999 }
1000 else
1001 {
1002 basepriority = 0;
1003 }
1004
1005 int seen = 0;
1006 if (actor != NULL && channel == CHAN_AUTO)
1007 {
1008 // Select a channel that isn't already playing something.
1009 // Try channel 0 first, then travel from channel 7 down.
1010 if (!S_IsChannelUsed(actor, 0, &seen))
1011 {
1012 channel = 0;
1013 }
1014 else
1015 {
1016 for (channel = 7; channel > 0; --channel)
1017 {
1018 if (!S_IsChannelUsed(actor, channel, &seen))
1019 {
1020 break;
1021 }
1022 }
1023 if (channel == 0)
1024 { // Crap. No free channels.
1025 return NULL;
1026 }
1027 }
1028 }
1029
1030 // If this actor is already playing something on the selected channel, stop it.
1031 if (type != SOURCE_None && ((actor == NULL && channel != CHAN_AUTO) || (actor != NULL && S_IsChannelUsed(actor, channel, &seen))))
1032 {
1033 for (chan = Channels; chan != NULL; chan = chan->NextChan)
1034 {
1035 if (chan->SourceType == type && chan->EntChannel == channel)
1036 {
1037 bool foundit;
1038
1039 switch (type)
1040 {
1041 case SOURCE_Actor: foundit = (chan->Actor == actor); break;
1042 case SOURCE_Sector: foundit = (chan->Sector == sec); break;
1043 case SOURCE_Polyobj: foundit = (chan->Poly == poly); break;
1044 case SOURCE_Unattached: foundit = (chan->Point[0] == pt->X && chan->Point[2] == pt->Z && chan->Point[1] == pt->Y); break;
1045 default: foundit = false; break;
1046 }
1047 if (foundit)
1048 {
1049 S_StopChannel(chan);
1050 break;
1051 }
1052 }
1053 }
1054 }
1055
1056 // sound is paused and a non-looped sound is being started.
1057 // Such a sound would play right after unpausing which wouldn't sound right.
1058 if (!(chanflags & CHAN_LOOP) && !(chanflags & (CHAN_UI|CHAN_NOPAUSE)) && SoundPaused)
1059 {
1060 return NULL;
1061 }
1062
1063 // Vary the sfx pitches.
1064 if (sfx->PitchMask != 0)
1065 {
1066 pitch = NORM_PITCH - (M_Random() & sfx->PitchMask) + (M_Random() & sfx->PitchMask);
1067 }
1068 else
1069 {
1070 pitch = NORM_PITCH;
1071 }
1072
1073 if (chanflags & CHAN_EVICTED)
1074 {
1075 chan = NULL;
1076 }
1077 else
1078 {
1079 int startflags = 0;
1080 if (chanflags & CHAN_LOOP) startflags |= SNDF_LOOP;
1081 if (chanflags & CHAN_AREA) startflags |= SNDF_AREA;
1082 if (chanflags & (CHAN_UI|CHAN_NOPAUSE)) startflags |= SNDF_NOPAUSE;
1083 if (chanflags & CHAN_UI) startflags |= SNDF_NOREVERB;
1084
1085 if (attenuation > 0)
1086 {
1087 SoundListener listener;
1088 S_SetListener(listener, players[consoleplayer].camera);
1089 chan = (FSoundChan*)GSnd->StartSound3D (sfx->data, &listener, volume, rolloff, attenuation, pitch, basepriority, pos, vel, channel, startflags, NULL);
1090 }
1091 else
1092 {
1093 chan = (FSoundChan*)GSnd->StartSound (sfx->data, volume, pitch, startflags, NULL);
1094 }
1095 }
1096 if (chan == NULL && (chanflags & CHAN_LOOP))
1097 {
1098 chan = (FSoundChan*)S_GetChannel(NULL);
1099 GSnd->MarkStartTime(chan);
1100 chanflags |= CHAN_EVICTED;
1101 }
1102 if (attenuation > 0)
1103 {
1104 chanflags |= CHAN_IS3D | CHAN_JUSTSTARTED;
1105 }
1106 else
1107 {
1108 chanflags |= CHAN_LISTENERZ | CHAN_JUSTSTARTED;
1109 }
1110 if (chan != NULL)
1111 {
1112 chan->SoundID = sound_id;
1113 chan->OrgID = FSoundID(org_id);
1114 chan->EntChannel = channel;
1115 chan->Volume = volume;
1116 chan->ChanFlags |= chanflags;
1117 chan->NearLimit = near_limit;
1118 chan->LimitRange = limit_range;
1119 chan->Pitch = pitch;
1120 chan->Priority = basepriority;
1121 chan->DistanceScale = attenuation;
1122 chan->SourceType = type;
1123 switch (type)
1124 {
1125 case SOURCE_Actor: chan->Actor = actor; break;
1126 case SOURCE_Sector: chan->Sector = sec; break;
1127 case SOURCE_Polyobj: chan->Poly = poly; break;
1128 case SOURCE_Unattached: chan->Point[0] = pt->X; chan->Point[1] = pt->Y; chan->Point[2] = pt->Z; break;
1129 default: break;
1130 }
1131 }
1132 return chan;
1133 }
1134
1135 //==========================================================================
1136 //
1137 // S_RestartSound
1138 //
1139 // Attempts to restart looping sounds that were evicted from their channels.
1140 //
1141 //==========================================================================
1142
S_RestartSound(FSoundChan * chan)1143 void S_RestartSound(FSoundChan *chan)
1144 {
1145 assert(chan->ChanFlags & CHAN_EVICTED);
1146
1147 FSoundChan *ochan;
1148 sfxinfo_t *sfx = &S_sfx[chan->SoundID];
1149
1150 // If this is a singular sound, don't play it if it's already playing.
1151 if (sfx->bSingular && S_CheckSingular(chan->SoundID))
1152 return;
1153
1154 sfx = S_LoadSound(sfx);
1155
1156 // The empty sound never plays.
1157 if (sfx->lumpnum == sfx_empty)
1158 {
1159 return;
1160 }
1161
1162 int oldflags = chan->ChanFlags;
1163
1164 int startflags = 0;
1165 if (chan->ChanFlags & CHAN_LOOP) startflags |= SNDF_LOOP;
1166 if (chan->ChanFlags & CHAN_AREA) startflags |= SNDF_AREA;
1167 if (chan->ChanFlags & (CHAN_UI|CHAN_NOPAUSE)) startflags |= SNDF_NOPAUSE;
1168 if (chan->ChanFlags & CHAN_ABSTIME) startflags |= SNDF_ABSTIME;
1169
1170 if (chan->ChanFlags & CHAN_IS3D)
1171 {
1172 FVector3 pos, vel;
1173
1174 CalcPosVel(chan, &pos, &vel);
1175
1176 // If this sound doesn't like playing near itself, don't play it if
1177 // that's what would happen.
1178 if (chan->NearLimit > 0 && S_CheckSoundLimit(&S_sfx[chan->SoundID], pos, chan->NearLimit, chan->LimitRange, NULL, 0))
1179 {
1180 return;
1181 }
1182
1183 SoundListener listener;
1184 S_SetListener(listener, players[consoleplayer].camera);
1185
1186 chan->ChanFlags &= ~(CHAN_EVICTED|CHAN_ABSTIME);
1187 ochan = (FSoundChan*)GSnd->StartSound3D(sfx->data, &listener, chan->Volume, &chan->Rolloff, chan->DistanceScale, chan->Pitch,
1188 chan->Priority, pos, vel, chan->EntChannel, startflags, chan);
1189 }
1190 else
1191 {
1192 chan->ChanFlags &= ~(CHAN_EVICTED|CHAN_ABSTIME);
1193 ochan = (FSoundChan*)GSnd->StartSound(sfx->data, chan->Volume, chan->Pitch, startflags, chan);
1194 }
1195 assert(ochan == NULL || ochan == chan);
1196 if (ochan == NULL)
1197 {
1198 chan->ChanFlags = oldflags;
1199 }
1200 }
1201
1202 //==========================================================================
1203 //
1204 // S_Sound - Unpositioned version
1205 //
1206 //==========================================================================
1207
S_Sound(int channel,FSoundID sound_id,float volume,float attenuation)1208 void S_Sound (int channel, FSoundID sound_id, float volume, float attenuation)
1209 {
1210 S_StartSound (NULL, NULL, NULL, NULL, channel, sound_id, volume, attenuation);
1211 }
1212
1213 //==========================================================================
1214 //
1215 // S_Sound - An actor is source
1216 //
1217 //==========================================================================
1218
S_Sound(AActor * ent,int channel,FSoundID sound_id,float volume,float attenuation)1219 void S_Sound (AActor *ent, int channel, FSoundID sound_id, float volume, float attenuation)
1220 {
1221 if (ent == NULL || ent->Sector->Flags & SECF_SILENT)
1222 return;
1223 S_StartSound (ent, NULL, NULL, NULL, channel, sound_id, volume, attenuation);
1224 }
1225
1226 //==========================================================================
1227 //
1228 // S_SoundMinMaxDist - An actor is source
1229 //
1230 // Attenuation is specified as min and max distances, rather than a scalar.
1231 //
1232 //==========================================================================
1233
S_SoundMinMaxDist(AActor * ent,int channel,FSoundID sound_id,float volume,float mindist,float maxdist)1234 void S_SoundMinMaxDist(AActor *ent, int channel, FSoundID sound_id, float volume, float mindist, float maxdist)
1235 {
1236 if (ent == NULL || ent->Sector->Flags & SECF_SILENT)
1237 return;
1238
1239 FRolloffInfo rolloff;
1240
1241 rolloff.RolloffType = ROLLOFF_Linear;
1242 rolloff.MinDistance = mindist;
1243 rolloff.MaxDistance = maxdist;
1244 S_StartSound(ent, NULL, NULL, NULL, channel, sound_id, volume, 1, &rolloff);
1245 }
1246
1247 //==========================================================================
1248 //
1249 // S_Sound - A polyobject is source
1250 //
1251 //==========================================================================
1252
S_Sound(const FPolyObj * poly,int channel,FSoundID sound_id,float volume,float attenuation)1253 void S_Sound (const FPolyObj *poly, int channel, FSoundID sound_id, float volume, float attenuation)
1254 {
1255 S_StartSound (NULL, NULL, poly, NULL, channel, sound_id, volume, attenuation);
1256 }
1257
1258 //==========================================================================
1259 //
1260 // S_Sound - A point is source
1261 //
1262 //==========================================================================
1263
S_Sound(fixed_t x,fixed_t y,fixed_t z,int channel,FSoundID sound_id,float volume,float attenuation)1264 void S_Sound (fixed_t x, fixed_t y, fixed_t z, int channel, FSoundID sound_id, float volume, float attenuation)
1265 {
1266 FVector3 pt(FIXED2FLOAT(x), FIXED2FLOAT(z), FIXED2FLOAT(y));
1267 S_StartSound (NULL, NULL, NULL, &pt, channel, sound_id, volume, attenuation);
1268 }
1269
1270 //==========================================================================
1271 //
1272 // S_Sound - An entire sector is source
1273 //
1274 //==========================================================================
1275
S_Sound(const sector_t * sec,int channel,FSoundID sfxid,float volume,float attenuation)1276 void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation)
1277 {
1278 S_StartSound (NULL, sec, NULL, NULL, channel, sfxid, volume, attenuation);
1279 }
1280
1281 //==========================================================================
1282 //
1283 // S_LoadSound
1284 //
1285 // Returns a pointer to the sfxinfo with the actual sound data.
1286 //
1287 //==========================================================================
1288
S_LoadSound(sfxinfo_t * sfx)1289 sfxinfo_t *S_LoadSound(sfxinfo_t *sfx)
1290 {
1291 if (GSnd->IsNull()) return sfx;
1292
1293 while (!sfx->data.isValid())
1294 {
1295 unsigned int i;
1296
1297 // If the sound doesn't exist, replace it with the empty sound.
1298 if (sfx->lumpnum == -1)
1299 {
1300 sfx->lumpnum = sfx_empty;
1301 }
1302
1303 // See if there is another sound already initialized with this lump. If so,
1304 // then set this one up as a link, and don't load the sound again.
1305 for (i = 0; i < S_sfx.Size(); i++)
1306 {
1307 if (S_sfx[i].data.isValid() && S_sfx[i].link == sfxinfo_t::NO_LINK && S_sfx[i].lumpnum == sfx->lumpnum)
1308 {
1309 DPrintf ("Linked %s to %s (%d)\n", sfx->name.GetChars(), S_sfx[i].name.GetChars(), i);
1310 sfx->link = i;
1311 // This is necessary to avoid using the rolloff settings of the linked sound if its
1312 // settings are different.
1313 if (sfx->Rolloff.MinDistance == 0) sfx->Rolloff = S_Rolloff;
1314 return &S_sfx[i];
1315 }
1316 }
1317
1318 DPrintf("Loading sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]);
1319
1320 int size = Wads.LumpLength(sfx->lumpnum);
1321 if (size > 0)
1322 {
1323 FWadLump wlump = Wads.OpenLumpNum(sfx->lumpnum);
1324 BYTE *sfxdata = new BYTE[size];
1325 wlump.Read(sfxdata, size);
1326 SDWORD dmxlen = LittleLong(((SDWORD *)sfxdata)[1]);
1327
1328 // If the sound is voc, use the custom loader.
1329 if (strncmp ((const char *)sfxdata, "Creative Voice File", 19) == 0)
1330 {
1331 sfx->data = GSnd->LoadSoundVoc(sfxdata, size);
1332 }
1333 // If the sound is raw, just load it as such.
1334 else if (sfx->bLoadRAW)
1335 {
1336 sfx->data = GSnd->LoadSoundRaw(sfxdata, size, sfx->RawRate, 1, 8, sfx->LoopStart);
1337 }
1338 // Otherwise, try the sound as DMX format.
1339 else if (((BYTE *)sfxdata)[0] == 3 && ((BYTE *)sfxdata)[1] == 0 && dmxlen <= size - 8)
1340 {
1341 int frequency = LittleShort(((WORD *)sfxdata)[1]);
1342 if (frequency == 0) frequency = 11025;
1343 sfx->data = GSnd->LoadSoundRaw(sfxdata+8, dmxlen, frequency, 1, 8, sfx->LoopStart);
1344 }
1345 // If that fails, let the sound system try and figure it out.
1346 else
1347 {
1348 sfx->data = GSnd->LoadSound(sfxdata, size);
1349 }
1350
1351 delete[] sfxdata;
1352 }
1353
1354 if (!sfx->data.isValid())
1355 {
1356 if (sfx->lumpnum != sfx_empty)
1357 {
1358 sfx->lumpnum = sfx_empty;
1359 continue;
1360 }
1361 }
1362 break;
1363 }
1364 return sfx;
1365 }
1366
1367 //==========================================================================
1368 //
1369 // S_CheckSingular
1370 //
1371 // Returns true if a copy of this sound is already playing.
1372 //
1373 //==========================================================================
1374
S_CheckSingular(int sound_id)1375 bool S_CheckSingular(int sound_id)
1376 {
1377 for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
1378 {
1379 if (chan->OrgID == sound_id)
1380 {
1381 return true;
1382 }
1383 }
1384 return false;
1385 }
1386
1387 //==========================================================================
1388 //
1389 // S_CheckSoundLimit
1390 //
1391 // Limits the number of nearby copies of a sound that can play near
1392 // each other. If there are NearLimit instances of this sound already
1393 // playing within sqrt(limit_range) (typically 256 units) of the new sound, the
1394 // new sound will not start.
1395 //
1396 // If an actor is specified, and it is already playing the same sound on
1397 // the same channel, this sound will not be limited. In this case, we're
1398 // restarting an already playing sound, so there's no need to limit it.
1399 //
1400 // Returns true if the sound should not play.
1401 //
1402 //==========================================================================
1403
S_CheckSoundLimit(sfxinfo_t * sfx,const FVector3 & pos,int near_limit,float limit_range,AActor * actor,int channel)1404 bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range,
1405 AActor *actor, int channel)
1406 {
1407 FSoundChan *chan;
1408 int count;
1409
1410 for (chan = Channels, count = 0; chan != NULL && count < near_limit; chan = chan->NextChan)
1411 {
1412 if (!(chan->ChanFlags & CHAN_EVICTED) && &S_sfx[chan->SoundID] == sfx)
1413 {
1414 FVector3 chanorigin;
1415
1416 if (actor != NULL && chan->EntChannel == channel &&
1417 chan->SourceType == SOURCE_Actor && chan->Actor == actor)
1418 { // We are restarting a playing sound. Always let it play.
1419 return false;
1420 }
1421
1422 CalcPosVel(chan, &chanorigin, NULL);
1423 if ((chanorigin - pos).LengthSquared() <= limit_range)
1424 {
1425 count++;
1426 }
1427 }
1428 }
1429 return count >= near_limit;
1430 }
1431
1432 //==========================================================================
1433 //
1434 // S_StopSound
1435 //
1436 // Stops an unpositioned sound from playing on a specific channel.
1437 //
1438 //==========================================================================
1439
S_StopSound(int channel)1440 void S_StopSound (int channel)
1441 {
1442 FSoundChan *chan = Channels;
1443 while (chan != NULL)
1444 {
1445 FSoundChan *next = chan->NextChan;
1446 if (chan->SourceType == SOURCE_None &&
1447 (chan->EntChannel == channel || (i_compatflags & COMPATF_MAGICSILENCE)))
1448 {
1449 S_StopChannel(chan);
1450 }
1451 chan = next;
1452 }
1453 }
1454
1455 //==========================================================================
1456 //
1457 // S_StopSound
1458 //
1459 // Stops a sound from a single actor from playing on a specific channel.
1460 //
1461 //==========================================================================
1462
S_StopSound(AActor * actor,int channel)1463 void S_StopSound (AActor *actor, int channel)
1464 {
1465 FSoundChan *chan = Channels;
1466 while (chan != NULL)
1467 {
1468 FSoundChan *next = chan->NextChan;
1469 if (chan->SourceType == SOURCE_Actor &&
1470 chan->Actor == actor &&
1471 (chan->EntChannel == channel || (i_compatflags & COMPATF_MAGICSILENCE)))
1472 {
1473 S_StopChannel(chan);
1474 }
1475 chan = next;
1476 }
1477 }
1478
1479 //==========================================================================
1480 //
1481 // S_StopSound
1482 //
1483 // Stops a sound from a single sector from playing on a specific channel.
1484 //
1485 //==========================================================================
1486
S_StopSound(const sector_t * sec,int channel)1487 void S_StopSound (const sector_t *sec, int channel)
1488 {
1489 FSoundChan *chan = Channels;
1490 while (chan != NULL)
1491 {
1492 FSoundChan *next = chan->NextChan;
1493 if (chan->SourceType == SOURCE_Sector &&
1494 chan->Sector == sec &&
1495 (chan->EntChannel == channel || (i_compatflags & COMPATF_MAGICSILENCE)))
1496 {
1497 S_StopChannel(chan);
1498 }
1499 chan = next;
1500 }
1501 }
1502
1503 //==========================================================================
1504 //
1505 // S_StopSound
1506 //
1507 // Stops a sound from a single polyobject from playing on a specific channel.
1508 //
1509 //==========================================================================
1510
S_StopSound(const FPolyObj * poly,int channel)1511 void S_StopSound (const FPolyObj *poly, int channel)
1512 {
1513 FSoundChan *chan = Channels;
1514 while (chan != NULL)
1515 {
1516 FSoundChan *next = chan->NextChan;
1517 if (chan->SourceType == SOURCE_Polyobj &&
1518 chan->Poly == poly &&
1519 (chan->EntChannel == channel || (i_compatflags & COMPATF_MAGICSILENCE)))
1520 {
1521 S_StopChannel(chan);
1522 }
1523 chan = next;
1524 }
1525 }
1526
1527 //==========================================================================
1528 //
1529 // S_StopAllChannels
1530 //
1531 //==========================================================================
1532
S_StopAllChannels()1533 void S_StopAllChannels ()
1534 {
1535 SN_StopAllSequences();
1536
1537 FSoundChan *chan = Channels;
1538 while (chan != NULL)
1539 {
1540 FSoundChan *next = chan->NextChan;
1541 S_StopChannel(chan);
1542 chan = next;
1543 }
1544 GSnd->UpdateSounds();
1545 }
1546
1547 //==========================================================================
1548 //
1549 // S_RelinkSound
1550 //
1551 // Moves all the sounds from one thing to another. If the destination is
1552 // NULL, then the sound becomes a positioned sound.
1553 //==========================================================================
1554
S_RelinkSound(AActor * from,AActor * to)1555 void S_RelinkSound (AActor *from, AActor *to)
1556 {
1557 if (from == NULL)
1558 return;
1559
1560 FSoundChan *chan = Channels;
1561 while (chan != NULL)
1562 {
1563 FSoundChan *next = chan->NextChan;
1564 if (chan->SourceType == SOURCE_Actor && chan->Actor == from)
1565 {
1566 if (to != NULL)
1567 {
1568 chan->Actor = to;
1569 }
1570 else if (!(chan->ChanFlags & CHAN_LOOP) && !(compatflags2 & COMPATF2_SOUNDCUTOFF))
1571 {
1572 chan->Actor = NULL;
1573 chan->SourceType = SOURCE_Unattached;
1574 chan->Point[0] = FIXED2FLOAT(from->SoundX());
1575 chan->Point[1] = FIXED2FLOAT(from->SoundZ());
1576 chan->Point[2] = FIXED2FLOAT(from->SoundY());
1577 }
1578 else
1579 {
1580 S_StopChannel(chan);
1581 }
1582 }
1583 chan = next;
1584 }
1585 }
1586
1587
1588 //==========================================================================
1589 //
1590 // S_ChangeSoundVolume
1591 //
1592 //==========================================================================
1593
S_ChangeSoundVolume(AActor * actor,int channel,float volume)1594 bool S_ChangeSoundVolume(AActor *actor, int channel, float volume)
1595 {
1596 for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
1597 {
1598 if (chan->SourceType == SOURCE_Actor &&
1599 chan->Actor == actor &&
1600 (chan->EntChannel == channel || (i_compatflags & COMPATF_MAGICSILENCE)))
1601 {
1602 GSnd->ChannelVolume(chan, volume);
1603 chan->Volume = volume;
1604 return true;
1605 }
1606 }
1607 return false;
1608 }
1609
1610 //==========================================================================
1611 //
1612 // S_GetSoundPlayingInfo
1613 //
1614 // Is a sound being played by a specific emitter?
1615 //==========================================================================
1616
S_GetSoundPlayingInfo(const AActor * actor,int sound_id)1617 bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id)
1618 {
1619 if (sound_id > 0)
1620 {
1621 for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
1622 {
1623 if (chan->OrgID == sound_id &&
1624 chan->SourceType == SOURCE_Actor &&
1625 chan->Actor == actor)
1626 {
1627 return true;
1628 }
1629 }
1630 }
1631 return false;
1632 }
1633
S_GetSoundPlayingInfo(const sector_t * sec,int sound_id)1634 bool S_GetSoundPlayingInfo (const sector_t *sec, int sound_id)
1635 {
1636 if (sound_id > 0)
1637 {
1638 for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
1639 {
1640 if (chan->OrgID == sound_id &&
1641 chan->SourceType == SOURCE_Sector &&
1642 chan->Sector == sec)
1643 {
1644 return true;
1645 }
1646 }
1647 }
1648 return false;
1649 }
1650
S_GetSoundPlayingInfo(const FPolyObj * poly,int sound_id)1651 bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id)
1652 {
1653 if (sound_id > 0)
1654 {
1655 for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
1656 {
1657 if (chan->OrgID == sound_id &&
1658 chan->SourceType == SOURCE_Polyobj &&
1659 chan->Poly == poly)
1660 {
1661 return true;
1662 }
1663 }
1664 }
1665 return false;
1666 }
1667
1668 //==========================================================================
1669 //
1670 // S_IsChannelUsed
1671 //
1672 // Returns true if the channel is in use. Also fills in a bitmask of
1673 // channels seen while scanning for this one, to make searching for unused
1674 // channels faster. Initialize seen to 0 for the first call.
1675 //
1676 //==========================================================================
1677
S_IsChannelUsed(AActor * actor,int channel,int * seen)1678 static bool S_IsChannelUsed(AActor *actor, int channel, int *seen)
1679 {
1680 if (*seen & (1 << channel))
1681 {
1682 return true;
1683 }
1684 for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
1685 {
1686 if (chan->SourceType == SOURCE_Actor && chan->Actor == actor)
1687 {
1688 *seen |= 1 << chan->EntChannel;
1689 if (chan->EntChannel == channel)
1690 {
1691 return true;
1692 }
1693 }
1694 }
1695 return false;
1696 }
1697
1698 //==========================================================================
1699 //
1700 // S_IsActorPlayingSomething
1701 //
1702 //==========================================================================
1703
S_IsActorPlayingSomething(AActor * actor,int channel,int sound_id)1704 bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id)
1705 {
1706 if (i_compatflags & COMPATF_MAGICSILENCE)
1707 {
1708 channel = 0;
1709 }
1710
1711 for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
1712 {
1713 if (chan->SourceType == SOURCE_Actor && chan->Actor == actor)
1714 {
1715 if (channel == 0 || chan->EntChannel == channel)
1716 {
1717 return sound_id <= 0 || chan->OrgID == sound_id;
1718 }
1719 }
1720 }
1721 return false;
1722 }
1723
1724 //==========================================================================
1725 //
1726 // S_PauseSound
1727 //
1728 // Stop music and sound effects, during game PAUSE.
1729 //==========================================================================
1730
S_PauseSound(bool notmusic,bool notsfx)1731 void S_PauseSound (bool notmusic, bool notsfx)
1732 {
1733 if (!notmusic && mus_playing.handle && !MusicPaused)
1734 {
1735 mus_playing.handle->Pause();
1736 MusicPaused = true;
1737 }
1738 if (!notsfx)
1739 {
1740 SoundPaused = true;
1741 GSnd->SetSfxPaused (true, 0);
1742 }
1743 }
1744
1745 //==========================================================================
1746 //
1747 // S_ResumeSound
1748 //
1749 // Resume music and sound effects, after game PAUSE.
1750 //==========================================================================
1751
S_ResumeSound(bool notsfx)1752 void S_ResumeSound (bool notsfx)
1753 {
1754 if (mus_playing.handle && MusicPaused)
1755 {
1756 mus_playing.handle->Resume();
1757 MusicPaused = false;
1758 }
1759 if (!notsfx)
1760 {
1761 SoundPaused = false;
1762 GSnd->SetSfxPaused (false, 0);
1763 }
1764 }
1765
1766 //==========================================================================
1767 //
1768 // S_SetSoundPaused
1769 //
1770 // Called with state non-zero when the app is active, zero when it isn't.
1771 //
1772 //==========================================================================
1773
S_SetSoundPaused(int state)1774 void S_SetSoundPaused (int state)
1775 {
1776 if (state)
1777 {
1778 if (paused == 0)
1779 {
1780 S_ResumeSound(true);
1781 if (GSnd != NULL)
1782 {
1783 GSnd->SetInactive(SoundRenderer::INACTIVE_Active);
1784 }
1785 }
1786 }
1787 else
1788 {
1789 if (paused == 0)
1790 {
1791 S_PauseSound(false, true);
1792 if (GSnd != NULL)
1793 {
1794 GSnd->SetInactive(gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL ?
1795 SoundRenderer::INACTIVE_Complete :
1796 SoundRenderer::INACTIVE_Mute);
1797 }
1798 }
1799 }
1800 if (!netgame
1801 #ifdef _DEBUG
1802 && !demoplayback
1803 #endif
1804 )
1805 {
1806 pauseext = !state;
1807 }
1808 }
1809
1810 //==========================================================================
1811 //
1812 // S_EvictAllChannels
1813 //
1814 // Forcibly evicts all channels so that there are none playing, but all
1815 // information needed to restart them is retained.
1816 //
1817 //==========================================================================
1818
S_EvictAllChannels()1819 void S_EvictAllChannels()
1820 {
1821 FSoundChan *chan, *next;
1822
1823 for (chan = Channels; chan != NULL; chan = next)
1824 {
1825 next = chan->NextChan;
1826
1827 if (!(chan->ChanFlags & CHAN_EVICTED))
1828 {
1829 chan->ChanFlags |= CHAN_EVICTED;
1830 if (chan->SysChannel != NULL)
1831 {
1832 if (!(chan->ChanFlags & CHAN_ABSTIME))
1833 {
1834 chan->StartTime.AsOne = GSnd ? GSnd->GetPosition(chan) : 0;
1835 chan->ChanFlags |= CHAN_ABSTIME;
1836 }
1837 S_StopChannel(chan);
1838 }
1839 // assert(chan->NextChan == next);
1840 }
1841 }
1842 }
1843
1844 //==========================================================================
1845 //
1846 // S_RestoreEvictedChannel
1847 //
1848 // Recursive helper for S_RestoreEvictedChannels().
1849 //
1850 //==========================================================================
1851
S_RestoreEvictedChannel(FSoundChan * chan)1852 void S_RestoreEvictedChannel(FSoundChan *chan)
1853 {
1854 if (chan == NULL)
1855 {
1856 return;
1857 }
1858 S_RestoreEvictedChannel(chan->NextChan);
1859 if (chan->ChanFlags & CHAN_EVICTED)
1860 {
1861 S_RestartSound(chan);
1862 if (!(chan->ChanFlags & CHAN_LOOP))
1863 {
1864 if (chan->ChanFlags & CHAN_EVICTED)
1865 { // Still evicted and not looping? Forget about it.
1866 S_ReturnChannel(chan);
1867 }
1868 else if (!(chan->ChanFlags & CHAN_JUSTSTARTED))
1869 { // Should this sound become evicted again, it's okay to forget about it.
1870 chan->ChanFlags |= CHAN_FORGETTABLE;
1871 }
1872 }
1873 }
1874 else if (chan->SysChannel == NULL && (chan->ChanFlags & (CHAN_FORGETTABLE | CHAN_LOOP)) == CHAN_FORGETTABLE)
1875 {
1876 S_ReturnChannel(chan);
1877 }
1878 }
1879
1880 //==========================================================================
1881 //
1882 // S_RestoreEvictedChannels
1883 //
1884 // Restarts as many evicted channels as possible. Any channels that could
1885 // not be started and are not looping are moved to the free pool.
1886 //
1887 //==========================================================================
1888
S_RestoreEvictedChannels()1889 void S_RestoreEvictedChannels()
1890 {
1891 // Restart channels in the same order they were originally played.
1892 S_RestoreEvictedChannel(Channels);
1893 }
1894
1895 //==========================================================================
1896 //
1897 // S_UpdateSounds
1898 //
1899 // Updates music & sounds
1900 //==========================================================================
1901
S_UpdateSounds(AActor * listenactor)1902 void S_UpdateSounds (AActor *listenactor)
1903 {
1904 FVector3 pos, vel;
1905 SoundListener listener;
1906
1907 I_UpdateMusic();
1908
1909 // [RH] Update music and/or playlist. IsPlaying() must be called
1910 // to attempt to reconnect to broken net streams and to advance the
1911 // playlist when the current song finishes.
1912 if (mus_playing.handle != NULL &&
1913 !mus_playing.handle->IsPlaying() &&
1914 PlayList)
1915 {
1916 PlayList->Advance();
1917 S_ActivatePlayList(false);
1918 }
1919
1920 // should never happen
1921 S_SetListener(listener, listenactor);
1922
1923 for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
1924 {
1925 if ((chan->ChanFlags & (CHAN_EVICTED | CHAN_IS3D)) == CHAN_IS3D)
1926 {
1927 CalcPosVel(chan, &pos, &vel);
1928 GSnd->UpdateSoundParams3D(&listener, chan, !!(chan->ChanFlags & CHAN_AREA), pos, vel);
1929 }
1930 chan->ChanFlags &= ~CHAN_JUSTSTARTED;
1931 }
1932
1933 SN_UpdateActiveSequences();
1934
1935
1936 GSnd->UpdateListener(&listener);
1937 GSnd->UpdateSounds();
1938
1939 if (level.time >= RestartEvictionsAt)
1940 {
1941 RestartEvictionsAt = 0;
1942 S_RestoreEvictedChannels();
1943 }
1944 }
1945
1946 //==========================================================================
1947 //
1948 // Sets the internal listener structure
1949 //
1950 //==========================================================================
1951
S_SetListener(SoundListener & listener,AActor * listenactor)1952 static void S_SetListener(SoundListener &listener, AActor *listenactor)
1953 {
1954 if (listenactor != NULL)
1955 {
1956 listener.angle = (float)(listenactor->angle) * ((float)PI / 2147483648.f);
1957 /*
1958 listener.velocity.X = listenactor->velx * (TICRATE/65536.f);
1959 listener.velocity.Y = listenactor->velz * (TICRATE/65536.f);
1960 listener.velocity.Z = listenactor->vely * (TICRATE/65536.f);
1961 */
1962 listener.velocity.Zero();
1963 listener.position.X = FIXED2FLOAT(listenactor->SoundX());
1964 listener.position.Y = FIXED2FLOAT(listenactor->SoundZ());
1965 listener.position.Z = FIXED2FLOAT(listenactor->SoundY());
1966 listener.underwater = listenactor->waterlevel == 3;
1967 assert(zones != NULL);
1968 listener.Environment = zones[listenactor->Sector->ZoneNumber].Environment;
1969 listener.valid = true;
1970 }
1971 else
1972 {
1973 listener.angle = 0;
1974 listener.position.Zero();
1975 listener.velocity.Zero();
1976 listener.underwater = false;
1977 listener.Environment = NULL;
1978 listener.valid = false;
1979 }
1980 }
1981
1982
1983
1984 //==========================================================================
1985 //
1986 // S_GetRolloff
1987 //
1988 //==========================================================================
1989
S_GetRolloff(FRolloffInfo * rolloff,float distance,bool logarithmic)1990 float S_GetRolloff(FRolloffInfo *rolloff, float distance, bool logarithmic)
1991 {
1992 if (rolloff == NULL)
1993 {
1994 return 0;
1995 }
1996
1997 if (distance <= rolloff->MinDistance)
1998 {
1999 return 1;
2000 }
2001 if (rolloff->RolloffType == ROLLOFF_Log)
2002 { // Logarithmic rolloff has no max distance where it goes silent.
2003 return rolloff->MinDistance / (rolloff->MinDistance + rolloff->RolloffFactor * (distance - rolloff->MinDistance));
2004 }
2005 if (distance >= rolloff->MaxDistance)
2006 {
2007 return 0;
2008 }
2009
2010 float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance);
2011 if (rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve != NULL)
2012 {
2013 volume = S_SoundCurve[int(S_SoundCurveSize * (1 - volume))] / 127.f;
2014 }
2015 if (logarithmic)
2016 {
2017 if (rolloff->RolloffType == ROLLOFF_Linear)
2018 {
2019 return volume;
2020 }
2021 else
2022 {
2023 return float((powf(10.f, volume) - 1.) / 9.);
2024 }
2025 }
2026 else
2027 {
2028 if (rolloff->RolloffType == ROLLOFF_Linear)
2029 {
2030 return float(log10(9. * volume + 1.));
2031 }
2032 else
2033 {
2034 return volume;
2035 }
2036 }
2037 }
2038
2039
2040 //==========================================================================
2041 //
2042 // S_ChannelEnded (callback for sound interface code)
2043 //
2044 //==========================================================================
2045
S_ChannelEnded(FISoundChannel * ichan)2046 void S_ChannelEnded(FISoundChannel *ichan)
2047 {
2048 FSoundChan *schan = static_cast<FSoundChan*>(ichan);
2049 bool evicted;
2050
2051 if (schan != NULL)
2052 {
2053 // If the sound was stopped with GSnd->StopSound(), then we know
2054 // it wasn't evicted. Otherwise, if it's looping, it must have
2055 // been evicted. If it's not looping, then it was evicted if it
2056 // didn't reach the end of its playback.
2057 if (schan->ChanFlags & CHAN_FORGETTABLE)
2058 {
2059 evicted = false;
2060 }
2061 else if (schan->ChanFlags & (CHAN_LOOP | CHAN_EVICTED))
2062 {
2063 evicted = true;
2064 }
2065 else
2066 {
2067 unsigned int pos = GSnd->GetPosition(schan);
2068 unsigned int len = GSnd->GetSampleLength(S_sfx[schan->SoundID].data);
2069 if (pos == 0)
2070 {
2071 evicted = !!(schan->ChanFlags & CHAN_JUSTSTARTED);
2072 }
2073 else
2074 {
2075 evicted = (pos < len);
2076 }
2077 }
2078 if (!evicted)
2079 {
2080 S_ReturnChannel(schan);
2081 }
2082 else
2083 {
2084 schan->ChanFlags |= CHAN_EVICTED;
2085 schan->SysChannel = NULL;
2086 }
2087 }
2088 }
2089
2090 //==========================================================================
2091 //
2092 // S_ChannelVirtualChanged (callback for sound interface code)
2093 //
2094 //==========================================================================
2095
S_ChannelVirtualChanged(FISoundChannel * ichan,bool is_virtual)2096 void S_ChannelVirtualChanged(FISoundChannel *ichan, bool is_virtual)
2097 {
2098 FSoundChan *schan = static_cast<FSoundChan*>(ichan);
2099 if (is_virtual)
2100 {
2101 schan->ChanFlags |= CHAN_VIRTUAL;
2102 }
2103 else
2104 {
2105 schan->ChanFlags &= ~CHAN_VIRTUAL;
2106 }
2107 }
2108
2109 //==========================================================================
2110 //
2111 // S_StopChannel
2112 //
2113 //==========================================================================
2114
S_StopChannel(FSoundChan * chan)2115 void S_StopChannel(FSoundChan *chan)
2116 {
2117 if (chan == NULL)
2118 return;
2119
2120 if (chan->SysChannel != NULL)
2121 {
2122 // S_EvictAllChannels() will set the CHAN_EVICTED flag to indicate
2123 // that it wants to keep all the channel information around.
2124 if (!(chan->ChanFlags & CHAN_EVICTED))
2125 {
2126 chan->ChanFlags |= CHAN_FORGETTABLE;
2127 if (chan->SourceType == SOURCE_Actor)
2128 {
2129 chan->Actor = NULL;
2130 }
2131 }
2132 GSnd->StopChannel(chan);
2133 }
2134 else
2135 {
2136 S_ReturnChannel(chan);
2137 }
2138 }
2139
2140 //==========================================================================
2141 //
2142 // (FArchive &) << (FSoundID &)
2143 //
2144 //==========================================================================
2145
operator <<(FArchive & arc,FSoundID & sid)2146 FArchive &operator<<(FArchive &arc, FSoundID &sid)
2147 {
2148 if (arc.IsStoring())
2149 {
2150 arc.WriteName((const char *)sid);
2151 }
2152 else
2153 {
2154 sid = arc.ReadName();
2155 }
2156 return arc;
2157 }
2158
2159 //==========================================================================
2160 //
2161 // (FArchive &) << (FSoundChan &)
2162 //
2163 //==========================================================================
2164
operator <<(FArchive & arc,FSoundChan & chan)2165 static FArchive &operator<<(FArchive &arc, FSoundChan &chan)
2166 {
2167 arc << chan.SourceType;
2168 switch (chan.SourceType)
2169 {
2170 case SOURCE_None: break;
2171 case SOURCE_Actor: arc << chan.Actor; break;
2172 case SOURCE_Sector: arc << chan.Sector; break;
2173 case SOURCE_Polyobj: arc << chan.Poly; break;
2174 case SOURCE_Unattached: arc << chan.Point[0] << chan.Point[1] << chan.Point[2]; break;
2175 default: I_Error("Unknown sound source type %d\n", chan.SourceType); break;
2176 }
2177 arc << chan.SoundID
2178 << chan.OrgID
2179 << chan.Volume
2180 << chan.DistanceScale
2181 << chan.Pitch
2182 << chan.ChanFlags
2183 << chan.EntChannel
2184 << chan.Priority
2185 << chan.NearLimit
2186 << chan.StartTime
2187 << chan.Rolloff.RolloffType
2188 << chan.Rolloff.MinDistance
2189 << chan.Rolloff.MaxDistance
2190 << chan.LimitRange;
2191
2192 return arc;
2193 }
2194
2195 //==========================================================================
2196 //
2197 // S_SerializeSounds
2198 //
2199 //==========================================================================
2200
S_SerializeSounds(FArchive & arc)2201 void S_SerializeSounds(FArchive &arc)
2202 {
2203 FSoundChan *chan;
2204
2205 GSnd->Sync(true);
2206
2207 if (arc.IsStoring())
2208 {
2209 TArray<FSoundChan *> chans;
2210
2211 // Count channels and accumulate them so we can store them in
2212 // reverse order. That way, they will be in the same order when
2213 // reloaded later as they are now.
2214 for (chan = Channels; chan != NULL; chan = chan->NextChan)
2215 {
2216 // If the sound is forgettable, this is as good a time as
2217 // any to forget about it. And if it's a UI sound, it shouldn't
2218 // be stored in the savegame.
2219 if (!(chan->ChanFlags & (CHAN_FORGETTABLE | CHAN_UI)))
2220 {
2221 chans.Push(chan);
2222 }
2223 }
2224
2225 arc.WriteCount(chans.Size());
2226
2227 for (unsigned int i = chans.Size(); i-- != 0; )
2228 {
2229 // Replace start time with sample position.
2230 QWORD start = chans[i]->StartTime.AsOne;
2231 chans[i]->StartTime.AsOne = GSnd ? GSnd->GetPosition(chans[i]) : 0;
2232 arc << *chans[i];
2233 chans[i]->StartTime.AsOne = start;
2234 }
2235 }
2236 else
2237 {
2238 unsigned int count;
2239
2240 S_StopAllChannels();
2241 count = arc.ReadCount();
2242 for (unsigned int i = 0; i < count; ++i)
2243 {
2244 chan = (FSoundChan*)S_GetChannel(NULL);
2245 arc << *chan;
2246 // Sounds always start out evicted when restored from a save.
2247 chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME;
2248 }
2249 // The two tic delay is to make sure any screenwipes have finished.
2250 // This needs to be two because the game is run for one tic before
2251 // the wipe so that it can produce a screen to wipe to. So if we
2252 // only waited one tic to restart the sounds, they would start
2253 // playing before the wipe, and depending on the synchronization
2254 // between the main thread and the mixer thread at the time, the
2255 // sounds might be heard briefly before pausing for the wipe.
2256 RestartEvictionsAt = level.time + 2;
2257 }
2258 DSeqNode::SerializeSequences(arc);
2259 GSnd->Sync(false);
2260 GSnd->UpdateSounds();
2261 }
2262
2263 //==========================================================================
2264 //
2265 // S_ActivatePlayList
2266 //
2267 // Plays the next song in the playlist. If no songs in the playlist can be
2268 // played, then it is deleted.
2269 //==========================================================================
2270
S_ActivatePlayList(bool goBack)2271 void S_ActivatePlayList (bool goBack)
2272 {
2273 int startpos, pos;
2274
2275 startpos = pos = PlayList->GetPosition ();
2276 S_StopMusic (true);
2277 while (!S_ChangeMusic (PlayList->GetSong (pos), 0, false, true))
2278 {
2279 pos = goBack ? PlayList->Backup () : PlayList->Advance ();
2280 if (pos == startpos)
2281 {
2282 delete PlayList;
2283 PlayList = NULL;
2284 Printf ("Cannot play anything in the playlist.\n");
2285 return;
2286 }
2287 }
2288 }
2289
2290 //==========================================================================
2291 //
2292 // S_ChangeCDMusic
2293 //
2294 // Starts a CD track as music.
2295 //==========================================================================
2296
S_ChangeCDMusic(int track,unsigned int id,bool looping)2297 bool S_ChangeCDMusic (int track, unsigned int id, bool looping)
2298 {
2299 char temp[32];
2300
2301 if (id != 0)
2302 {
2303 mysnprintf (temp, countof(temp), ",CD,%d,%x", track, id);
2304 }
2305 else
2306 {
2307 mysnprintf (temp, countof(temp), ",CD,%d", track);
2308 }
2309 return S_ChangeMusic (temp, 0, looping);
2310 }
2311
2312 //==========================================================================
2313 //
2314 // S_StartMusic
2315 //
2316 // Starts some music with the given name.
2317 //==========================================================================
2318
S_StartMusic(const char * m_id)2319 bool S_StartMusic (const char *m_id)
2320 {
2321 return S_ChangeMusic (m_id, 0, false);
2322 }
2323
2324 //==========================================================================
2325 //
2326 // S_ChangeMusic
2327 //
2328 // Starts playing a music, possibly looping.
2329 //
2330 // [RH] If music is a MOD, starts it at position order. If name is of the
2331 // format ",CD,<track>,[cd id]" song is a CD track, and if [cd id] is
2332 // specified, it will only be played if the specified CD is in a drive.
2333 //==========================================================================
2334
S_ChangeMusic(const char * musicname,int order,bool looping,bool force)2335 bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force)
2336 {
2337 if (!force && PlayList)
2338 { // Don't change if a playlist is active
2339 return false;
2340 }
2341
2342 // allow specifying "*" as a placeholder to play the level's default music.
2343 if (musicname != NULL && !strcmp(musicname, "*"))
2344 {
2345 if (gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL)
2346 {
2347 musicname = level.Music;
2348 order = level.musicorder;
2349 }
2350 else
2351 {
2352 musicname = NULL;
2353 }
2354 }
2355
2356 if (musicname == NULL || musicname[0] == 0)
2357 {
2358 // Don't choke if the map doesn't have a song attached
2359 S_StopMusic (true);
2360 mus_playing.name = "";
2361 LastSong = "";
2362 return true;
2363 }
2364
2365 FString DEH_Music;
2366 if (musicname[0] == '$')
2367 {
2368 // handle dehacked replacement.
2369 // Any music name defined this way needs to be prefixed with 'D_' because
2370 // Doom.exe does not contain the prefix so these strings don't either.
2371 const char * mus_string = GStrings[musicname+1];
2372 if (mus_string != NULL)
2373 {
2374 DEH_Music << "D_" << mus_string;
2375 musicname = DEH_Music;
2376 }
2377 }
2378
2379 FName *aliasp = MusicAliases.CheckKey(musicname);
2380 if (aliasp != NULL)
2381 {
2382 if (*aliasp == NAME_None)
2383 {
2384 return true; // flagged to be ignored
2385 }
2386 musicname = aliasp->GetChars();
2387 }
2388
2389 if (!mus_playing.name.IsEmpty() &&
2390 mus_playing.handle != NULL &&
2391 stricmp (mus_playing.name, musicname) == 0 &&
2392 mus_playing.handle->m_Looping == looping)
2393 {
2394 if (order != mus_playing.baseorder)
2395 {
2396 if (mus_playing.handle->SetSubsong(order))
2397 {
2398 mus_playing.baseorder = order;
2399 }
2400 }
2401 else if (!mus_playing.handle->IsPlaying())
2402 {
2403 mus_playing.handle->Play(looping, order);
2404 }
2405 return true;
2406 }
2407
2408 if (strnicmp (musicname, ",CD,", 4) == 0)
2409 {
2410 int track = strtoul (musicname+4, NULL, 0);
2411 const char *more = strchr (musicname+4, ',');
2412 unsigned int id = 0;
2413
2414 if (more != NULL)
2415 {
2416 id = strtoul (more+1, NULL, 16);
2417 }
2418 S_StopMusic (true);
2419 mus_playing.handle = I_RegisterCDSong (track, id);
2420 }
2421 else
2422 {
2423 int lumpnum = -1;
2424 int length = 0;
2425 MusInfo *handle = NULL;
2426 MidiDeviceSetting *devp = MidiDevices.CheckKey(musicname);
2427
2428 // Strip off any leading file:// component.
2429 if (strncmp(musicname, "file://", 7) == 0)
2430 {
2431 musicname += 7;
2432 }
2433
2434 FileReader *reader = NULL;
2435 if (!FileExists (musicname))
2436 {
2437 if ((lumpnum = Wads.CheckNumForFullName (musicname, true, ns_music)) == -1)
2438 {
2439 if (strstr(musicname, "://") > musicname)
2440 {
2441 // Looks like a URL; try it as such.
2442 handle = I_RegisterURLSong(musicname);
2443 if (handle == NULL)
2444 {
2445 Printf ("Could not open \"%s\"\n", musicname);
2446 return false;
2447 }
2448 }
2449 else
2450 {
2451 Printf ("Music \"%s\" not found\n", musicname);
2452 return false;
2453 }
2454 }
2455 if (handle == NULL)
2456 {
2457 if (Wads.LumpLength (lumpnum) == 0)
2458 {
2459 return false;
2460 }
2461 reader = Wads.ReopenLumpNumNewFile(lumpnum);
2462 if (reader == NULL)
2463 {
2464 return false;
2465 }
2466 }
2467 }
2468 else
2469 {
2470 // Load an external file.
2471 reader = new FileReader(musicname);
2472 }
2473
2474 // shutdown old music
2475 S_StopMusic (true);
2476
2477 // Just record it if volume is 0
2478 if (snd_musicvolume <= 0)
2479 {
2480 mus_playing.loop = looping;
2481 mus_playing.name = musicname;
2482 mus_playing.baseorder = order;
2483 LastSong = musicname;
2484 delete reader;
2485 return true;
2486 }
2487
2488 // load & register it
2489 if (handle != NULL)
2490 {
2491 mus_playing.handle = handle;
2492 delete reader;
2493 }
2494 else
2495 {
2496 mus_playing.handle = I_RegisterSong (reader, devp);
2497 }
2498 }
2499
2500 mus_playing.loop = looping;
2501 mus_playing.name = musicname;
2502 mus_playing.baseorder = 0;
2503 LastSong = "";
2504
2505 if (mus_playing.handle != 0)
2506 { // play it
2507 mus_playing.handle->Start(looping, S_GetMusicVolume (musicname), order);
2508 mus_playing.baseorder = order;
2509 return true;
2510 }
2511 return false;
2512 }
2513
2514 //==========================================================================
2515 //
2516 // S_RestartMusic
2517 //
2518 // Must only be called from snd_reset in i_sound.cpp!
2519 //==========================================================================
2520
S_RestartMusic()2521 void S_RestartMusic ()
2522 {
2523 if (!LastSong.IsEmpty())
2524 {
2525 FString song = LastSong;
2526 LastSong = "";
2527 S_ChangeMusic (song, mus_playing.baseorder, mus_playing.loop, true);
2528 }
2529 }
2530
2531 //==========================================================================
2532 //
2533 // S_MIDIDeviceChanged
2534 //
2535 //==========================================================================
2536
S_MIDIDeviceChanged()2537 void S_MIDIDeviceChanged()
2538 {
2539 if (mus_playing.handle != NULL && mus_playing.handle->IsMIDI())
2540 {
2541 mus_playing.handle->Stop();
2542 mus_playing.handle->Start(mus_playing.loop, -1, mus_playing.baseorder);
2543 }
2544 }
2545
2546 //==========================================================================
2547 //
2548 // S_GetMusic
2549 //
2550 //==========================================================================
2551
S_GetMusic(char ** name)2552 int S_GetMusic (char **name)
2553 {
2554 int order;
2555
2556 if (mus_playing.name)
2557 {
2558 *name = copystring (mus_playing.name);
2559 order = mus_playing.baseorder;
2560 }
2561 else
2562 {
2563 *name = NULL;
2564 order = 0;
2565 }
2566 return order;
2567 }
2568
2569 //==========================================================================
2570 //
2571 // S_StopMusic
2572 //
2573 //==========================================================================
2574
S_StopMusic(bool force)2575 void S_StopMusic (bool force)
2576 {
2577 // [RH] Don't stop if a playlist is active.
2578 if ((force || PlayList == NULL) && !mus_playing.name.IsEmpty())
2579 {
2580 if (mus_playing.handle != NULL)
2581 {
2582 if (MusicPaused)
2583 mus_playing.handle->Resume();
2584
2585 mus_playing.handle->Stop();
2586 delete mus_playing.handle;
2587 mus_playing.handle = NULL;
2588 }
2589 LastSong = mus_playing.name;
2590 mus_playing.name = "";
2591 }
2592 }
2593
2594 //==========================================================================
2595 //
2596 //
2597 //
2598 //==========================================================================
2599
S_UpdateMusic()2600 void S_UpdateMusic()
2601 {
2602 GSnd->UpdateMusic();
2603 }
2604
2605 //==========================================================================
2606 //
2607 // CCMD playsound
2608 //
2609 //==========================================================================
2610
CCMD(playsound)2611 CCMD (playsound)
2612 {
2613 if (argv.argc() > 1)
2614 {
2615 FSoundID id = argv[1];
2616 if (id == 0)
2617 {
2618 Printf("'%s' is not a sound\n", argv[1]);
2619 }
2620 else
2621 {
2622 S_Sound (CHAN_AUTO | CHAN_UI, id, 1.f, ATTN_NONE);
2623 }
2624 }
2625 }
2626
2627 //==========================================================================
2628 //
2629 // CCMD loopsound
2630 //
2631 //==========================================================================
2632
CCMD(loopsound)2633 CCMD (loopsound)
2634 {
2635 if (players[consoleplayer].mo != NULL && !netgame && argv.argc() > 1)
2636 {
2637 FSoundID id = argv[1];
2638 if (id == 0)
2639 {
2640 Printf("'%s' is not a sound\n", argv[1]);
2641 }
2642 else
2643 {
2644 AActor *icon = Spawn("SpeakerIcon", players[consoleplayer].mo->PosPlusZ(32*FRACUNIT), ALLOW_REPLACE);
2645 if (icon != NULL)
2646 {
2647 S_Sound(icon, CHAN_BODY | CHAN_LOOP, id, 1.f, ATTN_IDLE);
2648 }
2649 }
2650 }
2651 }
2652
2653 //==========================================================================
2654 //
2655 // CCMD idmus
2656 //
2657 //==========================================================================
2658
CCMD(idmus)2659 CCMD (idmus)
2660 {
2661 level_info_t *info;
2662 FString map;
2663 int l;
2664
2665 if (!nomusic)
2666 {
2667 if (argv.argc() > 1)
2668 {
2669 if (gameinfo.flags & GI_MAPxx)
2670 {
2671 l = atoi (argv[1]);
2672 if (l <= 99)
2673 {
2674 map = CalcMapName (0, l);
2675 }
2676 else
2677 {
2678 Printf ("%s\n", GStrings("STSTR_NOMUS"));
2679 return;
2680 }
2681 }
2682 else
2683 {
2684 map = CalcMapName (argv[1][0] - '0', argv[1][1] - '0');
2685 }
2686
2687 if ( (info = FindLevelInfo (map)) )
2688 {
2689 if (info->Music.IsNotEmpty())
2690 {
2691 S_ChangeMusic (info->Music, info->musicorder);
2692 Printf ("%s\n", GStrings("STSTR_MUS"));
2693 }
2694 }
2695 else
2696 {
2697 Printf ("%s\n", GStrings("STSTR_NOMUS"));
2698 }
2699 }
2700 }
2701 }
2702
2703 //==========================================================================
2704 //
2705 // CCMD changemus
2706 //
2707 //==========================================================================
2708
CCMD(changemus)2709 CCMD (changemus)
2710 {
2711 if (!nomusic)
2712 {
2713 if (argv.argc() > 1)
2714 {
2715 if (PlayList)
2716 {
2717 delete PlayList;
2718 PlayList = NULL;
2719 }
2720 S_ChangeMusic (argv[1], argv.argc() > 2 ? atoi (argv[2]) : 0);
2721 }
2722 else
2723 {
2724 const char *currentmus = mus_playing.name.GetChars();
2725 if(currentmus != NULL && *currentmus != 0)
2726 {
2727 Printf ("currently playing %s\n", currentmus);
2728 }
2729 else
2730 {
2731 Printf ("no music playing\n");
2732 }
2733 }
2734 }
2735 }
2736
2737 //==========================================================================
2738 //
2739 // CCMD stopmus
2740 //
2741 //==========================================================================
2742
CCMD(stopmus)2743 CCMD (stopmus)
2744 {
2745 if (PlayList)
2746 {
2747 delete PlayList;
2748 PlayList = NULL;
2749 }
2750 S_StopMusic (false);
2751 LastSong = ""; // forget the last played song so that it won't get restarted if some volume changes occur
2752 }
2753
2754 //==========================================================================
2755 //
2756 // CCMD cd_play
2757 //
2758 // Plays a specified track, or the entire CD if no track is specified.
2759 //==========================================================================
2760
CCMD(cd_play)2761 CCMD (cd_play)
2762 {
2763 char musname[16];
2764
2765 if (argv.argc() == 1)
2766 {
2767 strcpy (musname, ",CD,");
2768 }
2769 else
2770 {
2771 mysnprintf (musname, countof(musname), ",CD,%d", atoi(argv[1]));
2772 }
2773 S_ChangeMusic (musname, 0, true);
2774 }
2775
2776 //==========================================================================
2777 //
2778 // CCMD cd_stop
2779 //
2780 //==========================================================================
2781
CCMD(cd_stop)2782 CCMD (cd_stop)
2783 {
2784 CD_Stop ();
2785 }
2786
2787 //==========================================================================
2788 //
2789 // CCMD cd_eject
2790 //
2791 //==========================================================================
2792
CCMD(cd_eject)2793 CCMD (cd_eject)
2794 {
2795 CD_Eject ();
2796 }
2797
2798 //==========================================================================
2799 //
2800 // CCMD cd_close
2801 //
2802 //==========================================================================
2803
CCMD(cd_close)2804 CCMD (cd_close)
2805 {
2806 CD_UnEject ();
2807 }
2808
2809 //==========================================================================
2810 //
2811 // CCMD cd_pause
2812 //
2813 //==========================================================================
2814
CCMD(cd_pause)2815 CCMD (cd_pause)
2816 {
2817 CD_Pause ();
2818 }
2819
2820 //==========================================================================
2821 //
2822 // CCMD cd_resume
2823 //
2824 //==========================================================================
2825
CCMD(cd_resume)2826 CCMD (cd_resume)
2827 {
2828 CD_Resume ();
2829 }
2830
2831 //==========================================================================
2832 //
2833 // CCMD playlist
2834 //
2835 //==========================================================================
2836
CCMD(playlist)2837 CCMD (playlist)
2838 {
2839 int argc = argv.argc();
2840
2841 if (argc < 2 || argc > 3)
2842 {
2843 Printf ("playlist <playlist.m3u> [<position>|shuffle]\n");
2844 }
2845 else
2846 {
2847 if (PlayList != NULL)
2848 {
2849 PlayList->ChangeList (argv[1]);
2850 }
2851 else
2852 {
2853 PlayList = new FPlayList (argv[1]);
2854 }
2855 if (PlayList->GetNumSongs () == 0)
2856 {
2857 delete PlayList;
2858 PlayList = NULL;
2859 }
2860 else
2861 {
2862 if (argc == 3)
2863 {
2864 if (stricmp (argv[2], "shuffle") == 0)
2865 {
2866 PlayList->Shuffle ();
2867 }
2868 else
2869 {
2870 PlayList->SetPosition (atoi (argv[2]));
2871 }
2872 }
2873 S_ActivatePlayList (false);
2874 }
2875 }
2876 }
2877
2878 //==========================================================================
2879 //
2880 // CCMD playlistpos
2881 //
2882 //==========================================================================
2883
CheckForPlaylist()2884 static bool CheckForPlaylist ()
2885 {
2886 if (PlayList == NULL)
2887 {
2888 Printf ("No playlist is playing.\n");
2889 return false;
2890 }
2891 return true;
2892 }
2893
CCMD(playlistpos)2894 CCMD (playlistpos)
2895 {
2896 if (CheckForPlaylist() && argv.argc() > 1)
2897 {
2898 PlayList->SetPosition (atoi (argv[1]) - 1);
2899 S_ActivatePlayList (false);
2900 }
2901 }
2902
2903 //==========================================================================
2904 //
2905 // CCMD playlistnext
2906 //
2907 //==========================================================================
2908
CCMD(playlistnext)2909 CCMD (playlistnext)
2910 {
2911 if (CheckForPlaylist())
2912 {
2913 PlayList->Advance ();
2914 S_ActivatePlayList (false);
2915 }
2916 }
2917
2918 //==========================================================================
2919 //
2920 // CCMD playlistprev
2921 //
2922 //==========================================================================
2923
CCMD(playlistprev)2924 CCMD (playlistprev)
2925 {
2926 if (CheckForPlaylist())
2927 {
2928 PlayList->Backup ();
2929 S_ActivatePlayList (true);
2930 }
2931 }
2932
2933 //==========================================================================
2934 //
2935 // CCMD playliststatus
2936 //
2937 //==========================================================================
2938
CCMD(playliststatus)2939 CCMD (playliststatus)
2940 {
2941 if (CheckForPlaylist ())
2942 {
2943 Printf ("Song %d of %d:\n%s\n",
2944 PlayList->GetPosition () + 1,
2945 PlayList->GetNumSongs (),
2946 PlayList->GetSong (PlayList->GetPosition ()));
2947 }
2948 }
2949
2950 //==========================================================================
2951 //
2952 // CCMD cachesound <sound name>
2953 //
2954 //==========================================================================
2955
CCMD(cachesound)2956 CCMD (cachesound)
2957 {
2958 if (argv.argc() < 2)
2959 {
2960 Printf ("Usage: cachesound <sound> ...\n");
2961 return;
2962 }
2963 for (int i = 1; i < argv.argc(); ++i)
2964 {
2965 FSoundID sfxnum = argv[i];
2966 if (sfxnum != 0)
2967 {
2968 S_CacheSound (&S_sfx[sfxnum]);
2969 }
2970 }
2971 }
2972
2973
CCMD(listsoundchannels)2974 CCMD(listsoundchannels)
2975 {
2976 FSoundChan *chan;
2977 int count = 0;
2978 for (chan = Channels; chan != NULL; chan = chan->NextChan)
2979 {
2980 if (!(chan->ChanFlags & CHAN_EVICTED))
2981 {
2982 FVector3 chanorigin;
2983
2984 CalcPosVel(chan, &chanorigin, NULL);
2985
2986 Printf("%s at (%1.5f, %1.5f, %1.5f)\n", (const char*)chan->SoundID, chanorigin.X, chanorigin.Y, chanorigin.Z);
2987 count++;
2988 }
2989 }
2990 Printf("%d sounds playing\n", count);
2991 }
2992