Added pitch parameter to Sound() script function and allow pitch and sound level to be modified for sounds that have already been started.

Sound() called when the instance is already running used to fail. Now, it always succeeds (also for script sync safety) but updates the sound level and pitch parameters. SoundAt has not been modified since it allows creation of multiple concurrent sound instance of the same effect without object context.
lights3
Sven Eberhardt 2015-08-09 18:40:42 -04:00
parent 8ca07b7bc4
commit d92df758a8
5 changed files with 89 additions and 25 deletions

View File

@ -45,9 +45,16 @@
<desc>The further away the sound effect from the player, the more quiet it is played. By default, the sound will not be hearable anymore in a distance of 700 pixels. A custom distance can be specified here.</desc>
<optional />
</param>
<param>
<type>int</type>
<name>pitch</name>
<desc>Pitch modification between -90 and 1000. Values larger than zero let the sound play the faster and at a higher pitch. The resulting speed multiplication factor is (pitch + 100) / 100.</desc>
<optional />
</param>
</params>
</syntax>
<desc>Plays a sound. The specified sound file has to be available in the group Sound.ocg, in the active scenario file, or in any loaded object definition. The audibility of object local sounds will depend on the position of the object relative to the visible viewports.</desc>
<remark>When a sound effect is already played in the same context (calling object or globally), it is not played again. Instead, only the volume and pitch parameters are updated. This feature can be used to adjust sound parameters live e.g. to tune the pitch of a motor sound based on its speed.</remark>
<examples>
<example>
<code><funclink>FindObject</funclink>(<funclink>Find_ID</funclink>(WindGenerator))-&gt;Sound(&quot;Fanfare&quot;, false, 50);</code>

View File

@ -43,6 +43,12 @@
<desc>The further away the sound effect from the player, the more quiet it is played. By default, the sound will not be hearable anymore in a distance of 700 pixels. A custom distance can be specified here.</desc>
<optional />
</param>
<param>
<type>int</type>
<name>pitch</name>
<desc>Pitch modification between -90 and 1000. Values larger than zero let the sound play the faster and at a higher pitch. The resulting speed multiplication factor is (pitch + 100) / 100.</desc>
<optional />
</param>
</params>
</syntax>
<desc>Plays a sound at the specified position. The specified sound file has to be available in the group Sound.ocg, in the active scenario file, or in any loaded object definition.</desc>

View File

