FANDOM


// AC sound scheduler, manages available sound sources.
// It keeps a set of reserved sources for each priority level to avoid running out of sources by floods of low-priority sounds.
// Under load it uses priority and distance information to reassign its resources.
 
#include "cube.h"
 
#define DEBUGCOND (audiodebug==1)
 
VARP(soundschedpriorityscore, 0, 100, 1000);
VARP(soundscheddistancescore, 0, 5, 1000);
VARP(soundschedoldbonus, 0, 100, 1000);
VARP(soundschedreserve, 0, 2, 100);
 
sourcescheduler *sourcescheduler::inst;
 
sourcescheduler::sourcescheduler() : numsoundchannels(0) {}
 
sourcescheduler &sourcescheduler::instance()
{
    if(inst==NULL) inst = new sourcescheduler();
    return *inst;
}
 
void sourcescheduler::init(int numsoundchannels)
{
    this->numsoundchannels = numsoundchannels;
    int newchannels = numsoundchannels - sources.length();
    if(newchannels < 0)
    {
        loopv(sources)
        {
            source *src = sources[i];
            if(src->locked) continue;
            sources.remove(i--);
            delete src;
            if(sources.length() <= numsoundchannels) break;
        }
    }
    else loopi(newchannels)
    {
        source *src = new source();
        if(src->valid) sources.add(src);
        else
        {
            DELETEP(src);
            break;
        }
    }
}
 
void sourcescheduler::reset()
{
    loopv(sources) sources[i]->reset();
    sources.deletecontents();
}
 
// returns a free sound source (channel)
// consuming code must call sourcescheduler::releasesource() after use
 
source *sourcescheduler::newsource(int priority, const vec &o)
{
    if(!sources.length())
    {
        DEBUG("empty source collection");
        return NULL;
    }
 
    source *src = NULL;
 
    // reserve some sources for sounds of higher priority
    int reserved = (SP_HIGHEST-priority)*soundschedreserve;
    DEBUGVAR(reserved);
 
    // search unused source
    loopv(sources)
    {
        if(!sources[i]->locked && reserved--<=0)
        {
            src = sources[i];
            DEBUGVAR(src);
            break;
        }
    }
 
    if(!src)
    {
        DEBUG("no empty source found");
 
        // low priority sounds can't replace others
        if(SP_LOW==priority)
        {
            DEBUG("low prio sound aborted");
            return NULL;
        }
 
        // try replacing a used source
        // score our sound
        const float dist = o.iszero() ? 0.0f : camera1->o.dist(o);
        const float score = (priority*soundschedpriorityscore) - (dist*soundscheddistancescore);
 
        // score other sounds
        float worstscore = 0.0f;
        source *worstsource = NULL;
 
        loopv(sources)
        {
            source *s = sources[i];
            if(s->priority==SP_HIGHEST) continue; // highest priority sounds can't be replaced
 
            const vec & otherpos = s->position();
            float otherdist = otherpos.iszero() ? 0.0f : camera1->o.dist(otherpos);
            float otherscore = (s->priority*soundschedpriorityscore) - (otherdist*soundscheddistancescore) + soundschedoldbonus;
            if(!worstsource || otherscore < worstscore)
            {
                worstsource = s;
                worstscore = otherscore;
            }
        }
 
        // pick worst source and replace it
        if(worstsource && score>worstscore)
        {
            src = worstsource;
            src->onreassign(); // inform previous owner about the take-over
            DEBUG("replaced sound of same prio");
        }
    }
 
    if(!src)
    {
        DEBUG("sound aborted, no channel takeover possible");
        return NULL;
    }
 
    src->reset();       // default settings
    src->lock();        // exclusive lock
    src->priority = priority;
    return src;
}
 
// give source back to the pool
void sourcescheduler::releasesource(source *src)
{
    ASSERT(src);
    ASSERT(src->locked); // detect double release
 
    if(!src) return;
 
    DEBUG("unlocking source");
 
    src->unlock();
 
    if(sources.length() > numsoundchannels)
    {
        sources.removeobj(src);
        delete src;
        DEBUG("source deleted");
    }
}

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.