2006-02-24 04:48:15 +00:00
|
|
|
/*
|
|
|
|
** i_sound.cpp
|
|
|
|
** System interface for sound; uses fmod.dll
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright 1998-2005 Randy Heit
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <windows.h>
|
|
|
|
#include <mmsystem.h>
|
|
|
|
#include "resource.h"
|
|
|
|
extern HWND Window;
|
|
|
|
#else
|
|
|
|
#define FALSE 0
|
|
|
|
#define TRUE 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "templates.h"
|
|
|
|
#include "fmodsound.h"
|
|
|
|
#include "c_cvars.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "gi.h"
|
|
|
|
#include "actor.h"
|
|
|
|
#include "r_state.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
#include "sample_flac.h"
|
|
|
|
#include "i_music.h"
|
|
|
|
#include "i_musicinterns.h"
|
|
|
|
|
|
|
|
// killough 2/21/98: optionally use varying pitched sounds
|
|
|
|
#define PITCH(f,x) (snd_pitched ? ((f)*(x))/128 : (f))
|
|
|
|
|
|
|
|
extern int MAX_SND_DIST;
|
|
|
|
const int S_CLIPPING_DIST = 1200;
|
|
|
|
const int S_CLOSE_DIST = 160;
|
|
|
|
|
|
|
|
CVAR (Int, snd_driver, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
CVAR (Bool, snd_3d, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
CVAR (Bool, snd_waterreverb, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
CVAR (Bool, snd_fpumixer, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
|
|
|
|
EXTERN_CVAR (String, snd_output)
|
|
|
|
EXTERN_CVAR (Float, snd_musicvolume)
|
|
|
|
EXTERN_CVAR (Int, snd_buffersize)
|
|
|
|
EXTERN_CVAR (Int, snd_samplerate)
|
|
|
|
EXTERN_CVAR (Bool, snd_pitched)
|
|
|
|
|
|
|
|
static const ReverbContainer *PrevEnvironment;
|
|
|
|
ReverbContainer *ForcedEnvironment;
|
|
|
|
|
|
|
|
static const char *OutputNames[] =
|
|
|
|
{
|
|
|
|
"No sound",
|
|
|
|
"Windows Multimedia",
|
|
|
|
"DirectSound",
|
|
|
|
"A3D",
|
|
|
|
"OSS (Open Sound System)",
|
|
|
|
"ESD (Enlightenment Sound Daemon)",
|
|
|
|
"ALSA (Advanced Linux Sound Architecture)"
|
|
|
|
};
|
|
|
|
static const char *MixerNames[] =
|
|
|
|
{
|
|
|
|
"Auto",
|
|
|
|
"Non-MMX blendmode",
|
|
|
|
"Pentium MMX",
|
|
|
|
"PPro MMX",
|
|
|
|
"Quality auto",
|
|
|
|
"Quality FPU",
|
|
|
|
"Quality Pentium MMX",
|
|
|
|
"Quality PPro MMX"
|
|
|
|
};
|
|
|
|
|
|
|
|
static char FModLog (char success)
|
|
|
|
{
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
Printf (" succeeded\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Printf (" failed (error %d)\n", FSOUND_GetError());
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// The container for a FSOUND_STREAM.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
class FMODStreamCapsule : public SoundStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
FMODStreamCapsule (FSOUND_STREAM *stream)
|
|
|
|
: Stream(stream), UserData(NULL), Callback(NULL), Channel(-1) {}
|
|
|
|
|
|
|
|
FMODStreamCapsule (void *udata, SoundStreamCallback callback)
|
|
|
|
: Stream(NULL), UserData(udata), Callback(callback), Channel(-1) {}
|
|
|
|
|
|
|
|
~FMODStreamCapsule ()
|
|
|
|
{
|
|
|
|
if (Stream != NULL) FSOUND_Stream_Close (Stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetStream (FSOUND_STREAM *stream)
|
|
|
|
{
|
|
|
|
Stream = stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Play (float volume)
|
|
|
|
{
|
|
|
|
Channel = FSOUND_Stream_PlayEx (FSOUND_FREE, Stream, NULL, true);
|
|
|
|
if (Channel != -1)
|
|
|
|
{
|
|
|
|
FSOUND_SetVolumeAbsolute (Channel, clamp<int>((int)(volume * relative_volume * 255), 0, 255));
|
|
|
|
FSOUND_SetPan (Channel, FSOUND_STEREOPAN);
|
|
|
|
FSOUND_SetPaused (Channel, false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Stop ()
|
|
|
|
{
|
|
|
|
FSOUND_Stream_Stop (Stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetPaused (bool paused)
|
|
|
|
{
|
|
|
|
if (Channel != -1)
|
|
|
|
{
|
|
|
|
return !!FSOUND_SetPaused (Channel, paused);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int GetPosition ()
|
|
|
|
{
|
|
|
|
return FSOUND_Stream_GetPosition (Stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetVolume (float volume)
|
|
|
|
{
|
|
|
|
if (Channel != -1)
|
|
|
|
{
|
|
|
|
FSOUND_SetVolumeAbsolute (Channel, clamp<int>((int)(volume * relative_volume * 255), 0, 255));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static signed char F_CALLBACKAPI MyCallback (FSOUND_STREAM *stream, void *buff, int len, void *userdata)
|
|
|
|
{
|
|
|
|
FMODStreamCapsule *me = (FMODStreamCapsule *)userdata;
|
|
|
|
if (me->Callback == NULL)
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return me->Callback (me, buff, len, me->UserData);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
FSOUND_STREAM *Stream;
|
|
|
|
void *UserData;
|
|
|
|
SoundStreamCallback Callback;
|
|
|
|
int Channel;
|
|
|
|
};
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// The container for a FMUSIC_MODULE.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
class FMUSICCapsule : public SoundTrackerModule
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
FMUSICCapsule (FMUSIC_MODULE *mod) : Module (mod) {}
|
|
|
|
~FMUSICCapsule () { FMUSIC_FreeSong (Module); }
|
|
|
|
|
|
|
|
bool Play ()
|
|
|
|
{
|
|
|
|
return !!FMUSIC_PlaySong (Module);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Stop ()
|
|
|
|
{
|
|
|
|
FMUSIC_StopSong (Module);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetVolume (float volume)
|
|
|
|
{
|
|
|
|
FMUSIC_SetMasterVolume (Module, clamp<int>((int)(volume * relative_volume * 256), 0, 256));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetPaused (bool paused)
|
|
|
|
{
|
|
|
|
return !!FMUSIC_SetPaused (Module, paused);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsPlaying ()
|
|
|
|
{
|
|
|
|
return !!FMUSIC_IsPlaying (Module);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsFinished ()
|
|
|
|
{
|
|
|
|
return !!FMUSIC_IsFinished (Module);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetOrder (int order)
|
|
|
|
{
|
|
|
|
return !!FMUSIC_SetOrder (Module, order);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
FMUSIC_MODULE *Module;
|
|
|
|
};
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// The interface the game uses to talk to FMOD.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
FMODSoundRenderer::FMODSoundRenderer ()
|
|
|
|
{
|
|
|
|
DidInit = Init ();
|
|
|
|
}
|
|
|
|
|
|
|
|
FMODSoundRenderer::~FMODSoundRenderer ()
|
|
|
|
{
|
|
|
|
Shutdown ();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FMODSoundRenderer::IsValid ()
|
|
|
|
{
|
|
|
|
return DidInit;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FMODSoundRenderer::Init ()
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
static const FSOUND_OUTPUTTYPES outtypes[2] =
|
|
|
|
{ FSOUND_OUTPUT_DSOUND, FSOUND_OUTPUT_WINMM };
|
|
|
|
const int maxtrynum = 2;
|
|
|
|
#else
|
|
|
|
static const FSOUND_OUTPUTTYPES outtypes[3] =
|
|
|
|
{ FSOUND_OUTPUT_OSS, FSOUND_OUTPUT_ALSA, FSOUND_OUTPUT_ESD };
|
|
|
|
const int maxtrynum = 3;
|
|
|
|
#endif
|
|
|
|
#if 0
|
|
|
|
bool trya3d = false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int outindex;
|
|
|
|
int trynum;
|
|
|
|
bool nosound = false;
|
|
|
|
|
|
|
|
ChannelMap = NULL;
|
|
|
|
PrevEnvironment = DefaultEnvironments[0];
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (stricmp (snd_output, "dsound") == 0 || stricmp (snd_output, "directsound") == 0)
|
|
|
|
{
|
|
|
|
outindex = 0;
|
|
|
|
}
|
|
|
|
else if (stricmp (snd_output, "winmm") == 0 || stricmp (snd_output, "waveout") == 0)
|
|
|
|
{
|
|
|
|
outindex = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If snd_3d is true, try for a3d output if snd_output was not recognized above.
|
|
|
|
// However, if running under NT 4.0, a3d will only be tried if specifically requested.
|
|
|
|
outindex = (OSPlatform == os_WinNT) ? 1 : 0;
|
|
|
|
#if 0
|
|
|
|
// FMOD 3.6 no longer supports a3d. Keep this code here in case support comes back.
|
|
|
|
if (stricmp (snd_output, "a3d") == 0 || (outindex == 0 && snd_3d))
|
|
|
|
{
|
|
|
|
trya3d = true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (stricmp (snd_output, "oss") == 0)
|
|
|
|
{
|
|
|
|
outindex = 0;
|
|
|
|
}
|
|
|
|
else if (stricmp (snd_output, "esd") == 0 ||
|
|
|
|
stricmp (snd_output, "esound") == 0)
|
|
|
|
{
|
|
|
|
outindex = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
outindex = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Printf ("I_InitSound: Initializing FMOD\n");
|
|
|
|
#ifdef _WIN32
|
|
|
|
FSOUND_SetHWND (Window);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (snd_fpumixer)
|
|
|
|
{
|
|
|
|
FSOUND_SetMixer (FSOUND_MIXER_QUALITY_FPU);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FSOUND_SetMixer (FSOUND_MIXER_AUTODETECT);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (OSPlatform == os_WinNT)
|
|
|
|
{
|
|
|
|
// If running Windows NT 4, we need to initialize DirectSound before
|
|
|
|
// using WinMM. If we don't, then FSOUND_Close will corrupt a
|
|
|
|
// heap. This might just be the Audigy's drivers--I don't know why
|
|
|
|
// it happens. At least the fix is simple enough. I only need to
|
|
|
|
// initialize DirectSound once, and then I can initialize/close
|
|
|
|
// WinMM as many times as I want.
|
|
|
|
//
|
|
|
|
// Yes, using WinMM under NT 4 is a good idea. I can get latencies as
|
|
|
|
// low as 20 ms with WinMM, but with DirectSound I need to have the
|
|
|
|
// latency as high as 120 ms to avoid crackling--quite the opposite
|
|
|
|
// from the other Windows versions with real DirectSound support.
|
|
|
|
|
|
|
|
static bool initedDSound = false;
|
|
|
|
|
|
|
|
if (!initedDSound)
|
|
|
|
{
|
|
|
|
FSOUND_SetOutput (FSOUND_OUTPUT_DSOUND);
|
|
|
|
if (FSOUND_GetOutput () == FSOUND_OUTPUT_DSOUND)
|
|
|
|
{
|
|
|
|
if (FSOUND_Init (snd_samplerate, 64, 0))
|
|
|
|
{
|
|
|
|
Sleep (50);
|
|
|
|
FSOUND_Close ();
|
|
|
|
initedDSound = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (!nosound)
|
|
|
|
{
|
|
|
|
trynum = 0;
|
|
|
|
while (trynum < maxtrynum)
|
|
|
|
{
|
|
|
|
long outtype = /*trya3d ? FSOUND_OUTPUT_A3D :*/
|
|
|
|
outtypes[(outindex+trynum) % maxtrynum];
|
|
|
|
|
|
|
|
Printf (" Setting %s output", OutputNames[outtype]);
|
|
|
|
FModLog (FSOUND_SetOutput (outtype));
|
|
|
|
if (FSOUND_GetOutput() != outtype)
|
|
|
|
{
|
|
|
|
Printf (" Got %s output instead.\n", OutputNames[FSOUND_GetOutput()]);
|
|
|
|
#if 0
|
|
|
|
if (trya3d)
|
|
|
|
{
|
|
|
|
trya3d = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
++trynum;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Printf (" Setting driver %d", *snd_driver);
|
|
|
|
FModLog (FSOUND_SetDriver (snd_driver));
|
|
|
|
if (FSOUND_GetOutput() != outtype)
|
|
|
|
{
|
|
|
|
Printf (" Output changed to %s\n Trying driver 0",
|
|
|
|
OutputNames[FSOUND_GetOutput()]);
|
|
|
|
FSOUND_SetOutput (outtype);
|
|
|
|
FModLog (FSOUND_SetDriver (0));
|
|
|
|
}
|
|
|
|
if (snd_buffersize)
|
|
|
|
{
|
|
|
|
Printf (" Setting buffer size %d", *snd_buffersize);
|
|
|
|
FModLog (FSOUND_SetBufferSize (snd_buffersize));
|
|
|
|
}
|
|
|
|
FSOUND_GetDriverCaps (FSOUND_GetDriver(), &DriverCaps);
|
|
|
|
Printf (" Initialization");
|
|
|
|
if (!FModLog (FSOUND_Init (snd_samplerate, 64, FSOUND_INIT_DSOUND_DEFERRED)))
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
if (trya3d)
|
|
|
|
{
|
|
|
|
trya3d = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
trynum++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (trynum < 2)
|
|
|
|
{ // Initialized successfully
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nosound)
|
|
|
|
{
|
|
|
|
OutputType = FSOUND_GetOutput ();
|
|
|
|
if (snd_3d)
|
|
|
|
{
|
|
|
|
Sound3D = true;
|
|
|
|
if (gameinfo.gametype == GAME_Doom || gameinfo.gametype == GAME_Strife)
|
|
|
|
{
|
|
|
|
FSOUND_3D_SetRolloffFactor (1.7f);
|
|
|
|
}
|
|
|
|
else if (gameinfo.gametype == GAME_Heretic)
|
|
|
|
{
|
|
|
|
FSOUND_3D_SetRolloffFactor (1.24f);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FSOUND_3D_SetRolloffFactor (0.96f);
|
|
|
|
}
|
|
|
|
FSOUND_3D_SetDopplerFactor (1.f);
|
|
|
|
FSOUND_3D_SetDistanceFactor (100.f); // Distance factor only effects doppler!
|
|
|
|
if (!(DriverCaps & FSOUND_CAPS_HARDWARE))
|
|
|
|
{
|
|
|
|
Printf ("Using software 3D sound\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Sound3D = false;
|
|
|
|
}
|
|
|
|
snd_sfxvolume.Callback ();
|
|
|
|
}
|
|
|
|
return !nosound;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::Shutdown ()
|
|
|
|
{
|
|
|
|
if (DidInit)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
FSOUND_StopSound (FSOUND_ALL);
|
|
|
|
if (ChannelMap)
|
|
|
|
{
|
|
|
|
delete[] ChannelMap;
|
|
|
|
ChannelMap = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free all loaded samples
|
|
|
|
for (i = 0; i < S_sfx.Size (); i++)
|
|
|
|
{
|
|
|
|
S_sfx[i].data = NULL;
|
|
|
|
S_sfx[i].altdata = NULL;
|
|
|
|
S_sfx[i].bHaveLoop = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
FSOUND_Close ();
|
|
|
|
DidInit = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::PrintStatus ()
|
|
|
|
{
|
|
|
|
int output = FSOUND_GetOutput ();
|
|
|
|
int driver = FSOUND_GetDriver ();
|
|
|
|
int mixer = FSOUND_GetMixer ();
|
|
|
|
int num2d, num3d, total;
|
|
|
|
|
|
|
|
Printf ("Output: %s\n", OutputNames[output]);
|
|
|
|
Printf ("Driver: %d (%s)\n", driver, FSOUND_GetDriverName (driver));
|
|
|
|
Printf ("Mixer: %s\n", MixerNames[mixer]);
|
|
|
|
if (DriverCaps)
|
|
|
|
{
|
|
|
|
Printf ("Driver caps:");
|
|
|
|
if (DriverCaps & FSOUND_CAPS_HARDWARE) Printf (" HARDWARE");
|
|
|
|
if (DriverCaps & FSOUND_CAPS_EAX2) Printf (" EAX2");
|
|
|
|
if (DriverCaps & FSOUND_CAPS_EAX3) Printf (" EAX3");
|
|
|
|
Printf ("\n");
|
|
|
|
}
|
|
|
|
FSOUND_GetNumHWChannels (&num2d, &num3d, &total);
|
|
|
|
Printf ("Hardware 2D channels: %d\n", num2d);
|
|
|
|
Printf ("Hardware 3D channels: %d\n", num3d);
|
|
|
|
Printf ("Total hardware channels: %d\n", total);
|
|
|
|
if (Sound3D)
|
|
|
|
{
|
|
|
|
Printf ("\nUsing 3D sound\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::PrintDriversList ()
|
|
|
|
{
|
|
|
|
long numdrivers = FSOUND_GetNumDrivers ();
|
|
|
|
long i;
|
|
|
|
|
|
|
|
for (i = 0; i < numdrivers; i++)
|
|
|
|
{
|
|
|
|
Printf ("%ld. %s\n", i, FSOUND_GetDriverName (i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::GatherStats (char *outstring)
|
|
|
|
{
|
|
|
|
sprintf (outstring, "%d channels, %.2f%% CPU", FSOUND_GetChannelsPlaying(),
|
|
|
|
FSOUND_GetCPUUsage());
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::MovieDisableSound ()
|
|
|
|
{
|
|
|
|
I_ShutdownMusic ();
|
|
|
|
Shutdown ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::MovieResumeSound ()
|
|
|
|
{
|
|
|
|
Init ();
|
|
|
|
S_Init ();
|
|
|
|
S_RestartMusic ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::SetSfxVolume (float volume)
|
|
|
|
{
|
|
|
|
FSOUND_SetSFXMasterVolume (int(volume * 255.f));
|
|
|
|
// FMOD apparently resets absolute volume channels when setting master vol
|
|
|
|
snd_musicvolume.Callback ();
|
|
|
|
}
|
|
|
|
|
|
|
|
int FMODSoundRenderer::SetChannels (int numchannels)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// If using 3D sound, use all the hardware channels available,
|
|
|
|
// regardless of what the user sets with snd_channels. If there
|
|
|
|
// are fewer than 8 hardware channels, then force software.
|
|
|
|
if (Sound3D)
|
|
|
|
{
|
|
|
|
int hardChans;
|
|
|
|
|
|
|
|
FSOUND_GetNumHWChannels (NULL, &hardChans, NULL);
|
|
|
|
|
|
|
|
if (hardChans < 8)
|
|
|
|
{
|
|
|
|
Sound3D = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
numchannels = hardChans;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ChannelMap = new ChanMap[numchannels];
|
|
|
|
for (i = 0; i < numchannels; i++)
|
|
|
|
{
|
|
|
|
ChannelMap[i].soundID = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
NumChannels = numchannels;
|
|
|
|
return numchannels;
|
|
|
|
}
|
|
|
|
|
|
|
|
SoundStream *FMODSoundRenderer::CreateStream (SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata)
|
|
|
|
{
|
|
|
|
FMODStreamCapsule *capsule = new FMODStreamCapsule (userdata, callback);
|
|
|
|
unsigned int mode = FSOUND_2D | FSOUND_SIGNED;
|
|
|
|
mode |= (flags & SoundStream::Mono) ? FSOUND_MONO : FSOUND_STEREO;
|
|
|
|
mode |= (flags & SoundStream::Bits8) ? FSOUND_8BITS : FSOUND_16BITS;
|
|
|
|
FSOUND_STREAM *stream = FSOUND_Stream_Create (FMODStreamCapsule::MyCallback, buffbytes, mode, samplerate, capsule);
|
|
|
|
if (stream != NULL)
|
|
|
|
{
|
|
|
|
capsule->SetStream (stream);
|
|
|
|
return capsule;
|
|
|
|
}
|
|
|
|
delete capsule;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-04-14 12:58:52 +00:00
|
|
|
SoundStream *FMODSoundRenderer::OpenStream (const char *filename_or_data, int flags, int offset, int length)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
unsigned int mode = FSOUND_NORMAL | FSOUND_2D;
|
|
|
|
if (flags & SoundStream::Loop) mode |= FSOUND_LOOP_NORMAL;
|
2006-04-14 12:58:52 +00:00
|
|
|
FSOUND_STREAM *stream;
|
|
|
|
|
|
|
|
if (offset==-1)
|
|
|
|
{
|
|
|
|
mode |= FSOUND_LOADMEMORY;
|
|
|
|
offset=0;
|
|
|
|
}
|
|
|
|
stream = FSOUND_Stream_Open (filename_or_data, mode, offset, length);
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (stream != NULL)
|
|
|
|
{
|
|
|
|
return new FMODStreamCapsule (stream);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-04-14 12:58:52 +00:00
|
|
|
SoundTrackerModule *FMODSoundRenderer::OpenModule (const char *filename_or_data, int offset, int length)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-04-14 12:58:52 +00:00
|
|
|
FMUSIC_MODULE *mod;
|
|
|
|
|
|
|
|
int mode = FSOUND_LOOP_NORMAL;
|
|
|
|
if (offset==-1)
|
|
|
|
{
|
|
|
|
mode |= FSOUND_LOADMEMORY;
|
|
|
|
offset=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mod = FMUSIC_LoadSongEx (filename_or_data, offset, length, mode, NULL, 0);
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (mod != NULL)
|
|
|
|
{
|
|
|
|
return new FMUSICCapsule (mod);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// vol range is 0-255
|
|
|
|
// sep range is 0-255, -1 for surround, -2 for full vol middle
|
|
|
|
//
|
2006-05-23 04:25:49 +00:00
|
|
|
long FMODSoundRenderer::StartSound (sfxinfo_t *sfx, int vol, int sep, int pitch, int channel, bool looping, bool pauseable)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (!ChannelMap)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int id = int(sfx - &S_sfx[0]);
|
|
|
|
long volume;
|
|
|
|
long pan;
|
|
|
|
long freq;
|
|
|
|
long chan;
|
|
|
|
|
|
|
|
if (sep < 0)
|
|
|
|
{
|
|
|
|
pan = 128; // FSOUND_STEREOPAN is too loud relative to everything else
|
|
|
|
// when we don't want positioned sounds, so use center panning instead.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pan = sep;
|
|
|
|
}
|
|
|
|
|
|
|
|
freq = PITCH(sfx->frequency,pitch);
|
|
|
|
volume = vol;
|
|
|
|
|
|
|
|
chan = FSOUND_PlaySoundEx (FSOUND_FREE, CheckLooping (sfx, looping), NULL, true);
|
|
|
|
|
|
|
|
if (chan != -1)
|
|
|
|
{
|
|
|
|
//FSOUND_SetReserved (chan, TRUE);
|
|
|
|
FSOUND_SetSurround (chan, sep == -1 ? TRUE : FALSE);
|
|
|
|
//printf ("%ld\n", freq);
|
|
|
|
FSOUND_SetFrequency (chan, freq);
|
|
|
|
FSOUND_SetVolume (chan, vol);
|
|
|
|
FSOUND_SetPan (chan, pan);
|
|
|
|
FSOUND_SetPaused (chan, false);
|
|
|
|
ChannelMap[channel].channelID = chan;
|
|
|
|
ChannelMap[channel].soundID = id;
|
|
|
|
ChannelMap[channel].bIsLooping = looping ? true : false;
|
|
|
|
ChannelMap[channel].lastPos = 0;
|
|
|
|
ChannelMap[channel].bIs3D = false;
|
2006-05-23 04:25:49 +00:00
|
|
|
ChannelMap[channel].bIsPauseable = pauseable;
|
2006-02-24 04:48:15 +00:00
|
|
|
return channel + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DPrintf ("Sound %s failed to play: %d\n", sfx->name, FSOUND_GetError ());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
long FMODSoundRenderer::StartSound3D (sfxinfo_t *sfx, float vol, int pitch, int channel,
|
2006-05-23 04:25:49 +00:00
|
|
|
bool looping, float pos[3], float vel[3], bool pauseable)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (!Sound3D || !ChannelMap)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int id = int(sfx - &S_sfx[0]);
|
|
|
|
long freq;
|
|
|
|
long chan;
|
|
|
|
|
|
|
|
freq = PITCH(sfx->frequency,pitch);
|
|
|
|
|
|
|
|
FSOUND_SAMPLE *sample = CheckLooping (sfx, looping);
|
|
|
|
|
|
|
|
chan = FSOUND_PlaySoundEx (FSOUND_FREE, sample, NULL, true);
|
|
|
|
|
|
|
|
if (chan != -1)
|
|
|
|
{
|
|
|
|
//FSOUND_SetReserved (chan, TRUE);
|
|
|
|
FSOUND_SetFrequency (chan, freq);
|
|
|
|
FSOUND_SetVolume (chan, (int)(vol * 255.f));
|
|
|
|
FSOUND_3D_SetAttributes (chan, pos, vel);
|
|
|
|
FSOUND_SetPaused (chan, false);
|
|
|
|
ChannelMap[channel].channelID = chan;
|
|
|
|
ChannelMap[channel].soundID = id;
|
|
|
|
ChannelMap[channel].bIsLooping = looping ? true : false;
|
|
|
|
ChannelMap[channel].lastPos = 0;
|
|
|
|
ChannelMap[channel].bIs3D = true;
|
2006-05-23 04:25:49 +00:00
|
|
|
ChannelMap[channel].bIsPauseable = pauseable;
|
2006-02-24 04:48:15 +00:00
|
|
|
return channel + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DPrintf ("Sound %s failed to play: %d (%d)\n", sfx->name, FSOUND_GetError (), FSOUND_GetChannelsPlaying ());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::StopSound (long handle)
|
|
|
|
{
|
2006-05-21 02:10:16 +00:00
|
|
|
if (!handle || !ChannelMap)
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
handle--;
|
|
|
|
if (ChannelMap[handle].soundID != -1)
|
|
|
|
{
|
|
|
|
FSOUND_StopSound (ChannelMap[handle].channelID);
|
|
|
|
//FSOUND_SetReserved (ChannelMap[handle].channelID, FALSE);
|
|
|
|
UncheckSound (&S_sfx[ChannelMap[handle].soundID], ChannelMap[handle].bIsLooping);
|
|
|
|
ChannelMap[handle].soundID = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-21 02:10:16 +00:00
|
|
|
void FMODSoundRenderer::SetSfxPaused (bool paused)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < NumChannels; ++i)
|
|
|
|
{
|
|
|
|
if (ChannelMap[i].soundID != -1)
|
|
|
|
{
|
2006-05-23 04:25:49 +00:00
|
|
|
if (ChannelMap[i].bIsPauseable)
|
|
|
|
{
|
|
|
|
FSOUND_SetPaused (ChannelMap[i].channelID, paused);
|
|
|
|
}
|
2006-05-21 02:10:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
bool FMODSoundRenderer::IsPlayingSound (long handle)
|
|
|
|
{
|
|
|
|
if (!handle || !ChannelMap)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
handle--;
|
|
|
|
if (ChannelMap[handle].soundID == -1)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bool is;
|
|
|
|
|
|
|
|
// FSOUND_IsPlaying does not work with A3D
|
|
|
|
if (OutputType != FSOUND_OUTPUT_A3D)
|
|
|
|
{
|
|
|
|
is = !!FSOUND_IsPlaying (ChannelMap[handle].channelID);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned int pos;
|
|
|
|
if (ChannelMap[handle].bIsLooping)
|
|
|
|
{
|
|
|
|
is = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pos = FSOUND_GetCurrentPosition (ChannelMap[handle].channelID);
|
|
|
|
is = pos >= ChannelMap[handle].lastPos &&
|
|
|
|
pos <= S_sfx[ChannelMap[handle].soundID].length;
|
|
|
|
ChannelMap[handle].lastPos = pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!is)
|
|
|
|
{
|
|
|
|
//FSOUND_SetReserved (ChannelMap[handle].channelID, FALSE);
|
|
|
|
UncheckSound (&S_sfx[ChannelMap[handle].soundID], ChannelMap[handle].bIsLooping);
|
|
|
|
ChannelMap[handle].soundID = -1;
|
|
|
|
}
|
|
|
|
return is;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::UpdateSoundParams (long handle, int vol, int sep, int pitch)
|
|
|
|
{
|
|
|
|
if (!handle || !ChannelMap)
|
|
|
|
return;
|
|
|
|
|
|
|
|
handle--;
|
|
|
|
if (ChannelMap[handle].soundID == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
long volume;
|
|
|
|
long pan;
|
|
|
|
long freq;
|
|
|
|
|
|
|
|
freq = PITCH(S_sfx[ChannelMap[handle].soundID].frequency, pitch);
|
|
|
|
volume = vol;
|
|
|
|
|
|
|
|
if (sep < 0)
|
|
|
|
{
|
|
|
|
pan = 128; //FSOUND_STEREOPAN
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pan = sep;
|
|
|
|
}
|
|
|
|
|
|
|
|
FSOUND_SetSurround (ChannelMap[handle].channelID, sep == -1 ? TRUE : FALSE);
|
|
|
|
FSOUND_SetPan (ChannelMap[handle].channelID, pan);
|
|
|
|
FSOUND_SetVolume (ChannelMap[handle].channelID, volume);
|
|
|
|
FSOUND_SetFrequency (ChannelMap[handle].channelID, freq);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::UpdateSoundParams3D (long handle, float pos[3], float vel[3])
|
|
|
|
{
|
|
|
|
if (!handle || !ChannelMap)
|
|
|
|
return;
|
|
|
|
|
|
|
|
handle--;
|
|
|
|
if (ChannelMap[handle].soundID == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
FSOUND_3D_SetAttributes (ChannelMap[handle].channelID, pos, vel);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::UpdateListener (AActor *listener)
|
|
|
|
{
|
|
|
|
float angle;
|
|
|
|
float pos[3], vel[3];
|
|
|
|
float lpos[3];
|
|
|
|
|
|
|
|
if (Sound3D && ChannelMap)
|
|
|
|
{
|
|
|
|
vel[0] = listener->momx * (TICRATE/65536.f);
|
|
|
|
vel[1] = listener->momy * (TICRATE/65536.f);
|
|
|
|
vel[2] = listener->momz * (TICRATE/65536.f);
|
|
|
|
pos[0] = listener->x / 65536.f;
|
|
|
|
pos[2] = listener->y / 65536.f;
|
|
|
|
pos[1] = listener->z / 65536.f;
|
|
|
|
|
|
|
|
// Move sounds that are not meant to be heard in 3D so
|
|
|
|
// that they remain on top of the listener.
|
|
|
|
|
|
|
|
for (int i = 0; i < NumChannels; i++)
|
|
|
|
{
|
|
|
|
if (ChannelMap[i].soundID != -1 && !ChannelMap[i].bIs3D)
|
|
|
|
{
|
|
|
|
FSOUND_3D_SetAttributes (ChannelMap[i].channelID, pos, vel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sounds that are right on top of the listener can produce
|
|
|
|
// weird results depending on the environment, so position
|
|
|
|
// the listener back slightly from its true location.
|
|
|
|
|
|
|
|
angle = (float)(listener->angle) * ((float)PI / 2147483648.f);
|
|
|
|
|
|
|
|
lpos[0] = pos[0] - .5f * cosf (angle);
|
|
|
|
lpos[2] = pos[2] - .5f * sinf (angle);
|
|
|
|
lpos[1] = pos[1];
|
|
|
|
|
|
|
|
FSOUND_3D_Listener_SetAttributes (lpos, vel,
|
|
|
|
cosf (angle), 0.f, sinf (angle), 0.f, 1.f, 0.f);
|
|
|
|
|
|
|
|
//if (DriverCaps & (FSOUND_CAPS_EAX2|FSOUND_CAPS_EAX3))
|
|
|
|
{
|
|
|
|
bool underwater;
|
|
|
|
const ReverbContainer *env;
|
|
|
|
|
|
|
|
if (ForcedEnvironment)
|
|
|
|
{
|
|
|
|
env = ForcedEnvironment;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
underwater = (listener->waterlevel == 3 && snd_waterreverb);
|
|
|
|
env = zones[listener->Sector->ZoneNumber].Environment;
|
|
|
|
if (env == NULL)
|
|
|
|
{
|
|
|
|
env = DefaultEnvironments[0];
|
|
|
|
}
|
|
|
|
if (env == DefaultEnvironments[0] && underwater)
|
|
|
|
{
|
|
|
|
env = DefaultEnvironments[22];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (env != PrevEnvironment || env->Modified)
|
|
|
|
{
|
|
|
|
DPrintf ("Reverb Environment %s\n", env->Name);
|
|
|
|
const_cast<ReverbContainer*>(env)->Modified = false;
|
|
|
|
FSOUND_Reverb_SetProperties ((FSOUND_REVERB_PROPERTIES *)(&env->Properties));
|
|
|
|
PrevEnvironment = env;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FSOUND_Update ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::LoadSound (sfxinfo_t *sfx)
|
|
|
|
{
|
|
|
|
if (!sfx->data)
|
|
|
|
{
|
|
|
|
DPrintf ("loading sound \"%s\" (%d) ", sfx->name, sfx - &S_sfx[0]);
|
|
|
|
getsfx (sfx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::UnloadSound (sfxinfo_t *sfx)
|
|
|
|
{
|
|
|
|
if (sfx->data == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sfx->bHaveLoop = false;
|
|
|
|
sfx->normal = 0;
|
|
|
|
sfx->looping = 0;
|
|
|
|
if (sfx->altdata != NULL)
|
|
|
|
{
|
|
|
|
FSOUND_Sample_Free ((FSOUND_SAMPLE *)sfx->altdata);
|
|
|
|
sfx->altdata = NULL;
|
|
|
|
}
|
|
|
|
if (sfx->data != NULL)
|
|
|
|
{
|
|
|
|
FSOUND_Sample_Free ((FSOUND_SAMPLE *)sfx->data);
|
|
|
|
sfx->data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
DPrintf ("Unloaded sound \"%s\" (%d)\n", sfx->name, sfx - &S_sfx[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// FSOUND_Sample_Upload seems to mess up the signedness of sound data when
|
|
|
|
// uploading to hardware buffers. The pattern is not particularly predictable,
|
|
|
|
// so this is a replacement for it that loads the data manually. Source data
|
|
|
|
// is mono, unsigned, 8-bit. Output is mono, signed, 8- or 16-bit.
|
|
|
|
|
|
|
|
int FMODSoundRenderer::PutSampleData (FSOUND_SAMPLE *sample, const BYTE *data, int len, unsigned int mode)
|
|
|
|
{
|
|
|
|
/*if (mode & FSOUND_2D)
|
|
|
|
{
|
|
|
|
return FSOUND_Sample_Upload (sample, const_cast<BYTE *>(data),
|
|
|
|
FSOUND_8BITS|FSOUND_MONO|FSOUND_UNSIGNED);
|
|
|
|
}
|
|
|
|
else*/ if (FSOUND_Sample_GetMode (sample) & FSOUND_8BITS)
|
|
|
|
{
|
|
|
|
void *ptr1, *ptr2;
|
|
|
|
unsigned int len1, len2;
|
|
|
|
|
|
|
|
if (FSOUND_Sample_Lock (sample, 0, len, &ptr1, &ptr2, &len1, &len2))
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
BYTE *ptr;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
for (i = 0, ptr = (BYTE *)ptr1, len = len1;
|
|
|
|
i < 2 && ptr && len;
|
|
|
|
i++, ptr = (BYTE *)ptr2, len = len2)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
for (j = 0; j < len; j++)
|
|
|
|
{
|
|
|
|
ptr[j] = *data++ - 128;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FSOUND_Sample_Unlock (sample, ptr1, ptr2, len1, len2);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
void *ptr1, *ptr2;
|
|
|
|
unsigned int len1, len2;
|
|
|
|
|
|
|
|
if (FSOUND_Sample_Lock (sample, 0, len*2, &ptr1, &ptr2, &len1, &len2))
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
SWORD *ptr;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
for (i = 0, ptr = (SWORD *)ptr1, len = len1/2;
|
|
|
|
i < 2 && ptr && len;
|
|
|
|
i++, ptr = (SWORD *)ptr2, len = len2/2)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
for (j = 0; j < len; j++)
|
|
|
|
{
|
|
|
|
ptr[j] = ((*data<<8)|(*data)) - 32768;
|
|
|
|
data++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FSOUND_Sample_Unlock (sample, ptr1, ptr2, len1, len2);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::DoLoad (void **slot, sfxinfo_t *sfx)
|
|
|
|
{
|
|
|
|
BYTE *sfxdata;
|
|
|
|
int size;
|
|
|
|
int errcount;
|
|
|
|
unsigned long samplemode;
|
|
|
|
|
|
|
|
samplemode = Sound3D ? FSOUND_HW3D : FSOUND_2D;
|
|
|
|
sfxdata = NULL;
|
|
|
|
|
|
|
|
errcount = 0;
|
|
|
|
while (errcount < 2)
|
|
|
|
{
|
|
|
|
if (sfxdata != NULL)
|
|
|
|
{
|
|
|
|
delete[] sfxdata;
|
|
|
|
sfxdata = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errcount)
|
|
|
|
sfx->lumpnum = Wads.GetNumForName ("dsempty");
|
|
|
|
|
|
|
|
size = Wads.LumpLength (sfx->lumpnum);
|
|
|
|
if (size == 0)
|
|
|
|
{
|
|
|
|
errcount++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
FWadLump wlump = Wads.OpenLumpNum (sfx->lumpnum);
|
|
|
|
sfxdata = new byte[size];
|
|
|
|
wlump.Read (sfxdata, size);
|
|
|
|
SDWORD len = ((SDWORD *)sfxdata)[1];
|
|
|
|
|
|
|
|
// If the sound is raw, just load it as such.
|
|
|
|
// Otherwise, try the sound as DMX format.
|
|
|
|
// If that fails, let FMOD try and figure it out.
|
|
|
|
if (sfx->bLoadRAW ||
|
|
|
|
(((BYTE *)sfxdata)[0] == 3 && ((BYTE *)sfxdata)[1] == 0 && len <= size - 8))
|
|
|
|
{
|
|
|
|
FSOUND_SAMPLE *sample;
|
|
|
|
const BYTE *sfxstart;
|
|
|
|
unsigned int bits;
|
|
|
|
|
|
|
|
if (sfx->bLoadRAW)
|
|
|
|
{
|
|
|
|
len = Wads.LumpLength (sfx->lumpnum);
|
|
|
|
sfx->frequency = (sfx->bForce22050 ? 22050 : 11025);
|
|
|
|
sfxstart = sfxdata;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sfx->frequency = ((WORD *)sfxdata)[1];
|
|
|
|
if (sfx->frequency == 0)
|
|
|
|
{
|
|
|
|
sfx->frequency = 11025;
|
|
|
|
}
|
|
|
|
sfxstart = sfxdata + 8;
|
|
|
|
}
|
|
|
|
sfx->ms = sfx->length = len;
|
|
|
|
|
|
|
|
bits = FSOUND_8BITS;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
sample = FSOUND_Sample_Alloc (FSOUND_FREE, len,
|
|
|
|
samplemode|bits|FSOUND_LOOP_OFF|FSOUND_MONO,
|
|
|
|
sfx->frequency, 255, FSOUND_STEREOPAN, 255);
|
|
|
|
} while (sample == NULL && (bits <<= 1) == FSOUND_16BITS);
|
|
|
|
|
|
|
|
if (sample == NULL)
|
|
|
|
{
|
|
|
|
DPrintf ("Failed to allocate sample: %d\n", FSOUND_GetError ());
|
|
|
|
errcount++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!PutSampleData (sample, sfxstart, len, samplemode))
|
|
|
|
{
|
|
|
|
DPrintf ("Failed to upload sample: %d\n", FSOUND_GetError ());
|
|
|
|
FSOUND_Sample_Free (sample);
|
|
|
|
sample = NULL;
|
|
|
|
errcount++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*slot = sample;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (((BYTE *)sfxdata)[0] == 'f' && ((BYTE *)sfxdata)[1] == 'L' &&
|
|
|
|
((BYTE *)sfxdata)[2] == 'a' && ((BYTE *)sfxdata)[3] == 'C')
|
|
|
|
{
|
|
|
|
FLACSampleLoader loader (sfx);
|
|
|
|
*slot = loader.LoadSample (samplemode);
|
|
|
|
if (*slot == NULL && FSOUND_GetError() == FMOD_ERR_CREATEBUFFER && samplemode == FSOUND_HW3D)
|
|
|
|
{
|
|
|
|
DPrintf ("Trying to fall back to software sample\n");
|
|
|
|
*slot = FSOUND_Sample_Load (FSOUND_FREE, (char *)sfxdata, FSOUND_2D, 0, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*slot = FSOUND_Sample_Load (FSOUND_FREE, (char *)sfxdata,
|
|
|
|
samplemode|FSOUND_LOADMEMORY, 0, size);
|
|
|
|
if (*slot == NULL && FSOUND_GetError() == FMOD_ERR_CREATEBUFFER && samplemode == FSOUND_HW3D)
|
|
|
|
{
|
|
|
|
DPrintf ("Trying to fall back to software sample\n");
|
|
|
|
*slot = FSOUND_Sample_Load (FSOUND_FREE, (char *)sfxdata, FSOUND_2D|FSOUND_LOADMEMORY, 0, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*slot != NULL)
|
|
|
|
{
|
|
|
|
int probe;
|
|
|
|
|
|
|
|
FSOUND_Sample_GetDefaults ((FSOUND_SAMPLE *)sfx->data, &probe,
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
|
|
|
|
sfx->frequency = probe;
|
|
|
|
sfx->ms = FSOUND_Sample_GetLength ((FSOUND_SAMPLE *)sfx->data);
|
|
|
|
sfx->length = sfx->ms;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sfx->data)
|
|
|
|
{
|
|
|
|
sfx->ms = (sfx->ms * 1000) / (sfx->frequency);
|
|
|
|
DPrintf ("[%d Hz %d samples]\n", sfx->frequency, sfx->length);
|
|
|
|
|
|
|
|
if (Sound3D)
|
|
|
|
{
|
|
|
|
// Match s_sound.cpp min distance.
|
|
|
|
// Max distance is irrelevant.
|
|
|
|
FSOUND_Sample_SetMinMaxDistance ((FSOUND_SAMPLE *)sfx->data,
|
|
|
|
(float)S_CLOSE_DIST, (float)MAX_SND_DIST*2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sfxdata != NULL)
|
|
|
|
{
|
|
|
|
delete[] sfxdata;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::getsfx (sfxinfo_t *sfx)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
// Get the sound data from the WAD and register it with sound library
|
|
|
|
|
|
|
|
// If the sound doesn't exist, replace it with the empty sound.
|
|
|
|
if (sfx->lumpnum == -1)
|
|
|
|
{
|
|
|
|
sfx->lumpnum = Wads.GetNumForName ("dsempty");
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if there is another sound already initialized with this lump. If so,
|
|
|
|
// then set this one up as a link, and don't load the sound again.
|
|
|
|
for (i = 0; i < S_sfx.Size (); i++)
|
|
|
|
{
|
|
|
|
if (S_sfx[i].data && S_sfx[i].link == sfxinfo_t::NO_LINK && S_sfx[i].lumpnum == sfx->lumpnum)
|
|
|
|
{
|
|
|
|
DPrintf ("Linked to %s (%d)\n", S_sfx[i].name, i);
|
|
|
|
sfx->link = i;
|
|
|
|
sfx->ms = S_sfx[i].ms;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sfx->bHaveLoop = false;
|
|
|
|
sfx->normal = 0;
|
|
|
|
sfx->looping = 0;
|
|
|
|
sfx->altdata = NULL;
|
|
|
|
DoLoad (&sfx->data, sfx);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Right now, FMOD's biggest shortcoming compared to MIDAS is that it does not
|
|
|
|
// support multiple samples with the same sample data. Thus, if we want to
|
|
|
|
// play a looped and non-looped version of the same sound, we need to create
|
|
|
|
// two copies of it. Fortunately, most sounds will either be played looped or
|
|
|
|
// not, but not both at the same time, so this really isn't too much of a
|
|
|
|
// problem. This function juggles the sample between looping and non-looping,
|
|
|
|
// creating a copy if necessary. It also increments the appropriate use
|
|
|
|
// counter.
|
|
|
|
//
|
|
|
|
// Update: FMOD 3.3 added FSOUND_SetLoopMode to set a channel's looping status,
|
|
|
|
// but that only works with software channels. So I think I will continue to
|
|
|
|
// do this even though I don't *have* to anymore.
|
|
|
|
|
|
|
|
FSOUND_SAMPLE *FMODSoundRenderer::CheckLooping (sfxinfo_t *sfx, bool looped)
|
|
|
|
{
|
|
|
|
if (looped)
|
|
|
|
{
|
|
|
|
sfx->looping++;
|
|
|
|
if (sfx->bHaveLoop)
|
|
|
|
{
|
|
|
|
return (FSOUND_SAMPLE *)(sfx->altdata ? sfx->altdata : sfx->data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (sfx->normal == 0)
|
|
|
|
{
|
|
|
|
sfx->bHaveLoop = true;
|
|
|
|
FSOUND_Sample_SetMode ((FSOUND_SAMPLE *)sfx->data, FSOUND_LOOP_NORMAL);
|
|
|
|
return (FSOUND_SAMPLE *)sfx->data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sfx->normal++;
|
|
|
|
if (sfx->altdata || !sfx->bHaveLoop)
|
|
|
|
{
|
|
|
|
return (FSOUND_SAMPLE *)sfx->data;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (sfx->looping == 0)
|
|
|
|
{
|
|
|
|
sfx->bHaveLoop = false;
|
|
|
|
FSOUND_Sample_SetMode ((FSOUND_SAMPLE *)sfx->data, FSOUND_LOOP_OFF);
|
|
|
|
return (FSOUND_SAMPLE *)sfx->data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we get here, we need to create an alternate version of the sample.
|
|
|
|
FSOUND_Sample_SetMode ((FSOUND_SAMPLE *)sfx->data, FSOUND_LOOP_OFF);
|
|
|
|
DoLoad (&sfx->altdata, sfx);
|
|
|
|
FSOUND_Sample_SetMode ((FSOUND_SAMPLE *)sfx->altdata, FSOUND_LOOP_NORMAL);
|
|
|
|
sfx->bHaveLoop = true;
|
|
|
|
return (FSOUND_SAMPLE *)(looped ? sfx->altdata : sfx->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FMODSoundRenderer::UncheckSound (sfxinfo_t *sfx, bool looped)
|
|
|
|
{
|
|
|
|
if (looped)
|
|
|
|
{
|
|
|
|
if (sfx->looping > 0)
|
|
|
|
sfx->looping--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (sfx->normal > 0)
|
|
|
|
sfx->normal--;
|
|
|
|
}
|
|
|
|
}
|