@ -485,7 +485,7 @@ static C4Void FnBlastFree(C4PropList * _this, long iX, long iY, long iLevel, Nil
return C4Void();
}
static bool FnSoundAt(C4PropList * _this, C4String *szSound, long iX, long iY, Nillable<long> iLevel, Nillable<long> iAtPlayer, long iCustomFalloffDistance)
static bool FnSoundAt(C4PropList * _this, C4String *szSound, long iX, long iY, Nillable<long> iLevel, Nillable<long> iAtPlayer, long iCustomFalloffDistance, long iPitch)
{
// play here?
if (!iAtPlayer.IsNil())
@ -510,12 +510,12 @@ static bool FnSoundAt(C4PropList * _this, C4String *szSound, long iX, long iY, N
iX += pObj->GetX();
iY += pObj->GetY();
}
StartSoundEffectAt(FnStringPar(szSound),iX,iY,iLevel,iCustomFalloffDistance);
StartSoundEffectAt(FnStringPar(szSound), iX, iY, iLevel, iCustomFalloffDistance, iPitch);
// always return true (network safety!)
return true;
}
static bool FnSound(C4PropList * _this, C4String *szSound, bool fGlobal, Nillable<long> iLevel, Nillable<long> iAtPlayer, long iLoop, long iCustomFalloffDistance)
static bool FnSound(C4PropList * _this, C4String *szSound, bool fGlobal, Nillable<long> iLevel, Nillable<long> iAtPlayer, long iLoop, long iCustomFalloffDistance, long iPitch)
{
// play here?
if (!iAtPlayer.IsNil())
@ -536,14 +536,26 @@ static bool FnSound(C4PropList * _this, C4String *szSound, bool fGlobal, Nillabl
// target object
C4Object *pObj = NULL;
if (!fGlobal) pObj = Object(_this);
// already playing?
if (iLoop >= 0 && GetSoundInstance(FnStringPar(szSound), pObj))
return false;
// try to play effect
// play/stop?
if (iLoop >= 0)
StartSoundEffect(FnStringPar(szSound),!!iLoop,iLevel,pObj, iCustomFalloffDistance);
{
// already playing?
C4SoundInstance *inst = GetSoundInstance(FnStringPar(szSound), pObj);
if (inst)
{
// then just update parameters
SoundUpdate(inst, iLevel, iPitch);
}
else
{
// try to play effect
StartSoundEffect(FnStringPar(szSound), !!iLoop, iLevel, pObj, iCustomFalloffDistance, iPitch);
}
}
else
StopSoundEffect(FnStringPar(szSound),pObj);
{
StopSoundEffect(FnStringPar(szSound), pObj);
}
// always return true (network safety!)
return true;
}

View File

@ -47,7 +47,7 @@ public:
bool Load(const char *szFileName, C4Group &hGroup);
bool Load(BYTE *pData, size_t iDataLen, bool fRaw=false); // load directly from memory
void Execute();
C4SoundInstance *New(bool fLoop = false, int32_t iVolume = 100, C4Object *pObj = NULL, int32_t iCustomFalloffDistance = 0);
C4SoundInstance *New(bool fLoop = false, int32_t iVolume = 100, C4Object *pObj = NULL, int32_t iCustomFalloffDistance = 0, int32_t iPitch = 0);
C4SoundInstance *GetInstance(C4Object *pObj);
void ClearPointers(C4Object *pObj);
int32_t GetStartedInstanceCount(int32_t iX, int32_t iY, int32_t iRad); // local
@ -66,7 +66,8 @@ public:
~C4SoundInstance();
protected:
C4SoundEffect *pEffect;
int32_t iVolume, iPan, iChannel;
int32_t iVolume, iPan, iPitch, iChannel;
bool pitch_dirty;
C4TimeMilliseconds tStarted;
int32_t iNearInstanceMax;
bool fLooping;
@ -77,7 +78,7 @@ public:
C4Object *getObj() const { return pObj; }
bool isStarted() const { return iChannel != -1; }
void Clear();
bool Create(C4SoundEffect *pEffect, bool fLoop = false, int32_t iVolume = 100, C4Object *pObj = NULL, int32_t iNearInstanceMax = 0, int32_t iFalloffDistance = 0);
bool Create(C4SoundEffect *pEffect, bool fLoop = false, int32_t iVolume = 100, C4Object *pObj = NULL, int32_t iNearInstanceMax = 0, int32_t iFalloffDistance = 0, int32_t inPitch = 0);
bool CheckStart();
bool Start();
bool Stop();
@ -85,6 +86,7 @@ public:
void Execute();
void SetVolume(int32_t inVolume) { iVolume = inVolume; }
void SetPan(int32_t inPan) { iPan = inPan; }
void SetPitch(int32_t inPitch);
void SetVolumeByPos(int32_t x, int32_t y);
void SetObj(C4Object *pnObj) { pObj = pnObj; }
void ClearPointers(C4Object *pObj);
@ -185,13 +187,13 @@ void C4SoundEffect::Execute()
}
}
C4SoundInstance *C4SoundEffect::New(bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance)
C4SoundInstance *C4SoundEffect::New(bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch)
{
// check: too many instances?
if (!fLoop && Instances >= C4MaxSoundInstances) return NULL;
// create & init sound instance
C4SoundInstance *pInst = new C4SoundInstance();
if (!pInst->Create(this, fLoop, iVolume, pObj, 0, iCustomFalloffDistance)) { delete pInst; return NULL; }
if (!pInst->Create(this, fLoop, iVolume, pObj, 0, iCustomFalloffDistance, iPitch)) { delete pInst; return NULL; }
// add to list
AddInst(pInst);
// return
@ -254,7 +256,8 @@ void C4SoundEffect::RemoveInst(C4SoundInstance *pInst)
C4SoundInstance::C4SoundInstance():
pEffect (NULL),
iVolume (0), iPan (0),
iVolume(0), iPan(0), iPitch(0),
pitch_dirty(false),
iChannel (-1),
pNext (NULL)
{
@ -271,7 +274,7 @@ void C4SoundInstance::Clear()
iChannel = -1;
}
bool C4SoundInstance::Create(C4SoundEffect *pnEffect, bool fLoop, int32_t inVolume, C4Object *pnObj, int32_t inNearInstanceMax, int32_t iFalloffDistance)
bool C4SoundInstance::Create(C4SoundEffect *pnEffect, bool fLoop, int32_t inVolume, C4Object *pnObj, int32_t inNearInstanceMax, int32_t iFalloffDistance, int32_t inPitch)
{
// Sound check
if (!Config.Sound.RXSound || !pnEffect) return false;
@ -282,6 +285,7 @@ bool C4SoundInstance::Create(C4SoundEffect *pnEffect, bool fLoop, int32_t inVolu
// Set
tStarted = C4TimeMilliseconds::Now();
iVolume = inVolume; iPan = 0; iChannel = -1;
iPitch = inPitch; pitch_dirty = (iPitch != 0);
iNearInstanceMax = inNearInstanceMax;
this->iFalloffDistance = iFalloffDistance;
pObj = pnObj;
@ -291,6 +295,13 @@ bool C4SoundInstance::Create(C4SoundEffect *pnEffect, bool fLoop, int32_t inVolu
return true;
}
void C4SoundInstance::SetPitch(int32_t inPitch)
{
// set pitch and update on next call to Execute
iPitch = inPitch;
pitch_dirty = true;
}
bool C4SoundInstance::CheckStart()
{
// already started?
@ -441,6 +452,12 @@ void C4SoundInstance::Execute()
#elif AUDIO_TK == AUDIO_TK_OPENAL
alSource3f(iChannel, AL_POSITION, Clamp<float>(float(iPan)/100.0f, -1.0f, +1.0f), 0, 0.7f);
alSourcef(iChannel, AL_GAIN, float(iVol) / (100.0f*256.0f));
if (pitch_dirty)
{
// set pitch; map -90..+100 range to 0.1f..2.0f
alSourcef(iChannel, AL_PITCH, Max<float>(float(iPitch + 100) / 100.0f, 0.1f));
pitch_dirty = false;
}
#endif
}
}
@ -566,7 +583,7 @@ C4SoundEffect* C4SoundSystem::GetEffect(const char *szSndName)
return pSfx; // Is still NULL if nothing is found
}
C4SoundInstance *C4SoundSystem::NewEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance)
C4SoundInstance *C4SoundSystem::NewEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch)
{
// Sound not active
if (!Config.Sound.RXSound) return NULL;
@ -574,7 +591,7 @@ C4SoundInstance *C4SoundSystem::NewEffect(const char *szSndName, bool fLoop, int
C4SoundEffect *csfx;
if (!(csfx=GetEffect(szSndName))) return NULL;
// Play
return csfx->New(fLoop, iVolume, pObj, iCustomFalloffDistance);
return csfx->New(fLoop, iVolume, pObj, iCustomFalloffDistance, iPitch);
}
C4SoundInstance *C4SoundSystem::FindInstance(const char *szSndName, C4Object *pObj)
@ -659,20 +676,20 @@ void C4SoundSystem::ClearPointers(C4Object *pObj)
pEff->ClearPointers(pObj);
}
C4SoundInstance *StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance)
C4SoundInstance *StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch)
{
// Sound check
if (!Config.Sound.RXSound) return NULL;
// Start new
return Application.SoundSystem.NewEffect(szSndName, fLoop, iVolume, pObj, iCustomFalloffDistance);
return Application.SoundSystem.NewEffect(szSndName, fLoop, iVolume, pObj, iCustomFalloffDistance, iPitch);
}
C4SoundInstance *StartSoundEffectAt(const char *szSndName, int32_t iX, int32_t iY, int32_t iVolume, int32_t iCustomFallofDistance)
C4SoundInstance *StartSoundEffectAt(const char *szSndName, int32_t iX, int32_t iY, int32_t iVolume, int32_t iCustomFallofDistance, int32_t iPitch)
{
// Sound check
if (!Config.Sound.RXSound) return NULL;
// Create
C4SoundInstance *pInst = StartSoundEffect(szSndName, false, iVolume, NULL, iCustomFallofDistance);
C4SoundInstance *pInst = StartSoundEffect(szSndName, false, iVolume, NULL, iCustomFallofDistance, iPitch);
// Set volume by position
if (pInst) pInst->SetVolumeByPos(iX, iY);
// Return
@ -704,6 +721,7 @@ void SoundLevel(const char *szSndName, C4Object *pObj, int32_t iLevel)
pInst->SetVolume(iLevel);
pInst->Execute();
}
void SoundPan(const char *szSndName, C4Object *pObj, int32_t iPan)
{
// Find instance
@ -713,3 +731,22 @@ void SoundPan(const char *szSndName, C4Object *pObj, int32_t iPan)
pInst->SetPan(iPan);
pInst->Execute();
}
void SoundPitch(const char *szSndName, C4Object *pObj, int32_t iPitch)
{
// Find instance
C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
if (!pInst) return;
// Set pitch
pInst->SetPitch(iPitch);
pInst->Execute();
}
void SoundUpdate(C4SoundInstance *pInst, int32_t iLevel, int32_t iPitch)
{
// Set sound data
pInst->SetVolume(iLevel);
pInst->SetPitch(iPitch);
// Ensure it's reflected in audio engine
pInst->Execute();
}

