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