mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-12 07:34:50 +00:00
c808041337
SVN r181 (trunk)
1980 lines
46 KiB
C++
1980 lines
46 KiB
C++
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id: s_sound.c,v 1.3 1998/01/05 16:26:08 pekangas Exp $
|
|
//
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
//
|
|
// This source is available for distribution and/or modification
|
|
// only under the terms of the DOOM Source Code License as
|
|
// published by id Software. All rights reserved.
|
|
//
|
|
// The source is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
|
// for more details.
|
|
//
|
|
//
|
|
// DESCRIPTION: none
|
|
// Dolby Pro Logic code by Gavino.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include "m_alloc.h"
|
|
|
|
#include "i_system.h"
|
|
#include "i_sound.h"
|
|
#include "i_music.h"
|
|
#include "i_cd.h"
|
|
#include "s_sound.h"
|
|
#include "s_sndseq.h"
|
|
#include "s_playlist.h"
|
|
#include "c_dispatch.h"
|
|
#include "m_random.h"
|
|
#include "w_wad.h"
|
|
#include "doomdef.h"
|
|
#include "p_local.h"
|
|
#include "doomstat.h"
|
|
#include "cmdlib.h"
|
|
#include "v_video.h"
|
|
#include "v_text.h"
|
|
#include "a_sharedglobal.h"
|
|
#include "gstrings.h"
|
|
#include "vectors.h"
|
|
#include "gi.h"
|
|
#include "templates.h"
|
|
#include "zstring.h"
|
|
|
|
// MACROS ------------------------------------------------------------------
|
|
|
|
#ifdef NeXT
|
|
// NeXT doesn't need a binary flag in open call
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
#ifndef O_BINARY
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
#define SELECT_ATTEN(a) ((a)==ATTN_NONE ? 0 : (a)==ATTN_SURROUND ? -1 : \
|
|
(a)==ATTN_STATIC ? 3 : 1)
|
|
#ifndef FIXED2FLOAT
|
|
#define FIXED2FLOAT(f) (((float)(f))/(float)65536)
|
|
#endif
|
|
|
|
#define PRIORITY_MAX_ADJUST 10
|
|
#define DIST_ADJUST (MAX_SND_DIST/PRIORITY_MAX_ADJUST)
|
|
|
|
#define NORM_PITCH 128
|
|
#define NORM_PRIORITY 64
|
|
#define NORM_SEP 128
|
|
#define NONE_SEP -2
|
|
|
|
#define S_PITCH_PERTURB 1
|
|
#define S_STEREO_SWING (96<<FRACBITS)
|
|
|
|
/* Sound curve parameters for Doom */
|
|
|
|
// Maximum sound distance
|
|
const int S_CLIPPING_DIST = 1200;
|
|
|
|
// Sounds closer than this to the listener are maxed out.
|
|
// Originally: 200.
|
|
const int S_CLOSE_DIST = 160;
|
|
|
|
const int S_ATTENUATOR = S_CLIPPING_DIST - S_CLOSE_DIST;
|
|
|
|
// TYPES -------------------------------------------------------------------
|
|
|
|
typedef struct
|
|
{
|
|
AActor *mover; // Used for velocity
|
|
fixed_t *pt; // origin of sound
|
|
fixed_t x,y,z; // Origin if pt is NULL
|
|
sfxinfo_t* sfxinfo; // sound information (if null, channel avail.)
|
|
long handle; // handle of the sound being played
|
|
int sound_id; // sound id of playing sound
|
|
int org_id; // sound id of sound used to start this channel
|
|
int entchannel; // entity's sound channel
|
|
int basepriority;
|
|
float attenuation;
|
|
float volume;
|
|
int pitch;
|
|
int priority;
|
|
bool loop;
|
|
bool is3d;
|
|
bool constz;
|
|
int time; // time when sound started playing
|
|
} channel_t;
|
|
|
|
struct MusPlayingInfo
|
|
{
|
|
FString name;
|
|
void *handle;
|
|
int baseorder;
|
|
bool loop;
|
|
};
|
|
|
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
|
|
|
extern float S_GetMusicVolume (const char *music);
|
|
|
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
|
|
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
|
|
static fixed_t P_AproxDistance2 (fixed_t *listener, fixed_t x, fixed_t y);
|
|
static void S_StopChannel (int cnum);
|
|
static void S_StartSound (fixed_t *pt, AActor *mover, int channel,
|
|
int sound_id, float volume, float attenuation, BOOL looping);
|
|
static void S_ActivatePlayList (bool goBack);
|
|
static void CalcPosVel (fixed_t *pt, AActor *mover, int constz, float pos[3],
|
|
float vel[3]);
|
|
|
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
|
|
|
int MAX_SND_DIST;
|
|
static channel_t *Channel; // the set of channels available
|
|
static bool SoundPaused; // whether sound effects are paused
|
|
static bool MusicPaused; // whether music is paused
|
|
static MusPlayingInfo mus_playing; // music currently being played
|
|
static FString LastSong; // last music that was played
|
|
static byte *SoundCurve;
|
|
static int nextcleanup;
|
|
static FPlayList *PlayList;
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
// [RH] Hacks for pitch variance
|
|
int sfx_sawup, sfx_sawidl, sfx_sawful, sfx_sawhit;
|
|
int sfx_itemup, sfx_tink;
|
|
|
|
int sfx_empty;
|
|
|
|
int numChannels;
|
|
|
|
CVAR (Bool, snd_surround, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // [RH] Use surround sounds?
|
|
FBoolCVar noisedebug ("noise", false, 0); // [RH] Print sound debugging info?
|
|
CVAR (Int, snd_channels, 12, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // number of channels available
|
|
CVAR (Bool, snd_matrix, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
CVAR (Bool, snd_flipstereo, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
// CODE --------------------------------------------------------------------
|
|
|
|
//==========================================================================
|
|
//
|
|
// P_AproxDistance2
|
|
//
|
|
//==========================================================================
|
|
|
|
static fixed_t P_AproxDistance2 (fixed_t *listener, fixed_t x, fixed_t y)
|
|
{
|
|
// calculate the distance to sound origin
|
|
// and clip it if necessary
|
|
if (listener)
|
|
{
|
|
fixed_t adx = abs (listener[0] - x);
|
|
fixed_t ady = abs (listener[1] - y);
|
|
// From _GG1_ p.428. Appox. eucledian distance fast.
|
|
return adx + ady - ((adx < ady ? adx : ady)>>1);
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static fixed_t P_AproxDistance2 (AActor *listener, fixed_t x, fixed_t y)
|
|
{
|
|
return listener ? P_AproxDistance2 (&listener->x, x, y) : 0;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_NoiseDebug
|
|
//
|
|
// [RH] Print sound debug info. Called by status bar.
|
|
//==========================================================================
|
|
|
|
void S_NoiseDebug (void)
|
|
{
|
|
fixed_t ox, oy;
|
|
int i, y, color;
|
|
|
|
y = 32 * CleanYfac;
|
|
screen->DrawText (CR_YELLOW, 0, y, "*** SOUND DEBUG INFO ***", TAG_DONE);
|
|
y += 8;
|
|
|
|
screen->DrawText (CR_GOLD, 0, y, "name", TAG_DONE);
|
|
screen->DrawText (CR_GOLD, 70, y, "x", TAG_DONE);
|
|
screen->DrawText (CR_GOLD, 120, y, "y", TAG_DONE);
|
|
screen->DrawText (CR_GOLD, 170, y, "vol", TAG_DONE);
|
|
screen->DrawText (CR_GOLD, 200, y, "pri", TAG_DONE);
|
|
screen->DrawText (CR_GOLD, 240, y, "dist", TAG_DONE);
|
|
screen->DrawText (CR_GOLD, 280, y, "chan", TAG_DONE);
|
|
y += 8;
|
|
|
|
for (i = 0; i < numChannels && y < SCREENHEIGHT - 16; i++, y += 8)
|
|
{
|
|
if (Channel[i].sfxinfo)
|
|
{
|
|
char temp[16];
|
|
fixed_t *origin = Channel[i].pt;
|
|
|
|
if (Channel[i].attenuation <= 0)
|
|
{
|
|
ox = players[consoleplayer].camera->x;
|
|
oy = players[consoleplayer].camera->y;
|
|
}
|
|
else if (origin)
|
|
{
|
|
ox = origin[0];
|
|
oy = origin[1];
|
|
}
|
|
else
|
|
{
|
|
ox = Channel[i].x;
|
|
oy = Channel[i].y;
|
|
}
|
|
color = Channel[i].loop ? CR_BROWN : CR_GREY;
|
|
Wads.GetLumpName (temp, Channel[i].sfxinfo->lumpnum);
|
|
temp[8] = 0;
|
|
screen->DrawText (color, 0, y, temp, TAG_DONE);
|
|
sprintf (temp, "%ld", ox / FRACUNIT);
|
|
screen->DrawText (color, 70, y, temp, TAG_DONE);
|
|
sprintf (temp, "%ld", oy / FRACUNIT);
|
|
screen->DrawText (color, 120, y, temp, TAG_DONE);
|
|
sprintf (temp, "%g", Channel[i].volume);
|
|
screen->DrawText (color, 170, y, temp, TAG_DONE);
|
|
sprintf (temp, "%d", Channel[i].priority);
|
|
screen->DrawText (color, 200, y, temp, TAG_DONE);
|
|
sprintf (temp, "%ld", P_AproxDistance2 (players[consoleplayer].camera, ox, oy) / FRACUNIT);
|
|
screen->DrawText (color, 240, y, temp, TAG_DONE);
|
|
sprintf (temp, "%d", Channel[i].entchannel);
|
|
screen->DrawText (color, 280, y, temp, TAG_DONE);
|
|
}
|
|
else
|
|
{
|
|
screen->DrawText (CR_GREY, 0, y, "------", TAG_DONE);
|
|
}
|
|
}
|
|
BorderNeedRefresh = screen->GetPageCount ();
|
|
}
|
|
|
|
static FString LastLocalSndInfo;
|
|
static FString LastLocalSndSeq;
|
|
void S_AddLocalSndInfo(int lump);
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_Init
|
|
//
|
|
// Initializes sound stuff, including volume. Sets channels, SFX and
|
|
// music volume, allocates channel buffer, and sets S_sfx lookup.
|
|
//==========================================================================
|
|
|
|
void S_Init ()
|
|
{
|
|
static bool termdone=false;
|
|
int i;
|
|
int curvelump;
|
|
|
|
Printf ("S_Init\n");
|
|
if (!termdone)
|
|
{
|
|
termdone=true;
|
|
atterm (S_Shutdown);
|
|
}
|
|
|
|
// remove old data (S_Init can be called multiple times!)
|
|
LastLocalSndInfo = LastLocalSndSeq = "";
|
|
if (SoundCurve) delete [] SoundCurve;
|
|
|
|
// Heretic and Hexen have sound curve lookup tables. Doom does not.
|
|
curvelump = Wads.CheckNumForName ("SNDCURVE");
|
|
if (curvelump >= 0)
|
|
{
|
|
MAX_SND_DIST = Wads.LumpLength (curvelump);
|
|
SoundCurve = new BYTE[MAX_SND_DIST];
|
|
Wads.ReadLump (curvelump, SoundCurve);
|
|
|
|
// The maximum value in a SNDCURVE lump is 127, so scale it to
|
|
// fit our sound system's volume levels.
|
|
for (i = 0; i < S_CLIPPING_DIST; ++i)
|
|
{
|
|
SoundCurve[i] = SoundCurve[i] * 255 / 127;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MAX_SND_DIST = S_CLIPPING_DIST;
|
|
SoundCurve = new BYTE[S_CLIPPING_DIST];
|
|
for (i = 0; i < S_CLIPPING_DIST; ++i)
|
|
{
|
|
SoundCurve[i] = MIN (255, (S_CLIPPING_DIST - i) * 255 / S_ATTENUATOR);
|
|
}
|
|
}
|
|
|
|
// [RH] Read in sound sequences
|
|
S_ParseSndSeq (-1);
|
|
|
|
// Allocating the virtual channels
|
|
numChannels = GSnd ? GSnd->SetChannels (snd_channels) : 0;
|
|
if (Channel != NULL)
|
|
{
|
|
delete[] Channel;
|
|
}
|
|
Channel = new channel_t[numChannels];
|
|
|
|
// Free all channels for use
|
|
for (i = 0; i < numChannels; i++)
|
|
{
|
|
Channel[i].pt = NULL;
|
|
Channel[i].handle = 0;
|
|
Channel[i].sound_id = -1;
|
|
Channel[i].priority = 0;
|
|
Channel[i].sfxinfo = 0;
|
|
}
|
|
|
|
// no sounds are playing, and they are not paused
|
|
MusicPaused = false;
|
|
SoundPaused = false;
|
|
|
|
// Note that sounds have not been cached (yet).
|
|
// for (i=1; (size_t)i < S_sfx.Size (); i++)
|
|
// S_sfx[i].usefulness = -1;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_Shutdown
|
|
//
|
|
//==========================================================================
|
|
|
|
void S_Shutdown ()
|
|
{
|
|
if (Channel != NULL)
|
|
{
|
|
delete[] Channel;
|
|
Channel = NULL;
|
|
numChannels = 0;
|
|
}
|
|
if (SoundCurve != NULL)
|
|
{
|
|
delete[] SoundCurve;
|
|
SoundCurve = NULL;
|
|
}
|
|
if (PlayList != NULL)
|
|
{
|
|
delete PlayList;
|
|
PlayList = NULL;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_Start
|
|
//
|
|
// Per level startup code. Kills playing sounds at start of level
|
|
// and starts new music.
|
|
//==========================================================================
|
|
|
|
void S_Start ()
|
|
{
|
|
int cnum;
|
|
|
|
if (GSnd)
|
|
{
|
|
// kill all playing sounds at start of level (trust me - a good idea)
|
|
for (cnum = 0; cnum < numChannels; cnum++)
|
|
{
|
|
if (Channel[cnum].sfxinfo)
|
|
{
|
|
S_StopChannel (cnum);
|
|
}
|
|
}
|
|
|
|
// Check for local sound definitions. Only reload if they differ
|
|
// from the previous ones.
|
|
|
|
// To be certain better check whether level is valid!
|
|
char * LocalSndInfo= level.info? level.info->soundinfo : (char*)"";
|
|
char * LocalSndSeq = level.info? level.info->sndseq : (char*)"";
|
|
bool parse_ss=false;
|
|
|
|
// This level uses a different local SNDINFO
|
|
if (LastLocalSndInfo.CompareNoCase(LocalSndInfo) != 0 || !level.info)
|
|
{
|
|
// First delete the old sound list
|
|
for(unsigned i=1;i<S_sfx.Size();i++)
|
|
{
|
|
GSnd->UnloadSound(&S_sfx[i]);
|
|
}
|
|
|
|
// Parse the global SNDINFO
|
|
S_ParseSndInfo();
|
|
|
|
if (*LocalSndInfo)
|
|
{
|
|
// Now parse the local SNDINFO
|
|
int j = Wads.CheckNumForName(LocalSndInfo);
|
|
if (j>=0) S_AddLocalSndInfo(j);
|
|
}
|
|
|
|
// Also reload the SNDSEQ if the SNDINFO was replaced!
|
|
parse_ss=true;
|
|
}
|
|
else if (LastLocalSndSeq.CompareNoCase(LocalSndSeq) != 0)
|
|
{
|
|
parse_ss=true;
|
|
}
|
|
if (parse_ss)
|
|
{
|
|
S_ParseSndSeq(*LocalSndSeq? Wads.CheckNumForName(LocalSndSeq) : -1);
|
|
}
|
|
else
|
|
|
|
LastLocalSndInfo = LocalSndInfo;
|
|
LastLocalSndSeq = LocalSndSeq;
|
|
}
|
|
|
|
// start new music for the level
|
|
MusicPaused = false;
|
|
SoundPaused = false;
|
|
|
|
// [RH] This is a lot simpler now.
|
|
if (!savegamerestore)
|
|
{
|
|
if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid))
|
|
S_ChangeMusic (level.music, level.musicorder);
|
|
}
|
|
|
|
nextcleanup = 15;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_PrecacheLevel
|
|
//
|
|
// Like R_PrecacheLevel, but for sounds.
|
|
//
|
|
//==========================================================================
|
|
|
|
void S_PrecacheLevel ()
|
|
{
|
|
unsigned int i;
|
|
|
|
if (GSnd)
|
|
{
|
|
for (i = 0; i < S_sfx.Size(); ++i)
|
|
{
|
|
S_sfx[i].bUsed = false;
|
|
}
|
|
|
|
AActor *actor;
|
|
TThinkerIterator<AActor> iterator;
|
|
|
|
while ( (actor = iterator.Next ()) != NULL )
|
|
{
|
|
S_sfx[actor->SeeSound].bUsed = true;
|
|
S_sfx[actor->AttackSound].bUsed = true;
|
|
S_sfx[actor->PainSound].bUsed = true;
|
|
S_sfx[actor->DeathSound].bUsed = true;
|
|
S_sfx[actor->ActiveSound].bUsed = true;
|
|
S_sfx[actor->UseSound].bUsed = true;
|
|
}
|
|
|
|
for (i = 1; i < S_sfx.Size(); ++i)
|
|
{
|
|
if (S_sfx[i].bUsed)
|
|
{
|
|
S_CacheSound (&S_sfx[i]);
|
|
}
|
|
}
|
|
for (i = 1; i < S_sfx.Size(); ++i)
|
|
{
|
|
if (!S_sfx[i].bUsed && S_sfx[i].link == sfxinfo_t::NO_LINK)
|
|
{
|
|
GSnd->UnloadSound (&S_sfx[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_CacheSound
|
|
//
|
|
//==========================================================================
|
|
|
|
void S_CacheSound (sfxinfo_t *sfx)
|
|
{
|
|
if (GSnd)
|
|
{
|
|
if (sfx->bPlayerReserve)
|
|
{
|
|
return;
|
|
}
|
|
else if (sfx->bRandomHeader)
|
|
{
|
|
S_CacheRandomSound (sfx);
|
|
}
|
|
else
|
|
{
|
|
while (sfx->link != sfxinfo_t::NO_LINK)
|
|
{
|
|
sfx = &S_sfx[sfx->link];
|
|
}
|
|
sfx->bUsed = true;
|
|
GSnd->LoadSound (sfx);
|
|
}
|
|
}
|
|
}
|
|
|
|
// [RH] Split S_StartSoundAtVolume into multiple parts so that sounds can
|
|
// be specified both by id and by name. Also borrowed some stuff from
|
|
// Hexen and parameters from Quake.
|
|
|
|
//==========================================================================
|
|
//
|
|
// CalcPosVel
|
|
//
|
|
// Calculates a sounds position and velocity for 3D sounds.
|
|
//=========================================================================
|
|
|
|
void CalcPosVel (fixed_t *pt, AActor *mover, int constz,
|
|
float pos[3], float vel[3])
|
|
{
|
|
if (mover != NULL && 0)
|
|
{
|
|
vel[0] = FIXED2FLOAT(mover->momx) * TICRATE;
|
|
vel[1] = FIXED2FLOAT(mover->momz) * TICRATE;
|
|
vel[2] = FIXED2FLOAT(mover->momy) * TICRATE;
|
|
}
|
|
else
|
|
{
|
|
vel[0] = vel[1] = vel[2] = 0.f;
|
|
}
|
|
|
|
pos[0] = FIXED2FLOAT (pt[0]);
|
|
pos[2] = FIXED2FLOAT (pt[1]);
|
|
if (constz)
|
|
{
|
|
pos[1] = FIXED2FLOAT(players[consoleplayer].camera->z);
|
|
vel[1] = 0.f;
|
|
}
|
|
else
|
|
{
|
|
pos[1] = FIXED2FLOAT(pt[2]);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_StartSound
|
|
//
|
|
// 0 attenuation means full volume over whole level
|
|
// -1 attenuation means full volume in surround over whole level
|
|
// 0<attenuation<=1 means to scale the distance by that amount when
|
|
// calculating volume
|
|
//==========================================================================
|
|
|
|
static void S_StartSound (fixed_t *pt, AActor *mover, int channel,
|
|
int sound_id, float volume, float attenuation, bool looping)
|
|
{
|
|
sfxinfo_t *sfx;
|
|
int dist, vol;
|
|
int i;
|
|
int chanflags;
|
|
int basepriority, priority;
|
|
int sep;
|
|
int org_id;
|
|
fixed_t x, y, z;
|
|
angle_t angle;
|
|
|
|
static int sndcount = 0;
|
|
int chan;
|
|
|
|
if (sound_id <= 0 || volume <= 0 || GSnd == NULL)
|
|
return;
|
|
|
|
org_id = sound_id;
|
|
|
|
if (pt == NULL)
|
|
{
|
|
if (attenuation > 0)
|
|
attenuation = 0;
|
|
// Give these variables values, although they don't really matter
|
|
x = y = z = 0;
|
|
}
|
|
else
|
|
{
|
|
x = pt[0];
|
|
y = pt[1];
|
|
z = pt[2];
|
|
}
|
|
|
|
chanflags = channel & ~7;
|
|
if (chanflags & CHAN_IMMOBILE)
|
|
{
|
|
pt = NULL;
|
|
}
|
|
if (i_compatflags & COMPATF_MAGICSILENCE)
|
|
{ // For people who just can't play without a silent BFG.
|
|
channel = 1;
|
|
}
|
|
else
|
|
{
|
|
if ((channel & CHAN_MAYBE_LOCAL) && (i_compatflags & COMPATF_SILENTPICKUP))
|
|
{
|
|
if (mover != 0 && mover != players[consoleplayer].camera)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
channel &= 7;
|
|
}
|
|
if (volume > 1)
|
|
volume = 1;
|
|
|
|
if (attenuation == 0)
|
|
{
|
|
sep = NONE_SEP;
|
|
dist = 0;
|
|
}
|
|
else if (attenuation < 0)
|
|
{
|
|
sep = snd_surround ? -1 : NONE_SEP;
|
|
dist = 0;
|
|
}
|
|
else
|
|
{
|
|
// calculate the distance before other stuff so we can throw out
|
|
// sounds that are beyond the hearing range.
|
|
dist = (int)(FIXED2FLOAT
|
|
(P_AproxDistance2 (players[consoleplayer].camera, x, y))
|
|
* attenuation);
|
|
if (dist >= MAX_SND_DIST)
|
|
{
|
|
return; // sound is beyond the hearing range...
|
|
}
|
|
else if (dist < 0)
|
|
{
|
|
dist = 0;
|
|
}
|
|
sep = -3; // Calculate separation later on
|
|
}
|
|
|
|
sfx = &S_sfx[sound_id];
|
|
|
|
// Resolve player sounds, random sounds, and aliases
|
|
while (sfx->link != sfxinfo_t::NO_LINK)
|
|
{
|
|
if (sfx->bPlayerReserve)
|
|
{
|
|
sound_id = S_FindSkinnedSound (mover, sound_id);
|
|
}
|
|
else if (sfx->bRandomHeader)
|
|
{
|
|
sound_id = S_PickReplacement (sound_id);
|
|
}
|
|
else
|
|
{
|
|
sound_id = sfx->link;
|
|
}
|
|
sfx = &S_sfx[sound_id];
|
|
}
|
|
|
|
if (!sfx->data)
|
|
{
|
|
GSnd->LoadSound (sfx);
|
|
if (sfx->link != sfxinfo_t::NO_LINK)
|
|
{
|
|
sfx = &S_sfx[sfx->link];
|
|
}
|
|
}
|
|
|
|
if (sfx->lumpnum == sfx_empty)
|
|
{
|
|
basepriority = -1000;
|
|
return;
|
|
}
|
|
else if (attenuation <= 0)
|
|
{
|
|
basepriority = 200;
|
|
}
|
|
else
|
|
{
|
|
switch (channel)
|
|
{
|
|
case CHAN_WEAPON:
|
|
basepriority = 100;
|
|
break;
|
|
case CHAN_VOICE:
|
|
basepriority = 75;
|
|
break;
|
|
default:
|
|
case CHAN_BODY:
|
|
basepriority = 50;
|
|
break;
|
|
case CHAN_ITEM:
|
|
basepriority = 25;
|
|
break;
|
|
}
|
|
if (attenuation == 1)
|
|
basepriority += 50;
|
|
if (dist == 0)
|
|
basepriority += 30;
|
|
}
|
|
priority = basepriority * (PRIORITY_MAX_ADJUST - (dist/DIST_ADJUST));
|
|
|
|
if (pt != NULL && !S_StopSoundID (sound_id, priority, sfx->MaxChannels, sfx->bSingular, x, y))
|
|
return; // other sounds have greater priority
|
|
|
|
if (pt)
|
|
{
|
|
for (i = 0; i < numChannels; i++)
|
|
{
|
|
if (Channel[i].sfxinfo &&
|
|
pt == Channel[i].pt &&
|
|
Channel[i].entchannel == channel)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i < numChannels && channel != CHAN_AUTO)
|
|
{
|
|
S_StopChannel (i);
|
|
}
|
|
else if (channel == CHAN_AUTO)
|
|
{
|
|
int chansused[8], freechan;
|
|
|
|
memset (chansused, 0, sizeof(chansused));
|
|
freechan = numChannels;
|
|
|
|
for (i = 0; i < numChannels; i++)
|
|
{
|
|
if (!Channel[i].sfxinfo)
|
|
{
|
|
freechan = i;
|
|
}
|
|
else if (pt == Channel[i].pt)
|
|
{
|
|
chansused[Channel[i].entchannel] = 1;
|
|
}
|
|
}
|
|
for (i = 7; i >= 0; i--)
|
|
{
|
|
if (!chansused[i])
|
|
break;
|
|
}
|
|
if (i < 0)
|
|
return; // entity has no free channels
|
|
channel = i;
|
|
i = freechan;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i = numChannels;
|
|
}
|
|
|
|
if (i >= numChannels)
|
|
{
|
|
for (i = 0; i < numChannels; i++)
|
|
{
|
|
if (Channel[i].sfxinfo == NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i >= numChannels)
|
|
{
|
|
// look for a lower priority sound to replace.
|
|
sndcount++;
|
|
if (sndcount >= numChannels)
|
|
sndcount = 0;
|
|
for (chan = 0; chan < numChannels; chan++)
|
|
{
|
|
i = (sndcount + chan) % numChannels;
|
|
if (priority > Channel[i].priority)
|
|
{
|
|
chan = -1; //denote that sound should be replaced.
|
|
break;
|
|
}
|
|
}
|
|
if (chan != -1)
|
|
{
|
|
return; // no free channels.
|
|
}
|
|
else
|
|
{
|
|
// replace the lower priority sound.
|
|
S_StopChannel (i);
|
|
}
|
|
}
|
|
}
|
|
|
|
vol = (int)(SoundCurve[dist]*volume);
|
|
if (sep == -3)
|
|
{
|
|
AActor *listener = players[consoleplayer].camera;
|
|
if (listener == NULL)
|
|
{
|
|
sep = NONE_SEP;
|
|
}
|
|
else if (dist == 0)
|
|
{
|
|
sep = NORM_SEP;
|
|
}
|
|
else
|
|
{
|
|
angle = R_PointToAngle2 (listener->x, listener->y, x, y);
|
|
if (angle > listener->angle)
|
|
angle = angle - listener->angle;
|
|
else
|
|
angle = angle + (ANGLE_MAX - listener->angle);
|
|
angle >>= ANGLETOFINESHIFT;
|
|
sep = NORM_SEP - (FixedMul (S_STEREO_SWING, finesine[angle])>>FRACBITS);
|
|
if (snd_flipstereo)
|
|
{
|
|
sep = 255-sep;
|
|
}
|
|
if (snd_matrix)
|
|
{
|
|
int rearsep = NORM_SEP -
|
|
(FixedMul (S_STEREO_SWING, finecosine[angle])>>FRACBITS);
|
|
if (rearsep > 128)
|
|
{
|
|
sep = -1;
|
|
attenuation = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// vary the sfx pitches
|
|
if (sfx->PitchMask != 0)
|
|
{
|
|
Channel[i].pitch = NORM_PITCH-(M_Random()&sfx->PitchMask)+(M_Random()&sfx->PitchMask);
|
|
}
|
|
else
|
|
{
|
|
Channel[i].pitch = NORM_PITCH;
|
|
}
|
|
|
|
if (GSnd->Sound3D && attenuation > 0)
|
|
{
|
|
float pos[3];
|
|
float vel[3];
|
|
|
|
if (pt)
|
|
{
|
|
CalcPosVel (pt, mover, chanflags & CHAN_LISTENERZ, pos, vel);
|
|
}
|
|
else
|
|
{
|
|
fixed_t pt2[3];
|
|
pt2[0] = x;
|
|
pt2[1] = y;
|
|
pt2[2] = z;
|
|
CalcPosVel (pt2, mover, chanflags & CHAN_LISTENERZ, pos, vel);
|
|
}
|
|
Channel[i].handle = GSnd->StartSound3D (sfx, volume, Channel[i].pitch, i, looping, pos, vel, !(chanflags & CHAN_NOPAUSE));
|
|
Channel[i].is3d = true;
|
|
Channel[i].constz = !!(chanflags & CHAN_LISTENERZ);
|
|
}
|
|
else
|
|
{
|
|
Channel[i].handle = GSnd->StartSound (sfx, vol, sep, Channel[i].pitch, i, looping, !(chanflags & CHAN_NOPAUSE));
|
|
Channel[i].is3d = false;
|
|
Channel[i].constz = true;
|
|
}
|
|
Channel[i].sound_id = sound_id;
|
|
Channel[i].org_id = org_id;
|
|
Channel[i].mover = mover;
|
|
Channel[i].pt = pt ? pt : &Channel[i].x;
|
|
Channel[i].sfxinfo = sfx;
|
|
Channel[i].priority = priority;
|
|
Channel[i].basepriority = basepriority;
|
|
Channel[i].entchannel = channel;
|
|
Channel[i].attenuation = attenuation;
|
|
Channel[i].volume = volume;
|
|
Channel[i].x = x;
|
|
Channel[i].y = y;
|
|
Channel[i].z = z;
|
|
Channel[i].loop = looping ? true : false;
|
|
Channel[i].time = gametic;
|
|
|
|
// if (sfx->usefulness < 0)
|
|
// sfx->usefulness = 1;
|
|
// else
|
|
// sfx->usefulness++;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_SoundID
|
|
//
|
|
//==========================================================================
|
|
|
|
void S_SoundID (int channel, int sound_id, float volume, int attenuation)
|
|
{
|
|
S_StartSound ((fixed_t *)NULL, NULL, channel, sound_id, volume, SELECT_ATTEN(attenuation), false);
|
|
}
|
|
|
|
void S_SoundID (AActor *ent, int channel, int sound_id, float volume, int attenuation)
|
|
{
|
|
if (ent->Sector->MoreFlags & SECF_SILENT)
|
|
return;
|
|
S_StartSound (&ent->x, ent, channel, sound_id, volume, SELECT_ATTEN(attenuation), false);
|
|
}
|
|
|
|
void S_SoundID (fixed_t *pt, int channel, int sound_id, float volume, int attenuation)
|
|
{
|
|
S_StartSound (pt, NULL, channel, sound_id, volume, SELECT_ATTEN(attenuation), false);
|
|
}
|
|
|
|
void S_SoundID (fixed_t x, fixed_t y, fixed_t z, int channel, int sound_id, float volume, int attenuation)
|
|
{
|
|
fixed_t pt[3];
|
|
pt[0] = x;
|
|
pt[1] = y;
|
|
pt[2] = z;
|
|
S_StartSound (pt, NULL, channel|CHAN_IMMOBILE, sound_id, volume, SELECT_ATTEN(attenuation), false);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_LoopedSoundID
|
|
//
|
|
//==========================================================================
|
|
|
|
void S_LoopedSoundID (AActor *ent, int channel, int sound_id, float volume, int attenuation)
|
|
{
|
|
if (ent->Sector->MoreFlags & SECF_SILENT)
|
|
return;
|
|
S_StartSound (&ent->x, ent, channel, sound_id, volume, SELECT_ATTEN(attenuation), true);
|
|
}
|
|
|
|
void S_LoopedSoundID (fixed_t *pt, int channel, int sound_id, float volume, int attenuation)
|
|
{
|
|
S_StartSound (pt, NULL, channel, sound_id, volume, SELECT_ATTEN(attenuation), true);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_StartNamedSound
|
|
//
|
|
//==========================================================================
|
|
|
|
void S_StartNamedSound (AActor *ent, fixed_t *pt, int channel,
|
|
const char *name, float volume, float attenuation, bool looping)
|
|
{
|
|
int sfx_id;
|
|
|
|
if (name == NULL ||
|
|
(ent != NULL && ent->Sector->MoreFlags & SECF_SILENT))
|
|
{
|
|
return;
|
|
}
|
|
|
|
sfx_id = S_FindSound (name);
|
|
if (sfx_id == 0)
|
|
DPrintf ("Unknown sound %s\n", name);
|
|
|
|
if (ent)
|
|
S_StartSound (&ent->x, ent, channel, sfx_id, volume, attenuation, looping);
|
|
else
|
|
S_StartSound (pt, NULL, channel, sfx_id, volume, attenuation, looping);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_Sound
|
|
//
|
|
//==========================================================================
|
|
|
|
void S_Sound (int channel, const char *name, float volume, int attenuation)
|
|
{
|
|
S_StartNamedSound ((AActor *)NULL, NULL, channel, name, volume, SELECT_ATTEN(attenuation), false);
|
|
}
|
|
|
|
void S_Sound (AActor *ent, int channel, const char *name, float volume, int attenuation)
|
|
{
|
|
S_StartNamedSound (ent, NULL, channel, name, volume, SELECT_ATTEN(attenuation), false);
|
|
}
|
|
|
|
void S_Sound (fixed_t *pt, int channel, const char *name, float volume, int attenuation)
|
|
{
|
|
S_StartNamedSound (NULL, pt, channel, name, volume, SELECT_ATTEN(attenuation), false);
|
|
}
|
|
|
|
void S_Sound (fixed_t x, fixed_t y, int channel, const char *name, float volume, int attenuation)
|
|
{
|
|
fixed_t pt[3];
|
|
pt[0] = x;
|
|
pt[1] = y;
|
|
S_StartNamedSound (NULL, pt, channel|CHAN_LISTENERZ|CHAN_IMMOBILE,
|
|
name, volume, SELECT_ATTEN(attenuation), false);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_LoopedSound
|
|
//
|
|
//==========================================================================
|
|
|
|
void S_LoopedSound (AActor *ent, int channel, const char *name, float volume, int attenuation)
|
|
{
|
|
S_StartNamedSound (ent, NULL, channel, name, volume, SELECT_ATTEN(attenuation), true);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_StopSoundID
|
|
//
|
|
// from Hexen (albeit, modified a bit)
|
|
// Stops more than <limit> copies of the sound from playing at once.
|
|
//==========================================================================
|
|
|
|
BOOL S_StopSoundID (int sound_id, int priority, int limit, bool singular, fixed_t x, fixed_t y)
|
|
{
|
|
int i;
|
|
int lp; //least priority
|
|
int found;
|
|
|
|
if (limit <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
lp = -1;
|
|
found = 0;
|
|
for (i = 0; i < numChannels; i++)
|
|
{
|
|
if (Channel[i].sound_id == sound_id)
|
|
{
|
|
if (singular)
|
|
{
|
|
// This sound is already playing, so don't start it again.
|
|
return false;
|
|
}
|
|
if (Channel[i].sfxinfo &&
|
|
P_AproxDistance (Channel[i].pt[0] - x, Channel[i].pt[1] - y) < 256*FRACUNIT)
|
|
{
|
|
found++; //found one. Now, should we replace it??
|
|
if (priority > Channel[i].priority)
|
|
{ // if we're gonna kill one, then this'll be it
|
|
lp = i;
|
|
priority = Channel[i].priority;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (found < limit)
|
|
{
|
|
return true;
|
|
}
|
|
if (lp == -1)
|
|
{
|
|
return false; // don't replace any sounds
|
|
}
|
|
if (Channel[lp].handle)
|
|
{
|
|
S_StopChannel (lp);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_StopSound
|
|
//
|
|
// Stops a sound from a single source playing on a specific channel.
|
|
//==========================================================================
|
|
|
|
void S_StopSound (fixed_t *pt, int channel)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < numChannels; ++i)
|
|
{
|
|
if (Channel[i].sfxinfo &&
|
|
((pt == NULL && Channel[i].pt == &Channel[i].x) || Channel[i].pt == pt) &&
|
|
((i_compatflags & COMPATF_MAGICSILENCE) || Channel[i].entchannel == channel))
|
|
{
|
|
S_StopChannel (i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void S_StopSound (AActor *ent, int channel)
|
|
{
|
|
S_StopSound (&ent->x, channel);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_StopAllChannels
|
|
//
|
|
//==========================================================================
|
|
|
|
void S_StopAllChannels ()
|
|
{
|
|
SN_StopAllSequences ();
|
|
for (int i = 0; i < numChannels; i++)
|
|
if (Channel[i].sfxinfo)
|
|
S_StopChannel (i);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_RelinkSound
|
|
//
|
|
// Moves all the sounds from one thing to another. If the destination is
|
|
// NULL, then the sound becomes a positioned sound.
|
|
//==========================================================================
|
|
|
|
void S_RelinkSound (AActor *from, AActor *to)
|
|
{
|
|
int i;
|
|
|
|
if (!from)
|
|
return;
|
|
|
|
fixed_t *frompt = &from->x;
|
|
fixed_t *topt = to ? &to->x : NULL;
|
|
|
|
for (i = 0; i < numChannels; i++)
|
|
{
|
|
if (Channel[i].pt == frompt)
|
|
{
|
|
if (to != NULL || !Channel[i].loop)
|
|
{
|
|
Channel[i].pt = topt ? topt : &Channel[i].x;
|
|
Channel[i].x = frompt[0];
|
|
Channel[i].y = frompt[1];
|
|
Channel[i].z = frompt[2];
|
|
}
|
|
else
|
|
{
|
|
S_StopChannel (i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_GetSoundPlayingInfo
|
|
//
|
|
// Is a sound being played by a specific actor/point?
|
|
//==========================================================================
|
|
|
|
bool S_GetSoundPlayingInfo (fixed_t *pt, int sound_id)
|
|
{
|
|
int i;
|
|
|
|
if (sound_id != 0)
|
|
{
|
|
for (i = 0; i < numChannels; i++)
|
|
{
|
|
if (Channel[i].pt == pt && Channel[i].org_id == sound_id)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool S_GetSoundPlayingInfo (AActor *ent, int sound_id)
|
|
{
|
|
return S_GetSoundPlayingInfo (ent ? &ent->x : NULL, sound_id);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_IsActorPlayingSomething
|
|
//
|
|
//==========================================================================
|
|
|
|
bool S_IsActorPlayingSomething (AActor *actor, int channel)
|
|
{
|
|
int i;
|
|
|
|
if (i_compatflags & COMPATF_MAGICSILENCE)
|
|
{
|
|
channel = 0;
|
|
}
|
|
|
|
for (i = 0; i < numChannels; ++i)
|
|
{
|
|
if (Channel[i].pt == &actor->x)
|
|
{
|
|
if (channel == 0 || Channel[i].entchannel == channel)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_PauseSound
|
|
//
|
|
// Stop music and sound effects, during game PAUSE.
|
|
//==========================================================================
|
|
|
|
void S_PauseSound (bool notmusic)
|
|
{
|
|
if (!notmusic && mus_playing.handle && !MusicPaused)
|
|
{
|
|
I_PauseSong (mus_playing.handle);
|
|
MusicPaused = true;
|
|
}
|
|
if (GSnd != NULL && !SoundPaused)
|
|
{
|
|
GSnd->SetSfxPaused (true);
|
|
SoundPaused = true;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_ResumeSound
|
|
//
|
|
// Resume music and sound effects, after game PAUSE.
|
|
//==========================================================================
|
|
|
|
void S_ResumeSound ()
|
|
{
|
|
if (mus_playing.handle && MusicPaused)
|
|
{
|
|
I_ResumeSong (mus_playing.handle);
|
|
MusicPaused = false;
|
|
}
|
|
if (GSnd != NULL && SoundPaused)
|
|
{
|
|
GSnd->SetSfxPaused (false);
|
|
SoundPaused = false;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_UpdateSounds
|
|
//
|
|
// Updates music & sounds
|
|
//==========================================================================
|
|
|
|
void S_UpdateSounds (void *listener_p)
|
|
{
|
|
fixed_t *listener;
|
|
fixed_t x, y;
|
|
int i, dist, vol;
|
|
angle_t angle;
|
|
int sep;
|
|
|
|
if (GSnd == NULL)
|
|
return;
|
|
|
|
listener = listener_p ? &((AActor *)listener_p)->x : NULL;
|
|
|
|
// [RH] Update playlist
|
|
if (PlayList &&
|
|
mus_playing.handle &&
|
|
!I_QrySongPlaying (mus_playing.handle))
|
|
{
|
|
PlayList->Advance ();
|
|
S_ActivatePlayList (false);
|
|
}
|
|
|
|
for (i = 0; i < numChannels; ++i)
|
|
{
|
|
if (!Channel[i].sfxinfo)
|
|
continue;
|
|
|
|
if (!GSnd->IsPlayingSound (Channel[i].handle))
|
|
{
|
|
S_StopChannel (i);
|
|
}
|
|
if (Channel[i].sound_id == -1 ||
|
|
(Channel[i].pt == listener && !GSnd->Sound3D) ||
|
|
Channel[i].attenuation <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (!Channel[i].pt)
|
|
{
|
|
x = Channel[i].x;
|
|
y = Channel[i].y;
|
|
}
|
|
else
|
|
{
|
|
x = Channel[i].pt[0];
|
|
y = Channel[i].pt[1];
|
|
}
|
|
dist = (int)(FIXED2FLOAT(P_AproxDistance2 (listener, x, y))
|
|
* Channel[i].attenuation);
|
|
if (dist >= MAX_SND_DIST)
|
|
{
|
|
S_StopChannel (i);
|
|
continue;
|
|
}
|
|
else if (dist < 0)
|
|
{
|
|
dist = 0;
|
|
}
|
|
vol = (int)(SoundCurve[dist]*Channel[i].volume);
|
|
if (dist > 0)
|
|
{
|
|
angle = R_PointToAngle2(listener[0], listener[1], x, y);
|
|
if (angle > players[consoleplayer].camera->angle)
|
|
angle = angle - players[consoleplayer].camera->angle;
|
|
else
|
|
angle = angle + (ANGLE_MAX - players[consoleplayer].camera->angle);
|
|
angle >>= ANGLETOFINESHIFT;
|
|
sep = NORM_SEP - (FixedMul (S_STEREO_SWING, finesine[angle])>>FRACBITS);
|
|
if (snd_flipstereo)
|
|
{
|
|
sep = 255-sep;
|
|
}
|
|
if (snd_matrix)
|
|
{
|
|
int rearsep = NORM_SEP -
|
|
(FixedMul (S_STEREO_SWING, finecosine[angle])>>FRACBITS);
|
|
if (rearsep > 128)
|
|
{
|
|
sep = -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sep = NORM_SEP;
|
|
}
|
|
if (!Channel[i].is3d)
|
|
{
|
|
GSnd->UpdateSoundParams (Channel[i].handle, vol, sep, Channel[i].pitch);
|
|
}
|
|
else
|
|
{
|
|
float pos[3], vel[3];
|
|
CalcPosVel (Channel[i].pt, Channel[i].mover, Channel[i].constz, pos, vel);
|
|
GSnd->UpdateSoundParams3D (Channel[i].handle, pos, vel);
|
|
}
|
|
Channel[i].priority = Channel[i].basepriority * (PRIORITY_MAX_ADJUST-(dist/DIST_ADJUST));
|
|
}
|
|
}
|
|
|
|
SN_UpdateActiveSequences ();
|
|
|
|
if (GSnd->Sound3D && players[consoleplayer].camera)
|
|
{
|
|
GSnd->UpdateListener (players[consoleplayer].camera);
|
|
}
|
|
|
|
GSnd->UpdateSounds ();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_ActivatePlayList
|
|
//
|
|
// Plays the next song in the playlist. If no songs in the playlist can be
|
|
// played, then it is deleted.
|
|
//==========================================================================
|
|
|
|
void S_ActivatePlayList (bool goBack)
|
|
{
|
|
int startpos, pos;
|
|
|
|
startpos = pos = PlayList->GetPosition ();
|
|
S_StopMusic (true);
|
|
while (!S_ChangeMusic (PlayList->GetSong (pos), 0, false, true))
|
|
{
|
|
pos = goBack ? PlayList->Backup () : PlayList->Advance ();
|
|
if (pos == startpos)
|
|
{
|
|
delete PlayList;
|
|
PlayList = NULL;
|
|
Printf ("Cannot play anything in the playlist.\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_ChangeCDMusic
|
|
//
|
|
// Starts a CD track as music.
|
|
//==========================================================================
|
|
|
|
bool S_ChangeCDMusic (int track, unsigned int id, bool looping)
|
|
{
|
|
char temp[32];
|
|
|
|
if (id)
|
|
{
|
|
sprintf (temp, ",CD,%d,%x", track, id);
|
|
}
|
|
else
|
|
{
|
|
sprintf (temp, ",CD,%d", track);
|
|
}
|
|
return S_ChangeMusic (temp, 0, looping);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_StartMusic
|
|
//
|
|
// Starts some music with the given name.
|
|
//==========================================================================
|
|
|
|
bool S_StartMusic (const char *m_id)
|
|
{
|
|
return S_ChangeMusic (m_id, 0, false);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_ChangeMusic
|
|
//
|
|
// Starts playing a music, possibly looping.
|
|
//
|
|
// [RH] If music is a MOD, starts it at position order. If name is of the
|
|
// format ",CD,<track>,[cd id]" song is a CD track, and if [cd id] is
|
|
// specified, it will only be played if the specified CD is in a drive.
|
|
//==========================================================================
|
|
|
|
TArray<char> musiccache;
|
|
|
|
bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force)
|
|
{
|
|
if (!force && PlayList)
|
|
{ // Don't change if a playlist is active
|
|
return false;
|
|
}
|
|
|
|
if (musicname == NULL || musicname[0] == 0)
|
|
{
|
|
// Don't choke if the map doesn't have a song attached
|
|
S_StopMusic (true);
|
|
return false;
|
|
}
|
|
|
|
if (!mus_playing.name.IsEmpty() && stricmp (mus_playing.name, musicname) == 0)
|
|
{
|
|
if (order != mus_playing.baseorder)
|
|
{
|
|
mus_playing.baseorder =
|
|
(I_SetSongPosition (mus_playing.handle, order) ? order : 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (strnicmp (musicname, ",CD,", 4) == 0)
|
|
{
|
|
int track = strtoul (musicname+4, NULL, 0);
|
|
const char *more = strchr (musicname+4, ',');
|
|
unsigned int id = 0;
|
|
|
|
if (more != NULL)
|
|
{
|
|
id = strtoul (more+1, NULL, 16);
|
|
}
|
|
S_StopMusic (true);
|
|
mus_playing.handle = I_RegisterCDSong (track, id);
|
|
}
|
|
else
|
|
{
|
|
int lumpnum = -1;
|
|
int offset, length;
|
|
|
|
if (!FileExists (musicname))
|
|
{
|
|
if ((lumpnum = Wads.CheckNumForName (musicname)) == -1)
|
|
{
|
|
Printf ("Music \"%s\" not found\n", musicname);
|
|
return false;
|
|
}
|
|
if (!Wads.IsUncompressedFile(lumpnum))
|
|
{
|
|
// We must cache the music data and use it from memory.
|
|
|
|
// shut down old music before reallocating and overwriting the cache!
|
|
S_StopMusic (true);
|
|
|
|
offset = -1; // this tells the low level code that the music
|
|
// is being used from memory
|
|
length = Wads.LumpLength (lumpnum);
|
|
if (length == 0)
|
|
{
|
|
offset = 0;
|
|
return false;
|
|
}
|
|
musiccache.Resize(length);
|
|
Wads.ReadLump(lumpnum, &musiccache[0]);
|
|
}
|
|
else
|
|
{
|
|
offset = Wads.GetLumpOffset (lumpnum);
|
|
length = Wads.LumpLength (lumpnum);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
offset = 0;
|
|
length = 0;
|
|
}
|
|
|
|
// shutdown old music
|
|
S_StopMusic (true);
|
|
|
|
// load & register it
|
|
if (offset!=-1)
|
|
{
|
|
mus_playing.handle = I_RegisterSong (lumpnum != -1 ?
|
|
Wads.GetWadFullName (Wads.GetLumpFile (lumpnum)) :
|
|
musicname, NULL, offset, length);
|
|
}
|
|
else
|
|
{
|
|
// musiccache is used globally because otherwise I'd have to pass
|
|
// it as a parameter through several layers of code.
|
|
mus_playing.handle = I_RegisterSong (NULL, &musiccache[0], -1, length);
|
|
}
|
|
}
|
|
|
|
mus_playing.loop = looping;
|
|
|
|
if (mus_playing.handle != 0)
|
|
{ // play it
|
|
mus_playing.name = musicname;
|
|
I_PlaySong (mus_playing.handle, looping, S_GetMusicVolume (musicname));
|
|
mus_playing.baseorder =
|
|
(I_SetSongPosition (mus_playing.handle, order) ? order : 0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_RestartMusic
|
|
//
|
|
// Must only be called from snd_reset in i_sound.cpp!
|
|
//==========================================================================
|
|
|
|
void S_RestartMusic ()
|
|
{
|
|
if (!LastSong.IsEmpty())
|
|
{
|
|
S_ChangeMusic (LastSong, mus_playing.baseorder, mus_playing.loop, true);
|
|
LastSong = "";
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_GetMusic
|
|
//
|
|
//==========================================================================
|
|
|
|
int S_GetMusic (char **name)
|
|
{
|
|
int order;
|
|
|
|
if (mus_playing.name)
|
|
{
|
|
*name = copystring (mus_playing.name);
|
|
order = mus_playing.baseorder;
|
|
}
|
|
else
|
|
{
|
|
*name = NULL;
|
|
order = 0;
|
|
}
|
|
return order;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_StopMusic
|
|
//
|
|
//==========================================================================
|
|
|
|
void S_StopMusic (bool force)
|
|
{
|
|
// [RH] Don't stop if a playlist is active.
|
|
if ((force || PlayList == NULL) && !mus_playing.name.IsEmpty())
|
|
{
|
|
if (MusicPaused)
|
|
I_ResumeSong(mus_playing.handle);
|
|
|
|
I_StopSong(mus_playing.handle);
|
|
I_UnRegisterSong(mus_playing.handle);
|
|
|
|
LastSong = mus_playing.name;
|
|
mus_playing.name = "";
|
|
mus_playing.handle = 0;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// S_StopChannel
|
|
//
|
|
//==========================================================================
|
|
|
|
static void S_StopChannel (int cnum)
|
|
{
|
|
|
|
// int i;
|
|
channel_t* c = &Channel[cnum];
|
|
|
|
if (c->sfxinfo)
|
|
{
|
|
// stop the sound playing
|
|
if (GSnd != NULL) GSnd->StopSound (c->handle);
|
|
|
|
// check to see
|
|
// if other channels are playing the sound
|
|
/* for (i = 0; i < numChannels; i++)
|
|
{
|
|
if (cnum != i && c->sfxinfo == Channel[i].sfxinfo)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
// degrade usefulness of sound data
|
|
// c->sfxinfo->usefulness--;
|
|
|
|
c->sfxinfo = NULL;
|
|
c->handle = 0;
|
|
c->sound_id = -1;
|
|
c->pt = NULL;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD playsound
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (playsound)
|
|
{
|
|
if (argv.argc() > 1)
|
|
{
|
|
S_Sound (CHAN_AUTO, argv[1], 1.f, ATTN_NONE);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD idmus
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (idmus)
|
|
{
|
|
level_info_t *info;
|
|
char *map;
|
|
int l;
|
|
|
|
if (argv.argc() > 1)
|
|
{
|
|
if (gameinfo.flags & GI_MAPxx)
|
|
{
|
|
l = atoi (argv[1]);
|
|
if (l <= 99)
|
|
map = CalcMapName (0, l);
|
|
else
|
|
{
|
|
Printf ("%s\n", GStrings("STSTR_NOMUS"));
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
map = CalcMapName (argv[1][0] - '0', argv[1][1] - '0');
|
|
}
|
|
|
|
if ( (info = FindLevelInfo (map)) )
|
|
{
|
|
if (info->music)
|
|
{
|
|
S_ChangeMusic (info->music, info->musicorder);
|
|
Printf ("%s\n", GStrings("STSTR_MUS"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Printf ("%s\n", GStrings("STSTR_NOMUS"));
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD changemus
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (changemus)
|
|
{
|
|
if (argv.argc() > 1)
|
|
{
|
|
if (PlayList)
|
|
{
|
|
delete PlayList;
|
|
PlayList = NULL;
|
|
}
|
|
S_ChangeMusic (argv[1], argv.argc() > 2 ? atoi (argv[2]) : 0);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD stopmus
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (stopmus)
|
|
{
|
|
if (PlayList)
|
|
{
|
|
delete PlayList;
|
|
PlayList = NULL;
|
|
}
|
|
S_StopMusic (false);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD cd_play
|
|
//
|
|
// Plays a specified track, or the entire CD if no track is specified.
|
|
//==========================================================================
|
|
|
|
CCMD (cd_play)
|
|
{
|
|
char musname[16];
|
|
|
|
if (argv.argc() == 1)
|
|
strcpy (musname, ",CD,");
|
|
else
|
|
sprintf (musname, ",CD,%d", atoi(argv[1]));
|
|
S_ChangeMusic (musname, 0, true);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD cd_stop
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (cd_stop)
|
|
{
|
|
CD_Stop ();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD cd_eject
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (cd_eject)
|
|
{
|
|
CD_Eject ();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD cd_close
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (cd_close)
|
|
{
|
|
CD_UnEject ();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD cd_pause
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (cd_pause)
|
|
{
|
|
CD_Pause ();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD cd_resume
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (cd_resume)
|
|
{
|
|
CD_Resume ();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD playlist
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (playlist)
|
|
{
|
|
int argc = argv.argc();
|
|
|
|
if (argc < 2 || argc > 3)
|
|
{
|
|
Printf ("playlist <playlist.m3u> [<position>|shuffle]\n");
|
|
}
|
|
else
|
|
{
|
|
if (PlayList != NULL)
|
|
{
|
|
PlayList->ChangeList (argv[1]);
|
|
}
|
|
else
|
|
{
|
|
PlayList = new FPlayList (argv[1]);
|
|
}
|
|
if (PlayList->GetNumSongs () == 0)
|
|
{
|
|
delete PlayList;
|
|
PlayList = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (argc == 3)
|
|
{
|
|
if (stricmp (argv[2], "shuffle") == 0)
|
|
{
|
|
PlayList->Shuffle ();
|
|
}
|
|
else
|
|
{
|
|
PlayList->SetPosition (atoi (argv[2]));
|
|
}
|
|
}
|
|
S_ActivatePlayList (false);
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD playlistpos
|
|
//
|
|
//==========================================================================
|
|
|
|
static bool CheckForPlaylist ()
|
|
{
|
|
if (PlayList == NULL)
|
|
{
|
|
Printf ("No playlist is playing.\n");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
CCMD (playlistpos)
|
|
{
|
|
if (CheckForPlaylist() && argv.argc() > 1)
|
|
{
|
|
PlayList->SetPosition (atoi (argv[1]) - 1);
|
|
S_ActivatePlayList (false);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD playlistnext
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (playlistnext)
|
|
{
|
|
if (CheckForPlaylist())
|
|
{
|
|
PlayList->Advance ();
|
|
S_ActivatePlayList (false);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD playlistprev
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (playlistprev)
|
|
{
|
|
if (CheckForPlaylist())
|
|
{
|
|
PlayList->Backup ();
|
|
S_ActivatePlayList (true);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD playliststatus
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (playliststatus)
|
|
{
|
|
if (CheckForPlaylist ())
|
|
{
|
|
Printf ("Song %d of %d:\n%s\n",
|
|
PlayList->GetPosition () + 1,
|
|
PlayList->GetNumSongs (),
|
|
PlayList->GetSong (PlayList->GetPosition ()));
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD cachesound <sound name>
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (cachesound)
|
|
{
|
|
if (argv.argc() < 2)
|
|
{
|
|
Printf ("Usage: cachesound <sound> ...\n");
|
|
return;
|
|
}
|
|
for (int i = 1; i < argv.argc(); ++i)
|
|
{
|
|
int sfxnum = S_FindSound (argv[i]);
|
|
if (sfxnum > 0)
|
|
{
|
|
S_CacheSound (&S_sfx[sfxnum]);
|
|
}
|
|
}
|
|
}
|