View File

@ -41,7 +41,7 @@ public:
void Clear();
void Execute();
int32_t LoadEffects(C4Group &hGroup);
C4SoundInstance *NewEffect(const char *szSound, bool fLoop = false, int32_t iVolume = 100, C4Object *pObj = NULL, int32_t iCustomFalloffDistance = 0);
C4SoundInstance *NewEffect(const char *szSound, bool fLoop = false, int32_t iVolume = 100, C4Object *pObj = NULL, int32_t iCustomFalloffDistance = 0, int32_t iPitch = 0);
C4SoundInstance *FindInstance(const char *szSound, C4Object *pObj);
bool Init();
void ClearPointers(C4Object *pObj);
@ -53,12 +53,14 @@ protected:
int32_t RemoveEffect(const char *szFilename);
};
C4SoundInstance *StartSoundEffect(const char *szSndName, bool fLoop = false, int32_t iVolume = 100, C4Object *pObj=NULL, int32_t iCustomFalloffDistance=0);
C4SoundInstance *StartSoundEffectAt(const char *szSndName, int32_t iX, int32_t iY, int32_t iVolume = 100, int32_t iCustomFallofDistance=0);
C4SoundInstance *StartSoundEffect(const char *szSndName, bool fLoop = false, int32_t iVolume = 100, C4Object *pObj = NULL, int32_t iCustomFalloffDistance = 0, int32_t iPitch = 0);
C4SoundInstance *StartSoundEffectAt(const char *szSndName, int32_t iX, int32_t iY, int32_t iVolume = 100, int32_t iCustomFallofDistance = 0, int32_t iPitch = 0);
C4SoundInstance *GetSoundInstance(const char *szSndName, C4Object *pObj);
void StopSoundEffect(const char *szSndName, C4Object *pObj);
void SoundLevel(const char *szSndName, C4Object *pObj, int32_t iLevel);
void SoundPan(const char *szSndName, C4Object *pObj, int32_t iPan);
void SoundPitch(const char *szSndName, C4Object *pObj, int32_t iPitch);
void SoundUpdate(C4SoundInstance *inst, int32_t iLevel, int32_t iPitch);
#endif