2009-05-08 13:28:41 +00:00
|
|
|
/*
|
|
|
|
* OpenClonk, http://www.openclonk.org
|
|
|
|
*
|
2009-06-05 13:41:20 +00:00
|
|
|
* Copyright (c) 1998-2000, 2004, 2008 Matthes Bender
|
2010-12-23 00:01:24 +00:00
|
|
|
* Copyright (c) 2001, 2005-2006, 2008 Sven Eberhardt
|
|
|
|
* Copyright (c) 2003-2005 Peter Wortmann
|
2011-09-01 14:58:52 +00:00
|
|
|
* Copyright (c) 2005-2006, 2009, 2011 Günther Brammer
|
2010-12-23 00:01:24 +00:00
|
|
|
* Copyright (c) 2009 Nicolas Hake
|
|
|
|
* Copyright (c) 2010 Benjamin Herr
|
2011-09-01 10:47:54 +00:00
|
|
|
* Copyright (c) 2010 Martin Plicht
|
2009-05-08 13:28:41 +00:00
|
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
|
|
|
*
|
|
|
|
* Portions might be copyrighted by other authors who have contributed
|
|
|
|
* to OpenClonk.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
* See isc_license.txt for full license and disclaimer.
|
|
|
|
*
|
|
|
|
* "Clonk" is a registered trademark of Matthes Bender.
|
|
|
|
* See clonk_trademark_license.txt for full license.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Handles the sound bank and plays effects using DSoundX */
|
|
|
|
|
|
|
|
#include <C4Include.h>
|
|
|
|
#include <C4SoundSystem.h>
|
|
|
|
|
|
|
|
#include <C4Random.h>
|
|
|
|
#include <C4Object.h>
|
|
|
|
#include <C4Game.h>
|
|
|
|
#include <C4Config.h>
|
|
|
|
#include <C4Application.h>
|
2010-09-29 01:44:05 +00:00
|
|
|
#include <C4Viewport.h>
|
2010-09-26 00:36:35 +00:00
|
|
|
#include <C4SoundLoaders.h>
|
|
|
|
|
2011-01-24 00:17:06 +00:00
|
|
|
#ifdef HAVE_LIBSDL_MIXER
|
|
|
|
#define USE_RWOPS
|
|
|
|
#include <SDL_mixer.h>
|
|
|
|
#endif
|
|
|
|
|
2010-09-26 00:36:35 +00:00
|
|
|
using namespace C4SoundLoaders;
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
C4SoundEffect::C4SoundEffect():
|
2010-03-28 18:58:01 +00:00
|
|
|
UsageTime (0),
|
|
|
|
Instances (0),
|
|
|
|
Static (false),
|
|
|
|
pSample (NULL),
|
|
|
|
FirstInst (NULL),
|
|
|
|
Next (NULL)
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
Name[0]=0;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
C4SoundEffect::~C4SoundEffect()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
Clear();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
void C4SoundEffect::Clear()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
while (FirstInst) RemoveInst(FirstInst);
|
2009-11-02 13:18:47 +00:00
|
|
|
#ifdef HAVE_FMOD
|
2009-05-08 13:28:41 +00:00
|
|
|
if (pSample) FSOUND_Sample_Free(pSample);
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBSDL_MIXER
|
|
|
|
if (pSample) Mix_FreeChunk(pSample);
|
|
|
|
#endif
|
2010-09-26 00:36:35 +00:00
|
|
|
#ifdef USE_OPEN_AL
|
|
|
|
if (pSample) alDeleteBuffers(1, &pSample);
|
2009-05-08 13:28:41 +00:00
|
|
|
#endif
|
2010-09-26 00:36:35 +00:00
|
|
|
pSample = NULL;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4SoundEffect::Load(const char *szFileName, C4Group &hGroup, bool fStatic)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// Sound check
|
|
|
|
if (!Config.Sound.RXSound) return false;
|
|
|
|
// Locate sound in file
|
|
|
|
StdBuf WaveBuffer;
|
2011-03-05 01:44:26 +00:00
|
|
|
if (!hGroup.LoadEntry(szFileName, &WaveBuffer)) return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
// load it from mem
|
2009-08-15 18:50:32 +00:00
|
|
|
if (!Load((BYTE*)WaveBuffer.getData(), WaveBuffer.getSize(), fStatic)) return false;
|
2010-03-27 16:05:02 +00:00
|
|
|
// Set name
|
|
|
|
SCopy(szFileName,Name,C4MaxSoundName);
|
2009-08-15 18:50:32 +00:00
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4SoundEffect::Load(BYTE *pData, size_t iDataLen, bool fStatic, bool fRaw)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// Sound check
|
|
|
|
if (!Config.Sound.RXSound) return false;
|
2010-09-26 00:36:35 +00:00
|
|
|
|
|
|
|
SoundInfo info;
|
|
|
|
int32_t options = 0;
|
|
|
|
if (fRaw)
|
|
|
|
options |= SoundLoader::OPTION_Raw;
|
|
|
|
for (SoundLoader* loader = SoundLoader::first_loader; loader; loader = loader->next)
|
|
|
|
{
|
2010-10-01 23:06:51 +00:00
|
|
|
if (loader->ReadInfo(&info, pData, iDataLen))
|
2010-09-26 00:36:35 +00:00
|
|
|
{
|
|
|
|
if (info.final_handle)
|
|
|
|
{
|
|
|
|
// loader supplied the handle specific to the sound system used; just assign to pSample
|
|
|
|
pSample = info.final_handle;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifdef USE_OPEN_AL
|
|
|
|
Application.MusicSystem.SelectContext();
|
|
|
|
alGenBuffers(1, &pSample);
|
2010-10-01 23:06:51 +00:00
|
|
|
alBufferData(pSample, info.format, &info.sound_data[0], info.sound_data.size(), info.sample_rate);
|
2010-09-26 00:36:35 +00:00
|
|
|
#else
|
|
|
|
Log("SoundLoader does not provide a ready-made handle");
|
2009-05-08 13:28:41 +00:00
|
|
|
#endif
|
2010-09-26 00:36:35 +00:00
|
|
|
}
|
|
|
|
SampleRate = info.sample_rate;
|
|
|
|
Length = info.sample_length*1000;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
*Name = '\0';
|
|
|
|
// Set usage time
|
2010-03-27 16:05:02 +00:00
|
|
|
UsageTime=Game.Time;
|
|
|
|
Static=fStatic;
|
2010-12-11 18:50:38 +00:00
|
|
|
return !!pSample;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
void C4SoundEffect::Execute()
|
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// check for instances that have stopped and volume changes
|
2010-03-28 18:58:01 +00:00
|
|
|
for (C4SoundInstance *pInst = FirstInst; pInst; )
|
2010-03-27 16:05:02 +00:00
|
|
|
{
|
|
|
|
C4SoundInstance *pNext = pInst->pNext;
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!pInst->Playing())
|
2010-03-27 16:05:02 +00:00
|
|
|
RemoveInst(pInst);
|
|
|
|
else
|
|
|
|
pInst->Execute();
|
|
|
|
pInst = pNext;
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
C4SoundInstance *C4SoundEffect::New(bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance)
|
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// check: too many instances?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!fLoop && Instances >= C4MaxSoundInstances) return NULL;
|
2010-03-27 16:05:02 +00:00
|
|
|
// create & init sound instance
|
|
|
|
C4SoundInstance *pInst = new C4SoundInstance();
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!pInst->Create(this, fLoop, iVolume, pObj, 0, iCustomFalloffDistance)) { delete pInst; return NULL; }
|
2010-03-27 16:05:02 +00:00
|
|
|
// add to list
|
|
|
|
AddInst(pInst);
|
|
|
|
// return
|
|
|
|
return pInst;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
C4SoundInstance *C4SoundEffect::GetInstance(C4Object *pObj)
|
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
for (C4SoundInstance *pInst = FirstInst; pInst; pInst = pInst->pNext)
|
|
|
|
if (pInst->getObj() == pObj)
|
2010-03-27 16:05:02 +00:00
|
|
|
return pInst;
|
|
|
|
return NULL;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void C4SoundEffect::ClearPointers(C4Object *pObj)
|
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
for (C4SoundInstance *pInst = FirstInst; pInst; pInst = pInst->pNext)
|
2010-03-27 16:05:02 +00:00
|
|
|
pInst->ClearPointers(pObj);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int32_t C4SoundEffect::GetStartedInstanceCount(int32_t iX, int32_t iY, int32_t iRad)
|
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
int32_t cnt = 0;
|
2010-03-28 18:58:01 +00:00
|
|
|
for (C4SoundInstance *pInst = FirstInst; pInst; pInst = pInst->pNext)
|
|
|
|
if (pInst->isStarted() && pInst->getObj() && pInst->Inside(iX, iY, iRad))
|
2010-03-27 16:05:02 +00:00
|
|
|
cnt++;
|
|
|
|
return cnt;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int32_t C4SoundEffect::GetStartedInstanceCount()
|
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
int32_t cnt = 0;
|
2010-03-28 18:58:01 +00:00
|
|
|
for (C4SoundInstance *pInst = FirstInst; pInst; pInst = pInst->pNext)
|
|
|
|
if (pInst->isStarted() && pInst->Playing() && !pInst->getObj())
|
2010-03-27 16:05:02 +00:00
|
|
|
cnt++;
|
|
|
|
return cnt;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void C4SoundEffect::AddInst(C4SoundInstance *pInst)
|
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
pInst->pNext = FirstInst;
|
|
|
|
FirstInst = pInst;
|
|
|
|
Instances++;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
void C4SoundEffect::RemoveInst(C4SoundInstance *pInst)
|
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
if (pInst == FirstInst)
|
2010-03-27 16:05:02 +00:00
|
|
|
FirstInst = pInst->pNext;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
C4SoundInstance *pPos = FirstInst;
|
2010-03-28 18:58:01 +00:00
|
|
|
while (pPos && pPos->pNext != pInst) pPos = pPos->pNext;
|
|
|
|
if (pPos)
|
2010-03-27 16:05:02 +00:00
|
|
|
pPos->pNext = pInst->pNext;
|
|
|
|
}
|
|
|
|
delete pInst;
|
|
|
|
Instances--;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
C4SoundInstance::C4SoundInstance():
|
2010-03-28 18:58:01 +00:00
|
|
|
pEffect (NULL),
|
|
|
|
iVolume (0), iPan (0),
|
|
|
|
iChannel (-1),
|
|
|
|
pNext (NULL)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
C4SoundInstance::~C4SoundInstance()
|
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
Clear();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void C4SoundInstance::Clear()
|
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
Stop();
|
|
|
|
iChannel = -1;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4SoundInstance::Create(C4SoundEffect *pnEffect, bool fLoop, int32_t inVolume, C4Object *pnObj, int32_t inNearInstanceMax, int32_t iFalloffDistance)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// Sound check
|
|
|
|
if (!Config.Sound.RXSound || !pnEffect) return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Already playing? Stop
|
2010-03-28 18:58:01 +00:00
|
|
|
if (Playing()) { Stop(); return false; }
|
2010-03-27 16:05:02 +00:00
|
|
|
// Set effect
|
|
|
|
pEffect = pnEffect;
|
|
|
|
// Set
|
2011-03-13 16:25:35 +00:00
|
|
|
iStarted = GetTime();
|
2010-03-27 16:05:02 +00:00
|
|
|
iVolume = inVolume; iPan = 0; iChannel = -1;
|
|
|
|
iNearInstanceMax = inNearInstanceMax;
|
2009-05-08 13:28:41 +00:00
|
|
|
this->iFalloffDistance = iFalloffDistance;
|
2010-03-27 16:05:02 +00:00
|
|
|
pObj = pnObj;
|
|
|
|
fLooping = fLoop;
|
|
|
|
// Start
|
|
|
|
Execute();
|
|
|
|
// Safe usage
|
|
|
|
pEffect->UsageTime = Game.Time;
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4SoundInstance::CheckStart()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// already started?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (isStarted()) return true;
|
2010-03-27 16:05:02 +00:00
|
|
|
// don't bother if half the time is up and the sound is not looping
|
2011-03-13 16:25:35 +00:00
|
|
|
if (GetTime() > iStarted + pEffect->Length / 2 && !fLooping)
|
2010-03-27 16:05:02 +00:00
|
|
|
return false;
|
|
|
|
// do near-instances check
|
|
|
|
int32_t iNearInstances = pObj ? pEffect->GetStartedInstanceCount(pObj->GetX(), pObj->GetY(), C4NearSoundRadius)
|
2010-03-28 18:58:01 +00:00
|
|
|
: pEffect->GetStartedInstanceCount();
|
2010-03-27 16:05:02 +00:00
|
|
|
// over maximum?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (iNearInstances > iNearInstanceMax) return false;
|
2010-03-27 16:05:02 +00:00
|
|
|
// Start
|
|
|
|
return Start();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4SoundInstance::Start()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2009-11-02 13:18:47 +00:00
|
|
|
#ifdef HAVE_FMOD
|
2010-03-27 16:05:02 +00:00
|
|
|
// Start
|
2010-03-28 18:58:01 +00:00
|
|
|
if ((iChannel = FSOUND_PlaySound(FSOUND_FREE, pEffect->pSample)) == -1)
|
2010-03-27 16:05:02 +00:00
|
|
|
return false;
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!FSOUND_SetLoopMode(iChannel, fLooping ? FSOUND_LOOP_NORMAL : FSOUND_LOOP_OFF))
|
2010-03-27 16:05:02 +00:00
|
|
|
{ Stop(); return false; }
|
|
|
|
// set position
|
2011-03-13 16:25:35 +00:00
|
|
|
if (GetTime() > iStarted + 20)
|
2010-03-27 16:05:02 +00:00
|
|
|
{
|
2010-10-02 16:48:43 +00:00
|
|
|
assert(pEffect->Length > 0);
|
2011-03-13 16:25:35 +00:00
|
|
|
int32_t iTime = (GetTime() - iStarted) % pEffect->Length;
|
2010-03-27 16:05:02 +00:00
|
|
|
FSOUND_SetCurrentPosition(iChannel, iTime / 10 * pEffect->SampleRate / 100);
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
#elif defined HAVE_LIBSDL_MIXER
|
|
|
|
// Be paranoid about SDL_Mixer initialisation
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!Application.MusicSystem.MODInitialized) return false;
|
|
|
|
if ((iChannel = Mix_PlayChannel(-1, pEffect->pSample, fLooping? -1 : 0)) == -1)
|
2009-08-15 18:50:32 +00:00
|
|
|
return false;
|
2010-09-26 00:36:35 +00:00
|
|
|
#elif defined(USE_OPEN_AL)
|
|
|
|
Application.MusicSystem.SelectContext();
|
|
|
|
alGenSources(1, (ALuint*)&iChannel);
|
|
|
|
alSourcei(iChannel, AL_BUFFER, pEffect->pSample);
|
|
|
|
alSourcePlay(iChannel);
|
2009-05-08 13:28:41 +00:00
|
|
|
#else
|
2009-08-15 18:50:32 +00:00
|
|
|
return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
#endif
|
2010-03-27 16:05:02 +00:00
|
|
|
// Update volume
|
|
|
|
Execute();
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4SoundInstance::Stop()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
if (!pEffect) return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Stop sound
|
2009-08-15 18:50:32 +00:00
|
|
|
bool fRet = true;
|
2009-11-02 13:18:47 +00:00
|
|
|
#ifdef HAVE_FMOD
|
2010-03-28 18:58:01 +00:00
|
|
|
if (Playing())
|
2009-05-08 13:28:41 +00:00
|
|
|
fRet = !! FSOUND_StopSound(iChannel);
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBSDL_MIXER
|
|
|
|
// iChannel == -1 will halt all channels. Is that right?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (Playing())
|
2009-05-08 13:28:41 +00:00
|
|
|
Mix_HaltChannel(iChannel);
|
2010-09-26 00:36:35 +00:00
|
|
|
#endif
|
|
|
|
#ifdef USE_OPEN_AL
|
|
|
|
if (Playing())
|
|
|
|
alSourceStop(iChannel);
|
2009-05-08 13:28:41 +00:00
|
|
|
#endif
|
|
|
|
iChannel = -1;
|
|
|
|
iStarted = 0;
|
|
|
|
fLooping = false;
|
|
|
|
return fRet;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4SoundInstance::Playing()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
if (!pEffect) return false;
|
2009-11-02 13:18:47 +00:00
|
|
|
#ifdef HAVE_FMOD
|
2010-03-28 18:58:01 +00:00
|
|
|
if (fLooping) return true;
|
2010-03-27 16:05:02 +00:00
|
|
|
return isStarted() ? FSOUND_GetCurrentSample(iChannel) == pEffect->pSample
|
2011-03-13 16:25:35 +00:00
|
|
|
: GetTime() < iStarted + pEffect->Length;
|
2009-05-08 13:28:41 +00:00
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBSDL_MIXER
|
2009-07-07 12:27:19 +00:00
|
|
|
return Application.MusicSystem.MODInitialized && (iChannel != -1) && Mix_Playing(iChannel);
|
2010-09-26 00:36:35 +00:00
|
|
|
#endif
|
|
|
|
#ifdef USE_OPEN_AL
|
|
|
|
if (iChannel == -1)
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ALint state;
|
|
|
|
alGetSourcei(iChannel, AL_SOURCE_STATE, &state);
|
|
|
|
return state == AL_PLAYING;
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
#endif
|
|
|
|
return false;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
void C4SoundInstance::Execute()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Object deleted?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (pObj && !pObj->Status) ClearPointers(pObj);
|
2009-05-08 13:28:41 +00:00
|
|
|
// initial values
|
|
|
|
int32_t iVol = iVolume * 256 * Config.Sound.SoundVolume / 100, iPan = C4SoundInstance::iPan;
|
|
|
|
// bound to an object?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (pObj)
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
int iAudibility = pObj->Audible;
|
|
|
|
// apply custom falloff distance
|
|
|
|
if (iFalloffDistance)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
iAudibility = BoundBy<int32_t>(100 + (iAudibility - 100) * C4AudibilityRadius / iFalloffDistance, 0,100);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
iVol = iVol * iAudibility / 100;
|
|
|
|
iPan += pObj->AudiblePan;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// sound off?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!iVol)
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// stop, if started
|
2010-03-28 18:58:01 +00:00
|
|
|
if (isStarted())
|
|
|
|
{
|
2009-11-02 13:18:47 +00:00
|
|
|
#ifdef HAVE_FMOD
|
2009-05-08 13:28:41 +00:00
|
|
|
FSOUND_StopSound(iChannel);
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBSDL_MIXER
|
|
|
|
Mix_HaltChannel(iChannel);
|
2010-09-26 00:36:35 +00:00
|
|
|
#endif
|
|
|
|
#ifdef USE_OPEN_AL
|
|
|
|
alDeleteSources(1, (ALuint*)&iChannel);
|
2009-05-08 13:28:41 +00:00
|
|
|
#endif
|
|
|
|
iChannel = -1;
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
else
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// start
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!isStarted())
|
|
|
|
if (!CheckStart())
|
2009-05-08 13:28:41 +00:00
|
|
|
return;
|
|
|
|
// set volume & panning
|
2009-11-02 13:18:47 +00:00
|
|
|
#ifdef HAVE_FMOD
|
2009-05-08 13:28:41 +00:00
|
|
|
FSOUND_SetVolume(iChannel, BoundBy(iVol / 100, 0, 255));
|
|
|
|
FSOUND_SetPan(iChannel, BoundBy(256*(iPan+100)/200,0,255));
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBSDL_MIXER
|
|
|
|
Mix_Volume(iChannel, (iVol * MIX_MAX_VOLUME) / (100 * 256));
|
|
|
|
//Mix_SetPanning(iChannel, ((100 + iPan) * 256) / 200, ((100 - iPan) * 256) / 200);
|
|
|
|
Mix_SetPanning(iChannel, BoundBy((100 - iPan) * 256 / 100, 0, 255), BoundBy((100 + iPan) * 256 / 100, 0, 255));
|
2010-09-26 00:36:35 +00:00
|
|
|
#endif
|
|
|
|
#ifdef USE_OPEN_AL
|
|
|
|
alSource3f(iChannel, AL_POSITION, 0, 0, 0); // FIXME
|
|
|
|
alSourcef(iChannel, AL_GAIN, iVol / 100);
|
2009-05-08 13:28:41 +00:00
|
|
|
#endif
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
void C4SoundInstance::SetVolumeByPos(int32_t x, int32_t y)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-09-29 01:44:05 +00:00
|
|
|
iVolume = ::Viewports.GetAudibility(x, y, &iPan);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
void C4SoundInstance::ClearPointers(C4Object *pDelete)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
if (!Playing()) { Stop(); return; }
|
|
|
|
if (pObj == pDelete)
|
2010-03-27 16:05:02 +00:00
|
|
|
{
|
|
|
|
// stop if looping (would most likely loop forever)
|
2010-03-28 18:58:01 +00:00
|
|
|
if (fLooping)
|
2010-03-27 16:05:02 +00:00
|
|
|
Stop();
|
|
|
|
// otherwise: set volume by last position
|
|
|
|
else
|
|
|
|
SetVolumeByPos(pObj->GetX(), pObj->GetY());
|
|
|
|
pObj = NULL;
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
bool C4SoundInstance::Inside(int32_t iX, int32_t iY, int32_t iRad)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
return pObj &&
|
2010-03-28 18:58:01 +00:00
|
|
|
(pObj->GetX() - iX) * (pObj->GetX() - iX) + (pObj->GetY() - iY) * (pObj->GetY() - iY) <= iRad * iRad;
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
C4SoundSystem::C4SoundSystem():
|
2010-03-28 18:58:01 +00:00
|
|
|
FirstSound (NULL)
|
|
|
|
{
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
C4SoundSystem::~C4SoundSystem()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4SoundSystem::Init()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
if (!Application.MusicSystem.MODInitialized &&
|
|
|
|
!Application.MusicSystem.InitializeMOD())
|
2009-05-08 13:28:41 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// Might be reinitialisation
|
|
|
|
ClearEffects();
|
|
|
|
// Open sound file
|
|
|
|
if (!SoundFile.IsOpen())
|
2011-01-06 20:18:13 +00:00
|
|
|
if (!Reloc.Open(SoundFile, C4CFN_Sound)) return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
#ifdef HAVE_LIBSDL_MIXER
|
|
|
|
Mix_AllocateChannels(C4MaxSoundInstances);
|
|
|
|
#endif
|
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
void C4SoundSystem::Clear()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
ClearEffects();
|
|
|
|
// Close sound file
|
|
|
|
SoundFile.Close();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
void C4SoundSystem::ClearEffects()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Clear sound bank
|
|
|
|
C4SoundEffect *csfx,*next;
|
|
|
|
for (csfx=FirstSound; csfx; csfx=next)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
next=csfx->Next;
|
|
|
|
delete csfx;
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
FirstSound=NULL;
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
void C4SoundSystem::Execute()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-09-26 00:36:35 +00:00
|
|
|
#ifdef USE_OPEN_AL
|
|
|
|
Application.MusicSystem.SelectContext();
|
|
|
|
#endif
|
2009-05-08 13:28:41 +00:00
|
|
|
// Sound effect statistics & unload check
|
2010-03-27 16:05:02 +00:00
|
|
|
C4SoundEffect *csfx,*next=NULL,*prev=NULL;
|
|
|
|
for (csfx=FirstSound; csfx; csfx=next)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
next=csfx->Next;
|
|
|
|
// Instance removal check
|
|
|
|
csfx->Execute();
|
|
|
|
// Unload check
|
|
|
|
if (!csfx->Static && Game.Time-csfx->UsageTime > SoundUnloadTime && !csfx->FirstInst)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
if (prev) prev->Next=next;
|
|
|
|
else FirstSound=next;
|
|
|
|
delete csfx;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-03-27 16:05:02 +00:00
|
|
|
else
|
|
|
|
prev=csfx;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
int32_t C4SoundSystem::EffectInBank(const char *szSound)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
int32_t iResult = 0;
|
2010-03-27 16:05:02 +00:00
|
|
|
C4SoundEffect *pSfx;
|
|
|
|
char szName[C4MaxSoundName+4+1];
|
2009-05-08 13:28:41 +00:00
|
|
|
// Compose name (with extension)
|
2010-03-27 16:05:02 +00:00
|
|
|
SCopy(szSound,szName,C4MaxSoundName);
|
2009-05-08 13:28:41 +00:00
|
|
|
DefaultExtension(szName,"wav");
|
|
|
|
// Count all matching sounds in bank
|
2010-03-27 16:05:02 +00:00
|
|
|
for (pSfx=FirstSound; pSfx; pSfx=pSfx->Next)
|
|
|
|
if (WildcardMatch(szName,pSfx->Name))
|
|
|
|
iResult++;
|
|
|
|
return iResult;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
C4SoundEffect* C4SoundSystem::AddEffect(const char *szSoundName)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
C4SoundEffect *nsfx;
|
|
|
|
// Allocate new bank entry
|
|
|
|
if (!( nsfx=new C4SoundEffect )) return NULL;
|
|
|
|
// Load sound to entry
|
2009-05-08 13:28:41 +00:00
|
|
|
C4GRP_DISABLE_REWINDWARN // dynamic load; must rewind here :(
|
2010-03-27 16:05:02 +00:00
|
|
|
if (!nsfx->Load(szSoundName,SoundFile,false))
|
2009-08-15 18:50:32 +00:00
|
|
|
if (!nsfx->Load(szSoundName,Game.ScenarioFile,false))
|
2009-05-08 13:28:41 +00:00
|
|
|
{ C4GRP_ENABLE_REWINDWARN delete nsfx; return NULL; }
|
|
|
|
C4GRP_ENABLE_REWINDWARN
|
2010-03-27 16:05:02 +00:00
|
|
|
// Add sound to bank
|
|
|
|
nsfx->Next=FirstSound;
|
|
|
|
FirstSound=nsfx;
|
|
|
|
return nsfx;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
C4SoundEffect* C4SoundSystem::GetEffect(const char *szSndName)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
C4SoundEffect *pSfx;
|
|
|
|
char szName[C4MaxSoundName+4+1];
|
|
|
|
int32_t iNumber;
|
|
|
|
// Evaluate sound name
|
|
|
|
SCopy(szSndName,szName,C4MaxSoundName);
|
2009-05-08 13:28:41 +00:00
|
|
|
// Default extension
|
|
|
|
DefaultExtension(szName,"wav");
|
|
|
|
// Convert old style '*' wildcard to correct '?' wildcard
|
|
|
|
// For sound effects, '*' is supposed to match single digits only
|
2010-03-27 16:05:02 +00:00
|
|
|
SReplaceChar(szName, '*', '?');
|
2009-05-08 13:28:41 +00:00
|
|
|
// Sound with a wildcard: determine number of available matches
|
|
|
|
if (SCharCount('?',szName))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Search global sound file
|
|
|
|
if (!(iNumber=SoundFile.EntryCount(szName)))
|
|
|
|
// Search scenario local files
|
|
|
|
if (!(iNumber=Game.ScenarioFile.EntryCount(szName)))
|
|
|
|
// Search bank loaded sounds
|
|
|
|
if (!(iNumber=EffectInBank(szName)))
|
|
|
|
// None found: failure
|
|
|
|
return NULL;
|
|
|
|
// Insert index to name
|
2010-03-27 16:05:02 +00:00
|
|
|
iNumber=BoundBy(1+SafeRandom(iNumber),1,9);
|
|
|
|
SReplaceChar(szName,'?','0'+iNumber);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-03-27 16:05:02 +00:00
|
|
|
// Find requested sound effect in bank
|
|
|
|
for (pSfx=FirstSound; pSfx; pSfx=pSfx->Next)
|
|
|
|
if (SEqualNoCase(szName,pSfx->Name))
|
|
|
|
break;
|
|
|
|
// Sound not in bank, try add
|
|
|
|
if (!pSfx)
|
|
|
|
if (!(pSfx = AddEffect(szName)))
|
|
|
|
return NULL;
|
|
|
|
return pSfx;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
C4SoundInstance *C4SoundSystem::NewEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Sound not active
|
2011-09-28 17:42:56 +00:00
|
|
|
if (!Config.Sound.RXSound) return NULL;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Get sound
|
2010-03-27 16:05:02 +00:00
|
|
|
C4SoundEffect *csfx;
|
2011-09-28 17:42:56 +00:00
|
|
|
if (!(csfx=GetEffect(szSndName))) return NULL;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Play
|
2010-03-27 16:05:02 +00:00
|
|
|
return csfx->New(fLoop, iVolume, pObj, iCustomFalloffDistance);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
C4SoundInstance *C4SoundSystem::FindInstance(const char *szSndName, C4Object *pObj)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
char szName[C4MaxSoundName+4+1];
|
|
|
|
// Evaluate sound name (see GetEffect)
|
|
|
|
SCopy(szSndName,szName,C4MaxSoundName);
|
2009-05-08 13:28:41 +00:00
|
|
|
DefaultExtension(szName,"wav");
|
2010-03-27 16:05:02 +00:00
|
|
|
SReplaceChar(szName, '*', '?');
|
|
|
|
// Find an effect with a matching instance
|
2010-03-28 18:58:01 +00:00
|
|
|
for (C4SoundEffect *csfx = FirstSound; csfx; csfx = csfx->Next)
|
|
|
|
if (WildcardMatch(szName, csfx->Name))
|
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
C4SoundInstance *pInst = csfx->GetInstance(pObj);
|
2010-03-28 18:58:01 +00:00
|
|
|
if (pInst) return pInst;
|
|
|
|
}
|
2010-03-27 16:05:02 +00:00
|
|
|
return NULL;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// LoadEffects will load all sound effects of all known sound types (i.e. *.wav and *.ogg)
|
|
|
|
// To play an ogg effect, however, you will have to specify the extension in the sound
|
|
|
|
// command (e.g. Sound("Hello.ogg")), because all playback functions will default to wav only.
|
|
|
|
// LoadEffects is currently used for static loading from object definitions only.
|
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
int32_t C4SoundSystem::LoadEffects(C4Group &hGroup, bool fStatic)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
int32_t iNum=0;
|
|
|
|
char szFilename[_MAX_FNAME+1];
|
|
|
|
char szFileType[_MAX_FNAME+1];
|
|
|
|
C4SoundEffect *nsfx;
|
|
|
|
// Process segmented list of file types
|
|
|
|
for (int32_t i = 0; SCopySegment(C4CFN_SoundFiles, i, szFileType, '|', _MAX_FNAME); i++)
|
|
|
|
{
|
|
|
|
// Search all sound files in group
|
|
|
|
hGroup.ResetSearch();
|
|
|
|
while (hGroup.FindNextEntry(szFileType, szFilename))
|
|
|
|
// Create and load effect
|
2010-01-25 04:00:59 +00:00
|
|
|
if ((nsfx = new C4SoundEffect))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (nsfx->Load(szFilename,hGroup,fStatic))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Overload same name effects
|
|
|
|
RemoveEffect(szFilename);
|
|
|
|
// Add effect
|
|
|
|
nsfx->Next=FirstSound;
|
|
|
|
FirstSound=nsfx;
|
|
|
|
iNum++;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
else
|
|
|
|
delete nsfx;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
return iNum;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
int32_t C4SoundSystem::RemoveEffect(const char *szFilename)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
int32_t iResult=0;
|
|
|
|
C4SoundEffect *pNext,*pPrev=NULL;
|
|
|
|
for (C4SoundEffect *pSfx=FirstSound; pSfx; pSfx=pNext)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
pNext=pSfx->Next;
|
|
|
|
if (WildcardMatch(szFilename,pSfx->Name))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
delete pSfx;
|
|
|
|
if (pPrev) pPrev->Next=pNext;
|
|
|
|
else FirstSound=pNext;
|
|
|
|
iResult++;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
else
|
|
|
|
pPrev=pSfx;
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
return iResult;
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
void C4SoundSystem::ClearPointers(C4Object *pObj)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
for (C4SoundEffect *pEff=FirstSound; pEff; pEff=pEff->Next)
|
2010-03-27 16:05:02 +00:00
|
|
|
pEff->ClearPointers(pObj);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-06-12 18:52:21 +00:00
|
|
|
|
|
|
|
C4SoundInstance *StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// Sound check
|
2011-09-28 17:42:56 +00:00
|
|
|
if (!Config.Sound.RXSound) return NULL;
|
2010-03-27 16:05:02 +00:00
|
|
|
// Start new
|
|
|
|
return Application.SoundSystem.NewEffect(szSndName, fLoop, iVolume, pObj, iCustomFalloffDistance);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-06-12 18:52:21 +00:00
|
|
|
|
|
|
|
C4SoundInstance *StartSoundEffectAt(const char *szSndName, int32_t iX, int32_t iY, bool fLoop, int32_t iVolume)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// Sound check
|
2011-09-28 17:42:56 +00:00
|
|
|
if (!Config.Sound.RXSound) return NULL;
|
2010-03-27 16:05:02 +00:00
|
|
|
// Create
|
|
|
|
C4SoundInstance *pInst = StartSoundEffect(szSndName, fLoop, iVolume);
|
|
|
|
// Set volume by position
|
2010-03-28 18:58:01 +00:00
|
|
|
if (pInst) pInst->SetVolumeByPos(iX, iY);
|
2010-03-27 16:05:02 +00:00
|
|
|
// Return
|
|
|
|
return pInst;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-06-12 18:52:21 +00:00
|
|
|
|
|
|
|
C4SoundInstance *GetSoundInstance(const char *szSndName, C4Object *pObj)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
return Application.SoundSystem.FindInstance(szSndName, pObj);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-06-12 18:52:21 +00:00
|
|
|
|
|
|
|
void StopSoundEffect(const char *szSndName, C4Object *pObj)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// Find instance
|
|
|
|
C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!pInst) return;
|
2010-03-27 16:05:02 +00:00
|
|
|
// Stop
|
|
|
|
pInst->Stop();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-06-12 18:52:21 +00:00
|
|
|
void SoundLevel(const char *szSndName, C4Object *pObj, int32_t iLevel)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// Sound level zero? Stop
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!iLevel) { StopSoundEffect(szSndName, pObj); return; }
|
2010-03-27 16:05:02 +00:00
|
|
|
// Find or create instance
|
|
|
|
C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!pInst) pInst = StartSoundEffect(szSndName, true, iLevel, pObj);
|
|
|
|
if (!pInst) return;
|
2010-03-27 16:05:02 +00:00
|
|
|
// Set volume
|
|
|
|
pInst->SetVolume(iLevel);
|
|
|
|
pInst->Execute();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-06-12 18:52:21 +00:00
|
|
|
void SoundPan(const char *szSndName, C4Object *pObj, int32_t iPan)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// Find instance
|
|
|
|
C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!pInst) return;
|
2010-03-27 16:05:02 +00:00
|
|
|
// Set pan
|
|
|
|
pInst->SetPan(iPan);
|
|
|
|
pInst->Execute();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|