I needed some method of queueing sounds...
Usage Example
Code: Select all
// attach a SoundAnimator to the camera as a listener
cameraSoundAnim = new SoundAnimator(sengine);
smgr->getActiveCamera()->addAnimator(cameraSoundAnim);
cameraSoundAnim->setAsListener(true);
cameraSoundAnim->drop();
// In my game I have a fuel powered generator... attach new animator to it.
SoundAnimator *soundAnimator = new SoundAnimator(sengine);
generatorSceneNode->addAnimator(soundAnimator);
soundAnimator->drop();
// Generator has been refueled and started. Queue generator starting sound and looping sound.
soundAnimator->play("media/sfx/generatorstart.wav", SOUNDANIM_ENQUEUE);
soundAnimator->play("media/sfx/generatorloop.wav", SOUNDANIM_ENQUEUE|SOUNDANIM_LOOP);
// then to interrupt (e.g. generator explodes when player shoots it):
soundAnimator->clearAll(); // clear any currently playing sounds
soundAnimator->play("explode.wav", 0); // zero for second flags as no queueing or anything needed
Code: Select all
#include "SoundAnimator.h"
SoundAnimator::SoundAnimator(audio::ISoundEngine *sengine)
{
this->sengine = sengine;
lastTimeMs = 0;
listener = false;
}
SoundAnimator::~SoundAnimator()
{
clearAll();
}
void SoundAnimator::clearAll()
{
// stop and remove all sounds
for (u32 i = 0; i < sounds.size(); i ++)
{
if (sounds[i].isound)
{
if (!sounds[i].isound->isFinished()) sounds[i].isound->stop();
sounds[i].isound->drop();
}
}
sounds.clear();
}
audio::ISound *SoundAnimator::play(core::stringc fileName, u32 flags)
{
Sound s =
{
fileName,
flags,
sengine->play3D(fileName.c_str(), core::vector3df(0,0,0), flags & SOUNDANIM_LOOP, true, true),
false,
0
};
if (s.isound) sounds.push_back(s);
return s.isound;
}
void SoundAnimator::breakLoop()
{
Sound s =
{
core::stringc(),
SOUNDANIM_ENQUEUE|SOUNDANIM_BREAK_LOOP,
NULL,
false,
0
};
sounds.push_back(s);
}
void SoundAnimator::animateNode(scene::ISceneNode *node, u32 timeMs)
{
if (!node) return;
// position...
core::vector3df pos = node->getAbsolutePosition();
// on first frame...
if (lastTimeMs == 0)
{
// initial velocity of zero
lastTimeMs = timeMs;
lastPos = pos;
}
// calculate dt
f32 dt = ((f32)(timeMs-lastTimeMs)) / 1000.0;
lastTimeMs = timeMs;
// velocity (for doppler effect)
core::vector3df vel = (pos - lastPos) / dt;
lastPos = pos;
if (listener)
{
// up vector
core::vector3df up(0,1,0);
node->getAbsoluteTransformation().rotateVect(up);
// forward vector
core::vector3df forwards(0,0,1);
node->getAbsoluteTransformation().rotateVect(forwards);
// set listener
sengine->setListenerPosition(pos, forwards, vel, up);
}
bool noWait = true;
for (u32 i = 0; i < sounds.size(); i ++)
{
if (sounds[i].started)
{
// remove if finished OR has SOUNDANIM_UNTIL_NEXT and another sound waiting
int endSound = (sounds[i].isound->isFinished()
|| ( (sounds[i].flags & SOUNDANIM_UNTIL_NEXT) && sounds.size() > i+1)
);
// OR if: SOUNDANIM_LOOP and current getPlayPosition is smaller than last (loop over)
// and another sound waiting and other sound has SOUNDANIM_BREAK_LOOP
if ( (sounds[i].flags & SOUNDANIM_LOOP) && sounds.size() > i+1
&& sounds[i].isound->getPlayPosition() < sounds[i].lastPlayPosition)
{
endSound += sounds[i+1].flags & SOUNDANIM_BREAK_LOOP;
}
if (endSound)
{
// remove a finished sound
if (!sounds[i].isound->isFinished()) sounds[i].isound->stop();
sounds[i].isound->drop();
sounds.erase(i);
i --;
continue;
}
else
{
sounds[i].isound->setPosition(pos);
sounds[i].isound->setVelocity(vel);
sounds[i].lastPlayPosition = sounds[i].isound->getPlayPosition();
}
}
else // not playing yet, so decide whether it needs playing
{
if (sounds[i].flags & SOUNDANIM_ENQUEUE)
{
// Will play if first in queue OR all sounds so far were SOUNDANIM_NO_WAIT
if (noWait)
{
if (sounds[i].fileName.size() == 0)
{
// empty sound? remove it. Probably from breakLoop().
sounds.erase(i);
i --;
continue;
}
sounds[i].started = true;
sounds[i].isound->setIsPaused(false);
sounds[i].isound->setPosition(pos);
sounds[i].isound->setVelocity(vel);
}
}
else // play instantly
{
sounds[i].started = true;
sounds[i].isound->setIsPaused(false);
sounds[i].isound->setPosition(pos);
sounds[i].isound->setVelocity(vel);
continue;
}
}
if ( !(sounds[i].flags & SOUNDANIM_NO_WAIT) && !(sounds[i].flags & SOUNDANIM_UNTIL_NEXT) )
{
noWait = false;
}
}
}
void SoundAnimator::setAsListener(bool f)
{
listener = f;
}
Code: Select all
#ifndef __SoundAnimator_h
#define __SoundAnimator_h
#include "ISceneNode.h"
#include <irrKlang.h>
using namespace irr;
enum
{
SOUNDANIM_LOOP = 1 << 0, // loop sound (otherwise play once)
SOUNDANIM_ENQUEUE = 1 << 1, // add to queue (otherwise play immediately)
SOUNDANIM_NO_WAIT = 1 << 2, // next sound will not wait until this has finished
SOUNDANIM_UNTIL_NEXT = 1 << 3, // sound will end as soon as another available (added to queue)
SOUNDANIM_BREAK_LOOP = 1 << 4 // if the previous sound is looped, sound will start at end of its current loop.
};
struct Sound
{
core::stringc fileName;
u32 flags;
audio::ISound *isound;
bool started;
u32 lastPlayPosition;
};
class SoundAnimator : public scene::ISceneNodeAnimator
{
public:
SoundAnimator(audio::ISoundEngine *);
~SoundAnimator();
void clearAll();
audio::ISound *play(core::stringc, u32);
void breakLoop();
void animateNode(scene::ISceneNode *, u32);
// should only set one animator as listener
// (e.g. attached to camera scene node)
void setAsListener(bool);
private:
audio::ISoundEngine *sengine;
u32 lastTimeMs;
bool listener;
core::vector3df lastPos;
core::array <Sound> sounds;
};
#endif
- see the code for other play() flags
- SOUNDANIM_BREAK_LOOP is a bit dodgy. irrklang needs setIsLooped()?