(c++) irrklang animator (for queueing sounds)

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
xDan
Competition winner
Posts: 673
Joined: Thu Mar 30, 2006 1:23 pm
Location: UK
Contact:

(c++) irrklang animator (for queueing sounds)

Post by xDan »

Hi,

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
SoundAnimator.cpp

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;
}

SoundAnimator.h

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
Notes:
- see the code for other play() flags
- SOUNDANIM_BREAK_LOOP is a bit dodgy. irrklang needs setIsLooped()?
koller202
Posts: 143
Joined: Tue May 08, 2007 4:53 am
Location: Thailand

Post by koller202 »

Last edited by koller202 on Thu Feb 17, 2011 1:15 am, edited 1 time in total.
Ico
Posts: 289
Joined: Tue Aug 22, 2006 11:35 pm

Post by Ico »

Looks interesting - but as far as I know you shouldn't drop something as long as you still want to call some methods - maybe it's just the sample code, don't know.

Maybe you should handle your listener using a private static variable - could think of some odd problems with more nodes being active listeners
Post Reply