// 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");
}
}
Skip to content
153
pages