2008-01-27 11:25:03 +00:00
|
|
|
// 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
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
#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"
|
Update to ZDoom r905:
- Added Martin Howe's morph system update.
- Added support for defining composite textures in HIRESTEX. It is not fully tested
and right now can't do much more than the old TEXTUREx method.
- Added a few NULL pointer checks to the texture code.
- Made duplicate class names in DECORATE non-fatal. There is really no stability
concern here and the worst that can happen is that the wrong actor is spawned.
This was a constant hassle when testing with WADs that contain duplicate resources.
- Removed some GCC warnings.
- Fixed: MinGW doesn't have _get_pgmptr(), so it couldn't compile i_main.cpp.
- Fixed: MOD_WAVETABLE and MOD_SWSYNTH are not defined by w32api, so MinGW
failed compiling the new MIDI code.
- Fixed: LocalSndInfo and LocalSndSeq in S_Start() need to be const char
pointers, since "" is a constant.
- Fixed: parsecontext.h was missing a newline at the end of the file.
- Fixed: Timidity::Channel::mono, rpn, and nrpn were not initialized. In
particular, this meant that every channel was almost certainly in mono mode,
which can sound pretty bad if the song isn't meant to be played that way.
- Added bank numbers to the MIDI precaching for Timidity, since I guess I do
need to care about banks, if even the Duke MIDIs use various banks.
- Fixed: snd_midiprecache only exists in Win32 builds, so gameconfigfile.cpp
shouldn't unconditionally link against it.
- Fixed: pre_resample() was still disabled, and it left two samples at the end
of the new wave data uninitialized.
- Moved the xmap table from timidity/tables.cpp to playmidi.cpp. Now I can get
rid of timidity/tables.cpp, which conflicts in name with the main Doom
tables.cpp. (And interestingly, VC++ automatically renamed the object file,
so I wasn't aware of the problem with GCC.)
- Added a Gets function to the FileReader class which I planned to use
to enable Timidity to read its config and sound patches from Zips.
I put this on hold though after finding out that the sound quality
isn't even near that of Timidity++.
- GCC-Fixes (FString::GetChars() for Printf calls)
- Added a dummy Weapon.NOLMS flag so that Skulltag weapons using this flag
can be loaded
- Changed the MIDIStreamer to send the all notes off controller to each
channel when restarting the song, rather than emitting a single note off
event which only has 1 in 127 chance of being for a note that's playing
on that channel. Then I decided it would probably be a good idea to reset
all the controllers as well.
- Increasing the size of the internal Timidity stream buffer from 1/14 sec
(copied from the OPL player) improved its sound dramatically, so apparently
Timidity has issues with short stream buffers. It's now at 1/2 sec in
length. However, there seems to be something weird going on with
corazonazul_ff6boss.mid near the beginning where it stops and immediately
restarts a guitar on the exact same note.
- Added a new sound debugging cvar: snd_drawoutput, which can show various
oscilloscopes and spectrums.
- Eliminated some more global variables (onmobj, DoRipping, LastRipped,
MissileActor, bulletpitch and linetarget.)
- Internal TiMidity now plays music. Unfortunately, it doesn't sound right. :(
- Changed the progdir global variable into an FString.
git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@90 b0f79afe-0144-0410-b225-9a4edf0717df
2008-04-12 18:59:23 +00:00
|
|
|
#include "timidity/timidity.h"
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2008-03-21 21:15:56 +00:00
|
|
|
#define SELECT_ATTEN(a) ((a)==ATTN_NONE ? 0 : (a)==ATTN_STATIC ? 3 : 1)
|
2008-01-27 11:25:03 +00:00
|
|
|
#ifndef FIXED2FLOAT
|
|
|
|
#define FIXED2FLOAT(f) (((float)(f))/(float)65536)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define NORM_PITCH 128
|
|
|
|
#define NORM_PRIORITY 64
|
2008-03-09 16:01:55 +00:00
|
|
|
#define NORM_SEP 0
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
#define S_PITCH_PERTURB 1
|
2008-03-12 15:21:17 +00:00
|
|
|
#define S_STEREO_SWING 0.75
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
// TYPES -------------------------------------------------------------------
|
|
|
|
|
|
|
|
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 ---------------------------------------------
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
static fixed_t P_AproxDistance2(fixed_t *listener, fixed_t x, fixed_t y);
|
|
|
|
static void S_StartSound(fixed_t *pt, AActor *mover, int channel,
|
|
|
|
int sound_id, float volume, float attenuation);
|
2008-03-30 08:34:44 +00:00
|
|
|
static bool S_CheckSoundLimit(sfxinfo_t *sfx, float pos[3], int NearLimit);
|
2008-03-25 16:19:31 +00:00
|
|
|
static void S_ActivatePlayList(bool goBack);
|
|
|
|
static void CalcPosVel(fixed_t *pt, AActor *mover, int constz, float pos[3],
|
2008-01-27 11:25:03 +00:00
|
|
|
float vel[3]);
|
|
|
|
|
|
|
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
|
|
|
|
|
|
|
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 FPlayList *PlayList;
|
|
|
|
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
|
|
|
|
int sfx_empty;
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
FSoundChan *Channels;
|
|
|
|
FSoundChan *FreeChannels;
|
|
|
|
|
|
|
|
int S_RolloffType;
|
|
|
|
float S_MinDistance;
|
|
|
|
float S_MaxDistanceOrRolloffFactor;
|
|
|
|
BYTE *S_SoundCurve;
|
|
|
|
int S_SoundCurveSize;
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
CVAR (Bool, snd_surround, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // [RH] Use surround sounds?
|
|
|
|
FBoolCVar noisedebug ("noise", false, 0); // [RH] Print sound debugging info?
|
2008-03-09 16:01:55 +00:00
|
|
|
CVAR (Int, snd_channels, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // number of channels available
|
2008-01-27 11:25:03 +00:00
|
|
|
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)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
FSoundChan *chan;
|
|
|
|
fixed_t ox, oy, oz;
|
|
|
|
int y, color;
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
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);
|
2008-03-25 16:19:31 +00:00
|
|
|
screen->DrawText (CR_GOLD, 170, y, "z", TAG_DONE);
|
|
|
|
screen->DrawText (CR_GOLD, 220, y, "vol", TAG_DONE);
|
|
|
|
screen->DrawText (CR_GOLD, 250, y, "dist", TAG_DONE);
|
|
|
|
screen->DrawText (CR_GOLD, 290, y, "chan", TAG_DONE);
|
2008-01-27 11:25:03 +00:00
|
|
|
y += 8;
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
if (Channels == NULL)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-01-27 11:25:03 +00:00
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
// Display the last channel first.
|
|
|
|
for (chan = Channels; chan->NextChan != NULL; chan = chan->NextChan)
|
|
|
|
{ }
|
|
|
|
while (y < SCREENHEIGHT - 16)
|
|
|
|
{
|
|
|
|
char temp[16];
|
|
|
|
fixed_t *origin = chan->Pt;
|
|
|
|
|
|
|
|
if (!(chan->ChanFlags & CHAN_IS3D))
|
|
|
|
{
|
|
|
|
ox = players[consoleplayer].camera->x;
|
|
|
|
oy = players[consoleplayer].camera->y;
|
|
|
|
oz = players[consoleplayer].camera->z;
|
|
|
|
}
|
|
|
|
else if (origin)
|
|
|
|
{
|
|
|
|
ox = origin[0];
|
|
|
|
oy = origin[1];
|
|
|
|
oz = origin[2];
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
ox = chan->X;
|
|
|
|
oy = chan->Y;
|
|
|
|
oz = chan->Z;
|
|
|
|
}
|
|
|
|
color = (chan->ChanFlags & CHAN_LOOP) ? CR_BROWN : CR_GREY;
|
|
|
|
Wads.GetLumpName (temp, chan->SfxInfo->lumpnum);
|
|
|
|
temp[8] = 0;
|
|
|
|
screen->DrawText (color, 0, y, temp, TAG_DONE);
|
|
|
|
sprintf (temp, "%d", ox >> FRACBITS);
|
|
|
|
screen->DrawText (color, 70, y, temp, TAG_DONE);
|
|
|
|
sprintf (temp, "%d", oy >> FRACBITS);
|
|
|
|
screen->DrawText (color, 120, y, temp, TAG_DONE);
|
|
|
|
sprintf (temp, "%d", oz >> FRACBITS);
|
|
|
|
screen->DrawText (color, 170, y, temp, TAG_DONE);
|
|
|
|
sprintf (temp, "%g", chan->Volume);
|
|
|
|
screen->DrawText (color, 220, y, temp, TAG_DONE);
|
|
|
|
sprintf (temp, "%d", P_AproxDistance2 (players[consoleplayer].camera, ox, oy) / FRACUNIT);
|
|
|
|
screen->DrawText (color, 250, y, temp, TAG_DONE);
|
|
|
|
sprintf (temp, "%d", chan->EntChannel);
|
|
|
|
screen->DrawText (color, 290, y, temp, TAG_DONE);
|
|
|
|
y += 8;
|
|
|
|
if (chan->PrevChan == &Channels)
|
|
|
|
{
|
|
|
|
break;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
2008-03-25 16:19:31 +00:00
|
|
|
chan = (FSoundChan *)((size_t)chan->PrevChan - myoffsetof(FSoundChan, NextChan));
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
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 ()
|
|
|
|
{
|
|
|
|
int curvelump;
|
|
|
|
|
|
|
|
atterm (S_Shutdown);
|
|
|
|
|
|
|
|
// remove old data (S_Init can be called multiple times!)
|
2008-03-25 16:19:31 +00:00
|
|
|
if (S_SoundCurve != NULL)
|
2008-03-09 16:01:55 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
delete[] S_SoundCurve;
|
2008-03-09 16:01:55 +00:00
|
|
|
}
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
// Heretic and Hexen have sound curve lookup tables. Doom does not.
|
|
|
|
curvelump = Wads.CheckNumForName ("SNDCURVE");
|
|
|
|
if (curvelump >= 0)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
S_SoundCurveSize = Wads.LumpLength (curvelump);
|
|
|
|
S_SoundCurve = new BYTE[S_SoundCurveSize];
|
|
|
|
Wads.ReadLump(curvelump, S_SoundCurve);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
// Free all channels for use.
|
|
|
|
while (Channels != NULL)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
S_ReturnChannel(Channels);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
Update to ZDoom r905:
- Added Martin Howe's morph system update.
- Added support for defining composite textures in HIRESTEX. It is not fully tested
and right now can't do much more than the old TEXTUREx method.
- Added a few NULL pointer checks to the texture code.
- Made duplicate class names in DECORATE non-fatal. There is really no stability
concern here and the worst that can happen is that the wrong actor is spawned.
This was a constant hassle when testing with WADs that contain duplicate resources.
- Removed some GCC warnings.
- Fixed: MinGW doesn't have _get_pgmptr(), so it couldn't compile i_main.cpp.
- Fixed: MOD_WAVETABLE and MOD_SWSYNTH are not defined by w32api, so MinGW
failed compiling the new MIDI code.
- Fixed: LocalSndInfo and LocalSndSeq in S_Start() need to be const char
pointers, since "" is a constant.
- Fixed: parsecontext.h was missing a newline at the end of the file.
- Fixed: Timidity::Channel::mono, rpn, and nrpn were not initialized. In
particular, this meant that every channel was almost certainly in mono mode,
which can sound pretty bad if the song isn't meant to be played that way.
- Added bank numbers to the MIDI precaching for Timidity, since I guess I do
need to care about banks, if even the Duke MIDIs use various banks.
- Fixed: snd_midiprecache only exists in Win32 builds, so gameconfigfile.cpp
shouldn't unconditionally link against it.
- Fixed: pre_resample() was still disabled, and it left two samples at the end
of the new wave data uninitialized.
- Moved the xmap table from timidity/tables.cpp to playmidi.cpp. Now I can get
rid of timidity/tables.cpp, which conflicts in name with the main Doom
tables.cpp. (And interestingly, VC++ automatically renamed the object file,
so I wasn't aware of the problem with GCC.)
- Added a Gets function to the FileReader class which I planned to use
to enable Timidity to read its config and sound patches from Zips.
I put this on hold though after finding out that the sound quality
isn't even near that of Timidity++.
- GCC-Fixes (FString::GetChars() for Printf calls)
- Added a dummy Weapon.NOLMS flag so that Skulltag weapons using this flag
can be loaded
- Changed the MIDIStreamer to send the all notes off controller to each
channel when restarting the song, rather than emitting a single note off
event which only has 1 in 127 chance of being for a note that's playing
on that channel. Then I decided it would probably be a good idea to reset
all the controllers as well.
- Increasing the size of the internal Timidity stream buffer from 1/14 sec
(copied from the OPL player) improved its sound dramatically, so apparently
Timidity has issues with short stream buffers. It's now at 1/2 sec in
length. However, there seems to be something weird going on with
corazonazul_ff6boss.mid near the beginning where it stops and immediately
restarts a guitar on the exact same note.
- Added a new sound debugging cvar: snd_drawoutput, which can show various
oscilloscopes and spectrums.
- Eliminated some more global variables (onmobj, DoRipping, LastRipped,
MissileActor, bulletpitch and linetarget.)
- Internal TiMidity now plays music. Unfortunately, it doesn't sound right. :(
- Changed the progdir global variable into an FString.
git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@90 b0f79afe-0144-0410-b225-9a4edf0717df
2008-04-12 18:59:23 +00:00
|
|
|
|
2008-01-27 11:25:03 +00:00
|
|
|
// no sounds are playing, and they are not paused
|
|
|
|
MusicPaused = false;
|
|
|
|
SoundPaused = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_InitData
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_InitData ()
|
|
|
|
{
|
|
|
|
LastLocalSndInfo = LastLocalSndSeq = "";
|
|
|
|
S_ParseSndInfo ();
|
|
|
|
S_ParseSndSeq (-1);
|
|
|
|
S_ParseSndEax ();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_Shutdown
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_Shutdown ()
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
FSoundChan *chan, *next;
|
|
|
|
|
|
|
|
if (GSnd != NULL)
|
|
|
|
{
|
|
|
|
while (Channels != NULL)
|
|
|
|
{
|
|
|
|
GSnd->StopSound(Channels);
|
|
|
|
}
|
|
|
|
GSnd->UpdateSounds();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (chan = Channels; chan != NULL; chan = next)
|
|
|
|
{
|
|
|
|
next = chan->NextChan;
|
|
|
|
delete chan;
|
|
|
|
}
|
|
|
|
Channels = NULL;
|
|
|
|
}
|
|
|
|
for (chan = FreeChannels; chan != NULL; chan = next)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
next = chan->NextChan;
|
|
|
|
delete chan;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
2008-03-25 16:19:31 +00:00
|
|
|
FreeChannels = NULL;
|
|
|
|
|
|
|
|
if (S_SoundCurve != NULL)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
delete[] S_SoundCurve;
|
|
|
|
S_SoundCurve = NULL;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
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 ()
|
|
|
|
{
|
|
|
|
if (GSnd)
|
|
|
|
{
|
|
|
|
// kill all playing sounds at start of level (trust me - a good idea)
|
2008-03-25 16:19:31 +00:00
|
|
|
S_StopAllChannels();
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
// Check for local sound definitions. Only reload if they differ
|
|
|
|
// from the previous ones.
|
Update to ZDoom r905:
- Added Martin Howe's morph system update.
- Added support for defining composite textures in HIRESTEX. It is not fully tested
and right now can't do much more than the old TEXTUREx method.
- Added a few NULL pointer checks to the texture code.
- Made duplicate class names in DECORATE non-fatal. There is really no stability
concern here and the worst that can happen is that the wrong actor is spawned.
This was a constant hassle when testing with WADs that contain duplicate resources.
- Removed some GCC warnings.
- Fixed: MinGW doesn't have _get_pgmptr(), so it couldn't compile i_main.cpp.
- Fixed: MOD_WAVETABLE and MOD_SWSYNTH are not defined by w32api, so MinGW
failed compiling the new MIDI code.
- Fixed: LocalSndInfo and LocalSndSeq in S_Start() need to be const char
pointers, since "" is a constant.
- Fixed: parsecontext.h was missing a newline at the end of the file.
- Fixed: Timidity::Channel::mono, rpn, and nrpn were not initialized. In
particular, this meant that every channel was almost certainly in mono mode,
which can sound pretty bad if the song isn't meant to be played that way.
- Added bank numbers to the MIDI precaching for Timidity, since I guess I do
need to care about banks, if even the Duke MIDIs use various banks.
- Fixed: snd_midiprecache only exists in Win32 builds, so gameconfigfile.cpp
shouldn't unconditionally link against it.
- Fixed: pre_resample() was still disabled, and it left two samples at the end
of the new wave data uninitialized.
- Moved the xmap table from timidity/tables.cpp to playmidi.cpp. Now I can get
rid of timidity/tables.cpp, which conflicts in name with the main Doom
tables.cpp. (And interestingly, VC++ automatically renamed the object file,
so I wasn't aware of the problem with GCC.)
- Added a Gets function to the FileReader class which I planned to use
to enable Timidity to read its config and sound patches from Zips.
I put this on hold though after finding out that the sound quality
isn't even near that of Timidity++.
- GCC-Fixes (FString::GetChars() for Printf calls)
- Added a dummy Weapon.NOLMS flag so that Skulltag weapons using this flag
can be loaded
- Changed the MIDIStreamer to send the all notes off controller to each
channel when restarting the song, rather than emitting a single note off
event which only has 1 in 127 chance of being for a note that's playing
on that channel. Then I decided it would probably be a good idea to reset
all the controllers as well.
- Increasing the size of the internal Timidity stream buffer from 1/14 sec
(copied from the OPL player) improved its sound dramatically, so apparently
Timidity has issues with short stream buffers. It's now at 1/2 sec in
length. However, there seems to be something weird going on with
corazonazul_ff6boss.mid near the beginning where it stops and immediately
restarts a guitar on the exact same note.
- Added a new sound debugging cvar: snd_drawoutput, which can show various
oscilloscopes and spectrums.
- Eliminated some more global variables (onmobj, DoRipping, LastRipped,
MissileActor, bulletpitch and linetarget.)
- Internal TiMidity now plays music. Unfortunately, it doesn't sound right. :(
- Changed the progdir global variable into an FString.
git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@90 b0f79afe-0144-0410-b225-9a4edf0717df
2008-04-12 18:59:23 +00:00
|
|
|
const char *LocalSndInfo;
|
|
|
|
const char *LocalSndSeq;
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
// To be certain better check whether level is valid!
|
2008-04-05 16:46:26 +00:00
|
|
|
if (level.info && level.info->soundinfo)
|
|
|
|
{
|
|
|
|
LocalSndInfo = level.info->soundinfo;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LocalSndInfo = "";
|
- Fixed: Fog for flooding floor textures into gaps created by missing wall textures
didn't work since a parameter changes necessitated by ZDoom's render style 'enhancement'.
Update to ZDoom r940:
SBarInfo Update #18:
- Simplified the DrawGraphic function in sbarinfo_display.cpp
- Added xOffset, yOffset, and alpha to every drawing function in
sbarinfo_display.cpp. So Strife popups can be handeled better and allow for
other effects (translucent bars?). I'm thinking about making a struct for
these five (also x and y) arguments so that the argument lists don't become a
mess.
- Changed DRAWIMAGE in sbarinfo_display.cpp to not use so many calls to
DrawGraphic.
- DrawKeyBar wasn't using screen->DrawTexture.
- Added a Fade transition for popups. It takes two args fade in rate and fade
out rate. Both are floats (1.0 = 1 tic to complete 0.5 = 2 tics to complete
and so on).
- Added a translucency arg to statusbars. 1.0 = opaque and 0.0 = invisible.
- Fixed: When an instrument's envelope runs out, it does not immediately ramp
to zero. Rather, it lets the remainder of the sample finish playing.
- Fixed: When playing a MIDI file with EMIDI track designations to turn a
track off, any ticks that had only events on the disabled track would cause
the delay for that track to be thrown away, and the following notes on
enabled tracks would play too soon. This could be heard quite clearly in
xplasma.mid, where track 4 (FMGlass Drone 1) would interfere with the timing
of tracks 13 and 14 (EP1 Melody and EP1 Echo).
- Fixed: DFlashFader did some operations in its destructor that had to be moved
to its Destroy method.
- Fixed: Dropped weapons from dying players should not double ammo.
- Fixed: When note_on() is called and another copy of the same note is
already playing on the channel, it should stop it with finish_note(), not
kill_note(). This can be clearly heard in the final cymbal crashes of
D_DM2TTL where TiMidity cuts them off because the final cymbals are played
with a velocity of 1 before the preceding cymbals have finished. (I wonder
if I should be setting the self_nonexclusive flag for GUS patches to
disable even this behavior, though, since gf1note.c doesn't turn off
duplicate notes.)
- Changed envelope handling to hopefully match the GUS player's. The most
egregious mistake TiMidity makes is to treat bit 6 as an envelope enable
bit. This is not what it does; every sample has an envelope. Rather, this
is a "no sampled release" flag. Also, despite fiddling with the
PATCH_SUSTAIN flag during instrument loading, TiMidity never actually
used it. Nor did it do anything at all with the PATCH_FAST_REL flag.
- Fixed: wbstartstruct's lump name fields were only 8 characters long
and not properly zero-terminated when all 8 characters were used.
- Fixed: Local sound sequence definitions caused a crash because a proper
NULL check was missing.
- Added translucent blending modes to FMultipatchTexture (not tested yet!)
- Also changed all true color texture creation functions to use proper alpha
values instead of inverted ones.
- Changed FRemapTable so that all palette entries must contain proper alpha
values.
- Fixed: The F1 screen check in m_menu.cpp was missing a NULL pointer check.
- Changed: The boss brain's explosions play weapons/rocklx which is an
unlimited sound. This can become extremely loud. Replaced with a new
sound which is just an alias to weapons/rocklx but has a limit of 4.
git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@98 b0f79afe-0144-0410-b225-9a4edf0717df
2008-04-25 10:00:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (level.info && level.info->sndseq)
|
|
|
|
{
|
|
|
|
LocalSndSeq = level.info->sndseq;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-04-05 16:46:26 +00:00
|
|
|
LocalSndSeq = "";
|
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
bool parse_ss = false;
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
// This level uses a different local SNDINFO
|
|
|
|
if (LastLocalSndInfo.CompareNoCase(LocalSndInfo) != 0 || !level.info)
|
|
|
|
{
|
|
|
|
// First delete the old sound list
|
2008-03-25 16:19:31 +00:00
|
|
|
for(unsigned i = 1; i < S_sfx.Size(); i++)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
|
|
|
GSnd->UnloadSound(&S_sfx[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the global SNDINFO
|
|
|
|
S_ParseSndInfo();
|
|
|
|
|
|
|
|
if (*LocalSndInfo)
|
|
|
|
{
|
|
|
|
// Now parse the local SNDINFO
|
- Fixed: The players were not added to FS's list of spawned things.
- Update to ZDoom r882
- Added the option to use $ as a prefix to a string table name everywhere in
MAPINFO where 'lookup' could be specified so that there is one consistent
way to do it.
- Externalized all default episode definitions. Added an 'optional' keyword
to handle M4 and 5 in Doom and Heretic.
- Added P_CheckMapData function and replaced all calls to P_OpenMapData that
only checked for a map's presence with it.
- Added Martin Howe's player statusbar face submission.
- Added an 'adddefaultmap' option for MAPINFO. This is the same as 'defaultmap'
but keeps all existing information in the default and just adds to it. This
is needed because Hexen and Strife set some information in their base
MAPINFO and using 'defaultmap' in a PWAD would override that.
- Fixed: Using MAPINFO's f1 option could cause memory leaks.
- Added option to load lumps by full name to several places:
* Finale texts loaded from a text lump
* Demos
* Local SNDINFOs
* Local SNDSEQs
* Image names in FONTDEFS
* intermission script names
- Changed the STCFN121 handling. The character is not an 'I' but a '|' so
instead of discarding it it should be inserted at position 124.
- Renamed indexfont.fon to indexfont so that I could remove a special case
from V_GetFont that was just added for this one font.
- Added a 'dumpspawnedthings' CVAR that enables a listing of all things in
the map and the actor type they spawned.
SBarInfo Update #16
- Added: fillzeros flag for drawnumber. When set the string will always have
a length of the specified size and zeros will fill in for the missing places.
If the number is negative the negative sign will take the place of the last
digit.
- Added: globalarray type to drawnumber which will display the value in a
global array with the index set to the player's number. Untested.
- Added: isselected command to SBarInfo.
- Fixed: Bi and Tri colored numbers didn't work.
- Fixed: Crash when using nullimage as the last image in drawswitchableimage.
- Applied Graf suggestion to include the y coord when calulating heights to fix
most of the gaps caused by round off errors. At least for now anyways and it
is only applied for drawimage.
- SBarInfo inventory bars have been converted to use screen->DrawTexture()
- Increased limit for demon/melee to 4.
- Fixed: P_CheckSwitchRange accessed invalid memory when testing a one-sided
line.
- Fixed: P_SpawnPuff assumed that all melee attacks have the same range
(MELEERANGE) and didn't set the puff to its melee state if the range
was different. Even worse, it checked a global variable for this so
the behavior was undefined when P_SpawnPuff was called from anywhere
else but P_LineAttack. To reduce the amount of parameters I combined
this information with the hitthing and temporary parameters into one
flags parameter. Also changed P_LineAttack so that it gets passed
an additional parameter that specifies whether the attack is a melee
attack or not and set this to true in all calls that are to be considered
melee attacks. I couldn't use the damage type because A_CustomPunch
and A_CustomMeleeAttack allow passing any damage type they want.
- Added a sprite option as an alternative of particles for FX_ROCKET
and FX_GRENADE.
- Fixed: The minimum parameter count for ACS_Execute and ACS_ExecuteAlways for
DECORATE was wrong (2 instead of 1.)
- Changed: Hexen set every cluster to be a hub if it hadn't been defined before
a level using this cluster. Now it will only do that if HexenHack is true,
i.e. when original Hexen format MAPINFOs are parsed. For ZDoom format
MAPINFOs it will now be the same as for the other games which means that
'hub' has to be declared explicitly.
- Added an Idle state that is entered in place of the spawn state if a monster
has to return to its inactive state if it can't find any more targets.
- Added MF5_NOINTERACTION flag which completely disables all physics related
code for any actor with this flag. Mostly useful for particle effects where
the actors just move a certain distance and then disappear.
- Removed the last remains of the antialias precalculation code from
am_map.cpp because it was no longer used.
- Fixed: Two-sided lines bordering a secret sector were not drawn in the
proper color
- Fixed: The automap didn't check ACS_LockedExecuteDoor for its lock color.
- Switched sounds local to the listener from head-relative 3D sounds to 2D
sounds so stereo sounds have full separation. I tried using set3DSpread,
but that still caused some blending of the channels.
- Changed FScanner so that opening a lump gives the complete wad+lump name
rather than a generic one, so identifying errors among files that all have
the same lump name no longer involves any degree of guesswork in
determining exactly which file the error occurred in.
- Added a check to S_ParseSndSeq() for SNDSEQ lumps with unterminated final
sequences.
- Fixed: Parts of s_sndseq.cpp that scan the Sequences array need NULL
pointer checks, in case an improper sequence was encountered during
parsing but not early enough to avoid creating a slot for it in the array.
- Added support for dumping from RAW/DRO/IMF files, so now anything that
can be played as OPL can also be dumped.
- Removed the opl_enable cvar, since OPL playback is now selectable as just
another MIDI device.
- Added support for DRO playback and dual-chip RAW playback.
- Removed MUS support from OPLMUSSong, since using the OPLMIDIDevice with
MUSSong2 works just as well. There are still lots of leftover bits in
the class that should probably be removed at some point, too.
- Added dual-chip dumping support for the RAW format.
- Added DosBox Raw OPL (.DRO) dumping support. For whatever reason,
in_adlib calculates the song length for this format wrong, even though
the exact length is stored right in the header. (But in_adlib seems buggy
in general; too bad it's the only Windows version of Adplug that seems to
exist.)
- Rewrote the OPL dumper to work with MIDI as well as MUS.
git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@86 b0f79afe-0144-0410-b225-9a4edf0717df
2008-04-05 13:28:48 +00:00
|
|
|
int j = Wads.CheckNumForFullName(LocalSndInfo, true);
|
2008-01-27 11:25:03 +00:00
|
|
|
if (j>=0) S_AddLocalSndInfo(j);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Also reload the SNDSEQ if the SNDINFO was replaced!
|
2008-03-25 16:19:31 +00:00
|
|
|
parse_ss = true;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
else if (LastLocalSndSeq.CompareNoCase(LocalSndSeq) != 0)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
parse_ss = true;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
if (parse_ss)
|
|
|
|
{
|
- Fixed: The players were not added to FS's list of spawned things.
- Update to ZDoom r882
- Added the option to use $ as a prefix to a string table name everywhere in
MAPINFO where 'lookup' could be specified so that there is one consistent
way to do it.
- Externalized all default episode definitions. Added an 'optional' keyword
to handle M4 and 5 in Doom and Heretic.
- Added P_CheckMapData function and replaced all calls to P_OpenMapData that
only checked for a map's presence with it.
- Added Martin Howe's player statusbar face submission.
- Added an 'adddefaultmap' option for MAPINFO. This is the same as 'defaultmap'
but keeps all existing information in the default and just adds to it. This
is needed because Hexen and Strife set some information in their base
MAPINFO and using 'defaultmap' in a PWAD would override that.
- Fixed: Using MAPINFO's f1 option could cause memory leaks.
- Added option to load lumps by full name to several places:
* Finale texts loaded from a text lump
* Demos
* Local SNDINFOs
* Local SNDSEQs
* Image names in FONTDEFS
* intermission script names
- Changed the STCFN121 handling. The character is not an 'I' but a '|' so
instead of discarding it it should be inserted at position 124.
- Renamed indexfont.fon to indexfont so that I could remove a special case
from V_GetFont that was just added for this one font.
- Added a 'dumpspawnedthings' CVAR that enables a listing of all things in
the map and the actor type they spawned.
SBarInfo Update #16
- Added: fillzeros flag for drawnumber. When set the string will always have
a length of the specified size and zeros will fill in for the missing places.
If the number is negative the negative sign will take the place of the last
digit.
- Added: globalarray type to drawnumber which will display the value in a
global array with the index set to the player's number. Untested.
- Added: isselected command to SBarInfo.
- Fixed: Bi and Tri colored numbers didn't work.
- Fixed: Crash when using nullimage as the last image in drawswitchableimage.
- Applied Graf suggestion to include the y coord when calulating heights to fix
most of the gaps caused by round off errors. At least for now anyways and it
is only applied for drawimage.
- SBarInfo inventory bars have been converted to use screen->DrawTexture()
- Increased limit for demon/melee to 4.
- Fixed: P_CheckSwitchRange accessed invalid memory when testing a one-sided
line.
- Fixed: P_SpawnPuff assumed that all melee attacks have the same range
(MELEERANGE) and didn't set the puff to its melee state if the range
was different. Even worse, it checked a global variable for this so
the behavior was undefined when P_SpawnPuff was called from anywhere
else but P_LineAttack. To reduce the amount of parameters I combined
this information with the hitthing and temporary parameters into one
flags parameter. Also changed P_LineAttack so that it gets passed
an additional parameter that specifies whether the attack is a melee
attack or not and set this to true in all calls that are to be considered
melee attacks. I couldn't use the damage type because A_CustomPunch
and A_CustomMeleeAttack allow passing any damage type they want.
- Added a sprite option as an alternative of particles for FX_ROCKET
and FX_GRENADE.
- Fixed: The minimum parameter count for ACS_Execute and ACS_ExecuteAlways for
DECORATE was wrong (2 instead of 1.)
- Changed: Hexen set every cluster to be a hub if it hadn't been defined before
a level using this cluster. Now it will only do that if HexenHack is true,
i.e. when original Hexen format MAPINFOs are parsed. For ZDoom format
MAPINFOs it will now be the same as for the other games which means that
'hub' has to be declared explicitly.
- Added an Idle state that is entered in place of the spawn state if a monster
has to return to its inactive state if it can't find any more targets.
- Added MF5_NOINTERACTION flag which completely disables all physics related
code for any actor with this flag. Mostly useful for particle effects where
the actors just move a certain distance and then disappear.
- Removed the last remains of the antialias precalculation code from
am_map.cpp because it was no longer used.
- Fixed: Two-sided lines bordering a secret sector were not drawn in the
proper color
- Fixed: The automap didn't check ACS_LockedExecuteDoor for its lock color.
- Switched sounds local to the listener from head-relative 3D sounds to 2D
sounds so stereo sounds have full separation. I tried using set3DSpread,
but that still caused some blending of the channels.
- Changed FScanner so that opening a lump gives the complete wad+lump name
rather than a generic one, so identifying errors among files that all have
the same lump name no longer involves any degree of guesswork in
determining exactly which file the error occurred in.
- Added a check to S_ParseSndSeq() for SNDSEQ lumps with unterminated final
sequences.
- Fixed: Parts of s_sndseq.cpp that scan the Sequences array need NULL
pointer checks, in case an improper sequence was encountered during
parsing but not early enough to avoid creating a slot for it in the array.
- Added support for dumping from RAW/DRO/IMF files, so now anything that
can be played as OPL can also be dumped.
- Removed the opl_enable cvar, since OPL playback is now selectable as just
another MIDI device.
- Added support for DRO playback and dual-chip RAW playback.
- Removed MUS support from OPLMUSSong, since using the OPLMIDIDevice with
MUSSong2 works just as well. There are still lots of leftover bits in
the class that should probably be removed at some point, too.
- Added dual-chip dumping support for the RAW format.
- Added DosBox Raw OPL (.DRO) dumping support. For whatever reason,
in_adlib calculates the song length for this format wrong, even though
the exact length is stored right in the header. (But in_adlib seems buggy
in general; too bad it's the only Windows version of Adplug that seems to
exist.)
- Rewrote the OPL dumper to work with MIDI as well as MUS.
git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@86 b0f79afe-0144-0410-b225-9a4edf0717df
2008-04-05 13:28:48 +00:00
|
|
|
S_ParseSndSeq(*LocalSndSeq? Wads.CheckNumForFullName(LocalSndSeq, true) : -1);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
|
|
|
|
LastLocalSndInfo = LocalSndInfo;
|
|
|
|
LastLocalSndSeq = LocalSndSeq;
|
|
|
|
}
|
|
|
|
|
2008-03-01 18:54:31 +00:00
|
|
|
SoundPaused = false;
|
|
|
|
|
|
|
|
// stop the old music if it has been paused.
|
|
|
|
// This ensures that the new music is started from the beginning
|
|
|
|
// if it's the same as the last one and it has been paused.
|
|
|
|
if (MusicPaused) S_StopMusic(true);
|
|
|
|
|
2008-01-27 11:25:03 +00:00
|
|
|
// start new music for the level
|
|
|
|
MusicPaused = 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_GetChannel
|
|
|
|
//
|
|
|
|
// Returns a free channel for the system sound interface.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FSoundChan *S_GetChannel(void *syschan)
|
|
|
|
{
|
|
|
|
FSoundChan *chan;
|
|
|
|
|
|
|
|
if (FreeChannels != NULL)
|
|
|
|
{
|
|
|
|
chan = FreeChannels;
|
|
|
|
S_UnlinkChannel(chan);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
chan = new FSoundChan;
|
|
|
|
memset(chan, 0, sizeof(*chan));
|
|
|
|
}
|
|
|
|
S_LinkChannel(chan, &Channels);
|
|
|
|
chan->SysChannel = syschan;
|
|
|
|
return chan;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_ReturnChannel
|
|
|
|
//
|
|
|
|
// Returns a channel to the free pool.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_ReturnChannel(FSoundChan *chan)
|
|
|
|
{
|
|
|
|
if (chan->Mover != NULL)
|
|
|
|
{
|
|
|
|
chan->Mover->SoundChans &= ~(1 << chan->EntChannel);
|
|
|
|
}
|
|
|
|
S_UnlinkChannel(chan);
|
|
|
|
memset(chan, 0, sizeof(*chan));
|
|
|
|
S_LinkChannel(chan, &FreeChannels);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_UnlinkChannel
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_UnlinkChannel(FSoundChan *chan)
|
|
|
|
{
|
|
|
|
*(chan->PrevChan) = chan->NextChan;
|
|
|
|
if (chan->NextChan != NULL)
|
|
|
|
{
|
|
|
|
chan->NextChan->PrevChan = chan->PrevChan;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_LinkChannel
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_LinkChannel(FSoundChan *chan, FSoundChan **head)
|
|
|
|
{
|
|
|
|
chan->NextChan = *head;
|
|
|
|
if (chan->NextChan != NULL)
|
|
|
|
{
|
|
|
|
chan->NextChan->PrevChan = &chan->NextChan;
|
|
|
|
}
|
|
|
|
*head = chan;
|
|
|
|
chan->PrevChan = head;
|
|
|
|
}
|
|
|
|
|
2008-01-27 11:25:03 +00:00
|
|
|
// [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
|
|
|
|
//
|
2008-03-25 16:19:31 +00:00
|
|
|
// Calculates a sound's position and velocity for 3D sounds.
|
2008-01-27 11:25:03 +00:00
|
|
|
//=========================================================================
|
|
|
|
|
|
|
|
void CalcPosVel (fixed_t *pt, AActor *mover, int constz,
|
|
|
|
float pos[3], float vel[3])
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (mover != NULL)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
|
|
|
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
|
|
|
|
// 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,
|
2008-03-25 16:19:31 +00:00
|
|
|
int sound_id, float volume, float attenuation)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
|
|
|
sfxinfo_t *sfx;
|
|
|
|
int chanflags;
|
2008-03-25 16:19:31 +00:00
|
|
|
int basepriority;
|
2008-01-27 11:25:03 +00:00
|
|
|
int org_id;
|
2008-03-25 16:19:31 +00:00
|
|
|
int pitch;
|
2008-01-27 11:25:03 +00:00
|
|
|
fixed_t x, y, z;
|
2008-03-25 16:19:31 +00:00
|
|
|
FSoundChan *chan;
|
|
|
|
float pos[3];
|
|
|
|
float vel[3];
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
if (sound_id <= 0 || volume <= 0 || GSnd == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
org_id = sound_id;
|
2008-03-25 16:19:31 +00:00
|
|
|
chanflags = channel & ~7;
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
if (pt == NULL)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
attenuation = 0;
|
2008-01-27 11:25:03 +00:00
|
|
|
// Give these variables values, although they don't really matter
|
|
|
|
x = y = z = 0;
|
2008-03-25 16:19:31 +00:00
|
|
|
fixed_t pt2[3] = { 0, 0, 0 };
|
|
|
|
CalcPosVel (pt2, mover, chanflags & CHAN_LISTENERZ, pos, vel);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x = pt[0];
|
|
|
|
y = pt[1];
|
|
|
|
z = pt[2];
|
2008-03-25 16:19:31 +00:00
|
|
|
CalcPosVel (pt, mover, chanflags & CHAN_LISTENERZ, pos, vel);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (chanflags & CHAN_IMMOBILE)
|
|
|
|
{
|
|
|
|
pt = NULL;
|
|
|
|
}
|
|
|
|
if (i_compatflags & COMPATF_MAGICSILENCE)
|
|
|
|
{ // For people who just can't play without a silent BFG.
|
2008-03-25 16:19:31 +00:00
|
|
|
channel = CHAN_WEAPON;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((channel & CHAN_MAYBE_LOCAL) && (i_compatflags & COMPATF_SILENTPICKUP))
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (mover != NULL && mover != players[consoleplayer].camera)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
channel &= 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
sfx = &S_sfx[sound_id];
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
// Scale volume according to SNDINFO data.
|
|
|
|
volume = MIN(volume * sfx->Volume, 1.f);
|
|
|
|
if (volume <= 0)
|
|
|
|
return;
|
|
|
|
|
2008-03-30 08:34:44 +00:00
|
|
|
// When resolving a link we do not want to get the NearLimit of
|
|
|
|
// the referenced sound so some additional checks are required
|
|
|
|
int NearLimit = sfx->NearLimit;
|
|
|
|
|
2008-01-27 11:25:03 +00:00
|
|
|
// Resolve player sounds, random sounds, and aliases
|
|
|
|
while (sfx->link != sfxinfo_t::NO_LINK)
|
|
|
|
{
|
|
|
|
if (sfx->bPlayerReserve)
|
|
|
|
{
|
|
|
|
sound_id = S_FindSkinnedSound (mover, sound_id);
|
2008-03-30 08:34:44 +00:00
|
|
|
NearLimit = sfx[sound_id].NearLimit;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
else if (sfx->bRandomHeader)
|
|
|
|
{
|
|
|
|
sound_id = S_PickReplacement (sound_id);
|
2008-03-30 08:34:44 +00:00
|
|
|
if (NearLimit < 0) NearLimit = sfx[sound_id].NearLimit;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sound_id = sfx->link;
|
2008-03-30 08:34:44 +00:00
|
|
|
if (NearLimit < 0) NearLimit = sfx[sound_id].NearLimit;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
sfx = &S_sfx[sound_id];
|
|
|
|
}
|
2008-03-25 16:19:31 +00:00
|
|
|
|
|
|
|
// If this is a singular sound, don't play it if it's already playing.
|
|
|
|
if (sfx->bSingular && S_CheckSingular(sound_id))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If this sound doesn't like playing near itself, don't play it if
|
|
|
|
// that's what would happen.
|
- Fixed: The players were not added to FS's list of spawned things.
- Update to ZDoom r882
- Added the option to use $ as a prefix to a string table name everywhere in
MAPINFO where 'lookup' could be specified so that there is one consistent
way to do it.
- Externalized all default episode definitions. Added an 'optional' keyword
to handle M4 and 5 in Doom and Heretic.
- Added P_CheckMapData function and replaced all calls to P_OpenMapData that
only checked for a map's presence with it.
- Added Martin Howe's player statusbar face submission.
- Added an 'adddefaultmap' option for MAPINFO. This is the same as 'defaultmap'
but keeps all existing information in the default and just adds to it. This
is needed because Hexen and Strife set some information in their base
MAPINFO and using 'defaultmap' in a PWAD would override that.
- Fixed: Using MAPINFO's f1 option could cause memory leaks.
- Added option to load lumps by full name to several places:
* Finale texts loaded from a text lump
* Demos
* Local SNDINFOs
* Local SNDSEQs
* Image names in FONTDEFS
* intermission script names
- Changed the STCFN121 handling. The character is not an 'I' but a '|' so
instead of discarding it it should be inserted at position 124.
- Renamed indexfont.fon to indexfont so that I could remove a special case
from V_GetFont that was just added for this one font.
- Added a 'dumpspawnedthings' CVAR that enables a listing of all things in
the map and the actor type they spawned.
SBarInfo Update #16
- Added: fillzeros flag for drawnumber. When set the string will always have
a length of the specified size and zeros will fill in for the missing places.
If the number is negative the negative sign will take the place of the last
digit.
- Added: globalarray type to drawnumber which will display the value in a
global array with the index set to the player's number. Untested.
- Added: isselected command to SBarInfo.
- Fixed: Bi and Tri colored numbers didn't work.
- Fixed: Crash when using nullimage as the last image in drawswitchableimage.
- Applied Graf suggestion to include the y coord when calulating heights to fix
most of the gaps caused by round off errors. At least for now anyways and it
is only applied for drawimage.
- SBarInfo inventory bars have been converted to use screen->DrawTexture()
- Increased limit for demon/melee to 4.
- Fixed: P_CheckSwitchRange accessed invalid memory when testing a one-sided
line.
- Fixed: P_SpawnPuff assumed that all melee attacks have the same range
(MELEERANGE) and didn't set the puff to its melee state if the range
was different. Even worse, it checked a global variable for this so
the behavior was undefined when P_SpawnPuff was called from anywhere
else but P_LineAttack. To reduce the amount of parameters I combined
this information with the hitthing and temporary parameters into one
flags parameter. Also changed P_LineAttack so that it gets passed
an additional parameter that specifies whether the attack is a melee
attack or not and set this to true in all calls that are to be considered
melee attacks. I couldn't use the damage type because A_CustomPunch
and A_CustomMeleeAttack allow passing any damage type they want.
- Added a sprite option as an alternative of particles for FX_ROCKET
and FX_GRENADE.
- Fixed: The minimum parameter count for ACS_Execute and ACS_ExecuteAlways for
DECORATE was wrong (2 instead of 1.)
- Changed: Hexen set every cluster to be a hub if it hadn't been defined before
a level using this cluster. Now it will only do that if HexenHack is true,
i.e. when original Hexen format MAPINFOs are parsed. For ZDoom format
MAPINFOs it will now be the same as for the other games which means that
'hub' has to be declared explicitly.
- Added an Idle state that is entered in place of the spawn state if a monster
has to return to its inactive state if it can't find any more targets.
- Added MF5_NOINTERACTION flag which completely disables all physics related
code for any actor with this flag. Mostly useful for particle effects where
the actors just move a certain distance and then disappear.
- Removed the last remains of the antialias precalculation code from
am_map.cpp because it was no longer used.
- Fixed: Two-sided lines bordering a secret sector were not drawn in the
proper color
- Fixed: The automap didn't check ACS_LockedExecuteDoor for its lock color.
- Switched sounds local to the listener from head-relative 3D sounds to 2D
sounds so stereo sounds have full separation. I tried using set3DSpread,
but that still caused some blending of the channels.
- Changed FScanner so that opening a lump gives the complete wad+lump name
rather than a generic one, so identifying errors among files that all have
the same lump name no longer involves any degree of guesswork in
determining exactly which file the error occurred in.
- Added a check to S_ParseSndSeq() for SNDSEQ lumps with unterminated final
sequences.
- Fixed: Parts of s_sndseq.cpp that scan the Sequences array need NULL
pointer checks, in case an improper sequence was encountered during
parsing but not early enough to avoid creating a slot for it in the array.
- Added support for dumping from RAW/DRO/IMF files, so now anything that
can be played as OPL can also be dumped.
- Removed the opl_enable cvar, since OPL playback is now selectable as just
another MIDI device.
- Added support for DRO playback and dual-chip RAW playback.
- Removed MUS support from OPLMUSSong, since using the OPLMIDIDevice with
MUSSong2 works just as well. There are still lots of leftover bits in
the class that should probably be removed at some point, too.
- Added dual-chip dumping support for the RAW format.
- Added DosBox Raw OPL (.DRO) dumping support. For whatever reason,
in_adlib calculates the song length for this format wrong, even though
the exact length is stored right in the header. (But in_adlib seems buggy
in general; too bad it's the only Windows version of Adplug that seems to
exist.)
- Rewrote the OPL dumper to work with MIDI as well as MUS.
git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@86 b0f79afe-0144-0410-b225-9a4edf0717df
2008-04-05 13:28:48 +00:00
|
|
|
if (NearLimit > 0 && pt != NULL && mover != players[consoleplayer].camera &&
|
|
|
|
S_CheckSoundLimit(sfx, pos, NearLimit))
|
2008-03-25 16:19:31 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Make sure the sound is loaded.
|
|
|
|
if (sfx->data == NULL)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
|
|
|
GSnd->LoadSound (sfx);
|
|
|
|
if (sfx->link != sfxinfo_t::NO_LINK)
|
|
|
|
{
|
|
|
|
sfx = &S_sfx[sfx->link];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
// The empty sound never plays.
|
2008-01-27 11:25:03 +00:00
|
|
|
if (sfx->lumpnum == sfx_empty)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2008-03-25 16:19:31 +00:00
|
|
|
|
|
|
|
// Select priority.
|
2008-03-23 21:23:26 +00:00
|
|
|
if (attenuation <= 0 || mover == players[consoleplayer].camera)
|
|
|
|
{
|
|
|
|
basepriority = 40;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
basepriority = 0;
|
|
|
|
}
|
2008-01-27 11:25:03 +00:00
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
if (mover != NULL && channel == CHAN_AUTO)
|
|
|
|
{ // Select a channel that isn't already playing something.
|
|
|
|
BYTE mask = mover->SoundChans;
|
2008-01-27 11:25:03 +00:00
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
// Try channel 0 first, then travel from channel 7 down.
|
|
|
|
if ((mask & 1) == 0)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
channel = 0;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
2008-03-25 16:19:31 +00:00
|
|
|
else
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
for (channel = 7; channel > 0; --channel, mask <<= 1)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if ((mask & 0x80) == 0)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-03-25 16:19:31 +00:00
|
|
|
if (channel == 0)
|
|
|
|
{ // Crap. No free channels.
|
|
|
|
return;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
// If this actor is already playing something on the selected channel, stop it.
|
|
|
|
if ((mover == NULL && channel != CHAN_AUTO) || (mover != NULL && mover->SoundChans & (1 << channel)))
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
for (chan = Channels; chan != NULL; chan = chan->NextChan)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (((mover != NULL && chan->Mover == mover) || (chan->Pt == pt)) && chan->EntChannel == channel)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
GSnd->StopSound(chan);
|
|
|
|
break;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
// Vary the sfx pitches.
|
2008-01-27 11:25:03 +00:00
|
|
|
if (sfx->PitchMask != 0)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
pitch = NORM_PITCH - (M_Random()&sfx->PitchMask) + (M_Random()&sfx->PitchMask);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
pitch = NORM_PITCH;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
if (attenuation > 0)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
chan = GSnd->StartSound3D (sfx, volume, attenuation, pitch, basepriority, pos, vel, chanflags);
|
|
|
|
chanflags |= CHAN_IS3D;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
chan = GSnd->StartSound (sfx, volume, pitch, chanflags);
|
|
|
|
chanflags |= CHAN_LISTENERZ;
|
|
|
|
}
|
|
|
|
if (chan != NULL)
|
|
|
|
{
|
|
|
|
chan->SoundID = sound_id;
|
|
|
|
chan->OrgID = org_id;
|
|
|
|
chan->Mover = mover;
|
|
|
|
chan->Pt = pt != NULL ? pt : &chan->X;
|
|
|
|
chan->SfxInfo = sfx;
|
|
|
|
chan->EntChannel = channel;
|
|
|
|
chan->Volume = volume;
|
|
|
|
chan->X = x;
|
|
|
|
chan->Y = y;
|
|
|
|
chan->Z = z;
|
2008-04-20 10:26:25 +00:00
|
|
|
chan->ChanFlags |= chanflags;
|
2008-03-25 16:19:31 +00:00
|
|
|
if (mover != NULL)
|
|
|
|
{
|
|
|
|
mover->SoundChans |= 1 << channel;
|
|
|
|
}
|
|
|
|
}
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_SoundID
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_SoundID (int channel, int sound_id, float volume, int attenuation)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
S_StartSound ((fixed_t *)NULL, NULL, channel, sound_id, volume, SELECT_ATTEN(attenuation));
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void S_SoundID (AActor *ent, int channel, int sound_id, float volume, int attenuation)
|
|
|
|
{
|
|
|
|
if (ent->Sector->MoreFlags & SECF_SILENT)
|
|
|
|
return;
|
2008-03-25 16:19:31 +00:00
|
|
|
S_StartSound (&ent->x, ent, channel, sound_id, volume, SELECT_ATTEN(attenuation));
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void S_SoundID (fixed_t *pt, int channel, int sound_id, float volume, int attenuation)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
S_StartSound (pt, NULL, channel, sound_id, volume, SELECT_ATTEN(attenuation));
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2008-03-25 16:19:31 +00:00
|
|
|
S_StartSound (pt, NULL, channel|CHAN_IMMOBILE, sound_id, volume, SELECT_ATTEN(attenuation));
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_StartNamedSound
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_StartNamedSound (AActor *ent, fixed_t *pt, int channel,
|
2008-03-25 16:19:31 +00:00
|
|
|
const char *name, float volume, float attenuation)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
|
|
|
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)
|
2008-03-25 16:19:31 +00:00
|
|
|
S_StartSound (&ent->x, ent, channel, sfx_id, volume, attenuation);
|
2008-01-27 11:25:03 +00:00
|
|
|
else
|
2008-03-25 16:19:31 +00:00
|
|
|
S_StartSound (pt, NULL, channel, sfx_id, volume, attenuation);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_Sound
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_Sound (int channel, const char *name, float volume, int attenuation)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
S_StartNamedSound ((AActor *)NULL, NULL, channel, name, volume, SELECT_ATTEN(attenuation));
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void S_Sound (AActor *ent, int channel, const char *name, float volume, int attenuation)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
S_StartNamedSound (ent, NULL, channel, name, volume, SELECT_ATTEN(attenuation));
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void S_Sound (fixed_t *pt, int channel, const char *name, float volume, int attenuation)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
S_StartNamedSound (NULL, pt, channel, name, volume, SELECT_ATTEN(attenuation));
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2008-03-25 16:19:31 +00:00
|
|
|
name, volume, SELECT_ATTEN(attenuation));
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
2008-03-25 16:19:31 +00:00
|
|
|
// S_CheckSingular
|
|
|
|
//
|
|
|
|
// Returns true if a copy of this sound is already playing.
|
2008-01-27 11:25:03 +00:00
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
bool S_CheckSingular(int sound_id)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
|
|
|
|
{
|
|
|
|
if (chan->OrgID == sound_id)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
2008-03-25 16:19:31 +00:00
|
|
|
// S_CheckSoundLimit
|
|
|
|
//
|
|
|
|
// Limits the number of nearby copies of a sound that can play near
|
|
|
|
// each other. If there are NearLimit instances of this sound already
|
|
|
|
// playing within 256 units of the new sound, the new sound will not
|
|
|
|
// start.
|
2008-01-27 11:25:03 +00:00
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2008-03-30 08:34:44 +00:00
|
|
|
bool S_CheckSoundLimit(sfxinfo_t *sfx, float pos[3], int NearLimit)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
FSoundChan *chan;
|
|
|
|
int count;
|
|
|
|
|
2008-03-30 08:34:44 +00:00
|
|
|
for (chan = Channels, count = 0; chan != NULL && count < NearLimit; chan = chan->NextChan)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (chan->SfxInfo == sfx)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
double dx = FIXED2FLOAT(chan->Pt[0]) - pos[0];
|
|
|
|
double dy = FIXED2FLOAT(chan->Pt[1]) - pos[2];
|
|
|
|
double dz = FIXED2FLOAT(chan->Pt[2]) - pos[1];
|
|
|
|
if (dx*dx + dy*dy + dz*dz <= 256.0*256.0)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
count++;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-03-30 08:34:44 +00:00
|
|
|
return count >= NearLimit;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_StopSound
|
|
|
|
//
|
|
|
|
// Stops a sound from a single source playing on a specific channel.
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_StopSound (fixed_t *pt, int channel)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (((pt == NULL && chan->Pt == &chan->X) || chan->Pt == pt) &&
|
|
|
|
((i_compatflags & COMPATF_MAGICSILENCE) || chan->EntChannel == channel))
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
GSnd->StopSound(chan);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void S_StopSound (AActor *ent, int channel)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
// No need to search every channel if we know it's not playing anything.
|
|
|
|
if (ent != NULL && ent->SoundChans & (1 << channel))
|
|
|
|
{
|
|
|
|
S_StopSound (&ent->x, channel);
|
|
|
|
}
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_StopAllChannels
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_StopAllChannels ()
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (GSnd == NULL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SN_StopAllSequences();
|
|
|
|
while (Channels != NULL)
|
|
|
|
{
|
|
|
|
GSnd->StopSound(Channels);
|
|
|
|
}
|
|
|
|
GSnd->UpdateSounds();
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// 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)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (from == NULL || GSnd == NULL)
|
2008-01-27 11:25:03 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
fixed_t *frompt = &from->x;
|
|
|
|
fixed_t *topt = to ? &to->x : NULL;
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (chan->Pt == frompt)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (to != NULL || !(chan->ChanFlags & CHAN_LOOP))
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
chan->Pt = topt ? topt : &chan->X;
|
|
|
|
chan->X = frompt[0];
|
|
|
|
chan->Y = frompt[1];
|
|
|
|
chan->Z = frompt[2];
|
|
|
|
chan->Mover = to;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
GSnd->StopSound(chan);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_GetSoundPlayingInfo
|
|
|
|
//
|
|
|
|
// Is a sound being played by a specific actor/point?
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool S_GetSoundPlayingInfo (fixed_t *pt, int sound_id)
|
|
|
|
{
|
|
|
|
if (sound_id > 0)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (chan->Pt == pt && chan->OrgID == sound_id)
|
2008-01-27 11:25:03 +00:00
|
|
|
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 sound_id)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (actor->SoundChans == 0)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
return false;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
if (i_compatflags & COMPATF_MAGICSILENCE)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
channel = 0;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (chan->Pt == &actor->x)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (channel == 0 || chan->EntChannel == channel)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
return sound_id <= 0 || chan->OrgID == sound_id;
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
float pos[3], vel[3];
|
2008-03-09 16:01:55 +00:00
|
|
|
|
|
|
|
I_UpdateMusic();
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
if (GSnd == NULL)
|
|
|
|
return;
|
|
|
|
|
2008-05-01 21:45:22 +00:00
|
|
|
// [RH] Update music and/or playlist. I_QrySongPlaying() must be called
|
|
|
|
// to attempt to reconnect to broken net streams and to advance the
|
|
|
|
// playlist when the current song finishes.
|
|
|
|
if (mus_playing.handle != NULL &&
|
|
|
|
!I_QrySongPlaying(mus_playing.handle) &&
|
|
|
|
PlayList)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
PlayList->Advance();
|
|
|
|
S_ActivatePlayList(false);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
if (chan->ChanFlags & CHAN_IS3D)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-03-25 16:19:31 +00:00
|
|
|
CalcPosVel(chan->Pt, chan->Mover, chan->ChanFlags & CHAN_LISTENERZ, pos, vel);
|
|
|
|
GSnd->UpdateSoundParams3D(chan, pos, vel);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
SN_UpdateActiveSequences();
|
2008-01-27 11:25:03 +00:00
|
|
|
|
2008-03-25 16:19:31 +00:00
|
|
|
GSnd->UpdateListener();
|
|
|
|
GSnd->UpdateSounds();
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// allow specifying "*" as a placeholder to play the level's default music.
|
|
|
|
if (musicname != NULL && !strcmp(musicname, "*"))
|
|
|
|
{
|
|
|
|
if (gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL)
|
|
|
|
{
|
|
|
|
musicname = level.music;
|
|
|
|
order = level.musicorder;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
musicname = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
- Update to ZDoom r858:
- Added FMOD_OPENONLY to the callback version of CreateStream() to prevent it
from doing prebuffering of the song. This was causing the Linux version to
hang while waiting for input from the pipe, since Timidity hadn't been
started yet. I tried using a select call in the FillStream() method, but it
always seems to return the pipe as having nothing available. Unfortunately,
the game still falls all over itself if Timidity isn't available. Instead
of execvp failing nicely, X errors kill the game. I don't know why it's
doing that. My advice for Linux music: Skip Timidity++ and get a DLS patch
set (/WINDOWS/system32/drivers/gm.dls is probably the most common by far)
and set the snd_midipatchset cvar to point to it. It's faster and also
sounds a whole lot better than the crappy freepats Ubuntu wants to install
with Timidity++ (thank goodness I have the official patches from a real
GUS so I don't need to use them).
- GCC fixes.
- Fixed: After starting new music the music volume has to be reset so that
the song's relative volume takes effect.
- Removed the arbitrary 1024 bytes limit when the file being played is a MIDI
file. I had a D_DM2TTL that's only 990 bytes.
- Restructured I_RegisterSong so that $mididevice works again and also supports
selecting FMOD.
- Added Jim' Linux fix.
- Added MartinHowe's fix for mugshot display in status bars.
- The garbage collector is now run one last time just before exiting the game.
- Removed movie volume from the sound menu and renamed some of the other
options to give the MIDI device name more room to display itself.
- Moved the midi device selection into the main sound menu.
- Added FMOD as MIDI device -1, to replace the MIDI mapper. This is still the
default device. By default, it uses exactly the same DLS instruments as the
Microsoft GS Wavetable Synth. If you have another set DLS level 1 patch set
you want to use, set the snd_midipatchset cvar to specify where it should
load the instruments from.
- Changed the ProduceMIDI function to store its output into a TArray<BYTE>.
An overloaded version wraps around it to continue to supply file-writing
support for external Timidity++ usage.
- Added an FMOD credits banner to comply with their non-commercial license.
- Reimplemented the snd_buffersize cvar for the FMOD Ex sound system. Rather
than a time in ms, this is now the length in samples of the DSP buffer.
Also added the snd_buffercount cvar to offer complete control over the
call to FMOD::System::setDSPBufferSize(). Note that with any snd_samplerate
below about 44kHz, you will need to set snd_buffersize to avoid long
latencies.
- Reimplemented the snd_output cvar for the FMOD Ex sound system.
- Changed snd_samplerate default to 0. This now means to use the default
sample rate.
- Made snd_output, snd_output_format, snd_speakermode, snd_resampler, and
snd_hrtf available through the menu.
- Split the HRTF effect selection into its own cvar: snd_hrtf.
- Removed 96000 Hz option from the menu. It's still available through the
cvar, if desired.
- Fixed: If Windows sound init failed, retry with DirectSound. (Apparently,
WASAPI doesn't work with more than two speakers and PCM-Float output at the
same time.)
- Fixed: Area sounds only played from the front speakers once you got within
the 2D panning area.
git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@79 b0f79afe-0144-0410-b225-9a4edf0717df
2008-03-27 18:31:46 +00:00
|
|
|
int device = MDEV_DEFAULT;
|
2008-05-01 21:45:22 +00:00
|
|
|
void *handle = NULL;
|
2008-01-27 11:25:03 +00:00
|
|
|
|
- Update to ZDoom r858:
- Added FMOD_OPENONLY to the callback version of CreateStream() to prevent it
from doing prebuffering of the song. This was causing the Linux version to
hang while waiting for input from the pipe, since Timidity hadn't been
started yet. I tried using a select call in the FillStream() method, but it
always seems to return the pipe as having nothing available. Unfortunately,
the game still falls all over itself if Timidity isn't available. Instead
of execvp failing nicely, X errors kill the game. I don't know why it's
doing that. My advice for Linux music: Skip Timidity++ and get a DLS patch
set (/WINDOWS/system32/drivers/gm.dls is probably the most common by far)
and set the snd_midipatchset cvar to point to it. It's faster and also
sounds a whole lot better than the crappy freepats Ubuntu wants to install
with Timidity++ (thank goodness I have the official patches from a real
GUS so I don't need to use them).
- GCC fixes.
- Fixed: After starting new music the music volume has to be reset so that
the song's relative volume takes effect.
- Removed the arbitrary 1024 bytes limit when the file being played is a MIDI
file. I had a D_DM2TTL that's only 990 bytes.
- Restructured I_RegisterSong so that $mididevice works again and also supports
selecting FMOD.
- Added Jim' Linux fix.
- Added MartinHowe's fix for mugshot display in status bars.
- The garbage collector is now run one last time just before exiting the game.
- Removed movie volume from the sound menu and renamed some of the other
options to give the MIDI device name more room to display itself.
- Moved the midi device selection into the main sound menu.
- Added FMOD as MIDI device -1, to replace the MIDI mapper. This is still the
default device. By default, it uses exactly the same DLS instruments as the
Microsoft GS Wavetable Synth. If you have another set DLS level 1 patch set
you want to use, set the snd_midipatchset cvar to specify where it should
load the instruments from.
- Changed the ProduceMIDI function to store its output into a TArray<BYTE>.
An overloaded version wraps around it to continue to supply file-writing
support for external Timidity++ usage.
- Added an FMOD credits banner to comply with their non-commercial license.
- Reimplemented the snd_buffersize cvar for the FMOD Ex sound system. Rather
than a time in ms, this is now the length in samples of the DSP buffer.
Also added the snd_buffercount cvar to offer complete control over the
call to FMOD::System::setDSPBufferSize(). Note that with any snd_samplerate
below about 44kHz, you will need to set snd_buffersize to avoid long
latencies.
- Reimplemented the snd_output cvar for the FMOD Ex sound system.
- Changed snd_samplerate default to 0. This now means to use the default
sample rate.
- Made snd_output, snd_output_format, snd_speakermode, snd_resampler, and
snd_hrtf available through the menu.
- Split the HRTF effect selection into its own cvar: snd_hrtf.
- Removed 96000 Hz option from the menu. It's still available through the
cvar, if desired.
- Fixed: If Windows sound init failed, retry with DirectSound. (Apparently,
WASAPI doesn't work with more than two speakers and PCM-Float output at the
same time.)
- Fixed: Area sounds only played from the front speakers once you got within
the 2D panning area.
git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@79 b0f79afe-0144-0410-b225-9a4edf0717df
2008-03-27 18:31:46 +00:00
|
|
|
int *devp = MidiDevices.CheckKey(FName(musicname));
|
2008-01-27 11:25:03 +00:00
|
|
|
if (devp != NULL) device = *devp;
|
|
|
|
|
2008-05-01 21:45:22 +00:00
|
|
|
// Strip off any leading file:// component.
|
|
|
|
if (strncmp(musicname, "file://", 7) == 0)
|
|
|
|
{
|
|
|
|
musicname += 7;
|
|
|
|
}
|
|
|
|
|
2008-01-27 11:25:03 +00:00
|
|
|
if (!FileExists (musicname))
|
|
|
|
{
|
2008-03-30 08:34:44 +00:00
|
|
|
if ((lumpnum = Wads.CheckNumForFullName (musicname, true, ns_music)) == -1)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-05-01 21:45:22 +00:00
|
|
|
if (strstr(musicname, "://") > musicname)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-05-01 21:45:22 +00:00
|
|
|
// Looks like a URL; try it as such.
|
|
|
|
handle = I_RegisterURLSong(musicname);
|
|
|
|
if (handle == NULL)
|
|
|
|
{
|
|
|
|
Printf ("Could not open \"%s\"\n", musicname);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Printf ("Music \"%s\" not found\n", musicname);
|
2008-01-27 11:25:03 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2008-05-01 21:45:22 +00:00
|
|
|
if (handle == NULL)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
2008-05-01 21:45:22 +00:00
|
|
|
if (!Wads.IsUncompressedFile(lumpnum))
|
2008-03-25 16:19:31 +00:00
|
|
|
{
|
2008-05-01 21:45:22 +00:00
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
musiccache.Resize(length);
|
|
|
|
Wads.ReadLump(lumpnum, &musiccache[0]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
offset = Wads.GetLumpOffset (lumpnum);
|
|
|
|
length = Wads.LumpLength (lumpnum);
|
|
|
|
if (length == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-03-25 16:19:31 +00:00
|
|
|
}
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
offset = 0;
|
|
|
|
length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// shutdown old music
|
|
|
|
S_StopMusic (true);
|
|
|
|
|
2008-03-28 09:56:50 +00:00
|
|
|
// Just record it if volume is 0
|
|
|
|
if (snd_musicvolume <= 0)
|
|
|
|
{
|
|
|
|
mus_playing.loop = looping;
|
|
|
|
mus_playing.name = "";
|
|
|
|
mus_playing.baseorder = 0;
|
|
|
|
LastSong = musicname;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-01-27 11:25:03 +00:00
|
|
|
// load & register it
|
2008-05-01 21:45:22 +00:00
|
|
|
if (handle != NULL)
|
|
|
|
{
|
|
|
|
mus_playing.handle = handle;
|
|
|
|
}
|
|
|
|
else if (offset != -1)
|
2008-01-27 11:25:03 +00:00
|
|
|
{
|
|
|
|
mus_playing.handle = I_RegisterSong (lumpnum != -1 ?
|
|
|
|
Wads.GetWadFullName (Wads.GetLumpFile (lumpnum)) :
|
|
|
|
musicname, NULL, offset, length, device);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mus_playing.handle = I_RegisterSong (NULL, &musiccache[0], -1, length, device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mus_playing.loop = looping;
|
2008-03-28 09:56:50 +00:00
|
|
|
mus_playing.name = musicname;
|
|
|
|
LastSong = "";
|
2008-01-27 11:25:03 +00:00
|
|
|
|
|
|
|
if (mus_playing.handle != 0)
|
|
|
|
{ // play it
|
|
|
|
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())
|
|
|
|
{
|
2008-03-28 09:56:50 +00:00
|
|
|
FString song = LastSong;
|
2008-01-27 11:25:03 +00:00
|
|
|
LastSong = "";
|
2008-03-28 09:56:50 +00:00
|
|
|
S_ChangeMusic (song, mus_playing.baseorder, mus_playing.loop, true);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// 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]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|