priority & distance based sound scheduler

git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@1787 e016b1c5-ed68-44aa-a2f7-31cac88cffde
This commit is contained in:
driAn
2008-04-12 20:06:28 +00:00
parent f2f0ec4994
commit f67e6493c8
8 changed files with 180 additions and 102 deletions
+141 -81
View File
@@ -7,21 +7,24 @@ bool nosound = true;
VARP(musicvol, 0, 128, 255);
VARP(soundvol, 0, 255, 255);
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alut.h"
#include "vorbis/vorbisfile.h"
void alerr(ALenum error, const char *note)
{
conoutf("OpenAL %s (%s) %X", alutGetErrorString(error), note, error);
}
bool alerr()
{
ALenum er = alutGetError();
if(er) alerr(er, NULL);
return er!=0;
ALenum er = alutGetError(); // alut
if(er)
{
conoutf("OpenAL %s %X", alutGetErrorString(er), er);
return true;
}
else
{
er = alGetError(); // al
if(er)
{
conoutf("OpenAL Error %X", er);
return true;
}
}
return false;
}
struct oggstream
@@ -155,7 +158,7 @@ struct oggstream
struct sbuffer
{
ALuint dat;
ALuint id;
string name;
sbuffer()
@@ -170,7 +173,7 @@ struct sbuffer
bool load(char *sound)
{
alGenBuffers(1, &dat);
alGenBuffers(1, &id);
if(!alerr())
{
const char *exts[] = { "", ".wav", ".ogg" };
@@ -179,7 +182,7 @@ struct sbuffer
{
s_sprintf(filepath)("packages/audio/sounds/%s%s", sound, exts[i]);
const char *file = findfile(path(filepath), "rb");
int len = strlen(filepath);
size_t len = strlen(filepath);
if(len >= 4 && !strcasecmp(filepath + len - 4, ".ogg"))
{
FILE *f = fopen(file, "rb");
@@ -202,7 +205,7 @@ struct sbuffer
loopi(bytes) buf.add(buffer[i]);
} while(bytes > 0);
alBufferData(dat, info->channels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, buf.getbuf(), buf.length(), info->rate);
alBufferData(id, info->channels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, buf.getbuf(), buf.length(), info->rate);
ov_clear(&oggfile);
}
else
@@ -220,7 +223,7 @@ struct sbuffer
alutLoadWAVFile((ALbyte *)file, &format, &data, &size, &freq, &loop);
if(alutGetError()) continue; // try another file extension
alBufferData(dat, format, data, size, freq);
alBufferData(id, format, data, size, freq);
if(alerr()) break;
alutUnloadWAV(format, data, size, freq);
@@ -237,12 +240,8 @@ struct sbuffer
void unload()
{
alDeleteBuffers(1, &dat);
ALenum er;
if((er = alGetError()))
{
alerr(er, "unloading sound");
}
alDeleteBuffers(1, &id);
alerr();
}
};
@@ -262,44 +261,45 @@ struct slot
}
};
// FIXME
VAR(al_reference_distance, 0, 100, 10000);
VAR(al_rolloff_factor, 0, 100, 10000);
// short living sound occurrence, dies once the sound stops
struct location
{
slot *s;
ALuint dat;
ALuint id;
bool inuse;
int priority;
vec pos;
physent *p;
entity *e;
location() : inuse(false)
{
alGenSources(1, &dat);
location() : inuse(false), priority(SP_NORMAL) {};
~location() { delsource(); }
bool gensource()
{
alGenSources(1, &id);
return !alerr();
}
~location()
void delsource()
{
alDeleteSources(1, &dat);
ASSERT(!alGetError());
alDeleteSources(1, &id);
alerr();
}
void assignslot(slot *sl)
{
if(inuse) return;
ASSERT(sl && sl > 0);
s = sl;
alSourcei(dat, AL_BUFFER, s->buf->dat);
alSourcei(dat, AL_LOOPING, s->loop);
alSourcef(dat, AL_GAIN, s->vol/100.0f);
alSourcei(id, AL_BUFFER, s->buf->id);
alSourcei(id, AL_LOOPING, s->loop);
}
void play()
{
alSourcePlay(dat);
alSourcePlay(id);
if(!inuse)
{
s->uses++;
@@ -311,7 +311,7 @@ struct location
void pause()
{
if(!inuse) return;
alSourcePause(dat);
alSourcePause(id);
}
void attachtoworldobj(physent *d)
@@ -319,10 +319,10 @@ struct location
if(inuse) return;
p = d;
e = NULL;
if(p == player1) // disable distance calculations for local sounds
if(p == camera1) // disable distance calculations for local sounds
{
alSourcef(dat, AL_ROLLOFF_FACTOR, 0.0);
alSourcei(dat, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcef(id, AL_ROLLOFF_FACTOR, 0.0);
alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);
}
}
@@ -356,38 +356,42 @@ struct location
if(e) e->soundinuse = false;
e = NULL;
inuse = false;
alSourceStop(dat);
priority = SP_NORMAL;
alSourceStop(id);
// default settings
alSourcei(dat, AL_SOURCE_RELATIVE, AL_FALSE);
alSourcef(dat, AL_REFERENCE_DISTANCE, al_reference_distance/100.0f);
alSourcef(dat, AL_ROLLOFF_FACTOR, al_rolloff_factor/100.0f);
alSource3f(dat, AL_POSITION, 0.0, 0.0, 0.0);
alSource3f(dat, AL_VELOCITY, 0.0, 0.0, 0.0);
alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);
alSourcef(id, AL_REFERENCE_DISTANCE, 1.0f);
alSourcef(id, AL_ROLLOFF_FACTOR, 1.0f);
alSource3f(id, AL_POSITION, 0.0, 0.0, 0.0);
alSource3f(id, AL_VELOCITY, 0.0, 0.0, 0.0);
}
void gain(float g)
{
alSourcef(dat, AL_GAIN, g);
alSourcef(id, AL_GAIN, g);
}
int seconds()
{
ALint secs;
alGetSourcei(dat, AL_SEC_OFFSET, &secs);
alGetSourcei(id, AL_SEC_OFFSET, &secs);
return secs;
}
vec o() { return p ? p->o : e ? vec(e->x, e->y, e->z) : pos; }
bool playing()
{
ALint p;
alGetSourcei(dat, AL_SOURCE_STATE, &p);
alGetSourcei(id, AL_SOURCE_STATE, &p);
return (p == AL_PLAYING);
}
void update()
{
if(!inuse) return;
ALint s; alGetSourcei(dat, AL_SOURCE_STATE, &s);
ALint s; alGetSourcei(id, AL_SOURCE_STATE, &s);
switch(s)
{
case AL_PLAYING:
@@ -404,20 +408,18 @@ struct location
{
if(p) // players
{
if(p==player1) // dampen local sounds
if(p == camera1) gain(s->vol/1000.0f*0.1f);
else
{
gain(0.2f);
}
else // set correct distance
{
alSourcefv(dat, AL_POSITION, (ALfloat *) &p->o);
gain(s->vol/1000.0f);
alSourcefv(id, AL_POSITION, (ALfloat *) &p->o);
}
}
else if(e) // entities
{
alSource3f(dat, AL_POSITION, (float)e->x, (float)e->y, (float)e->z);
alSource3f(id, AL_POSITION, (float)e->x, (float)e->y, (float)e->z);
}
else alSourcefv(dat, AL_POSITION, (ALfloat *) &pos); // static stuff
else alSourcefv(id, AL_POSITION, (ALfloat *) &pos); // static stuff
}
};
@@ -444,10 +446,9 @@ VARF(soundbufferlen, 128, 1024, 4096, initwarning());
void initsound()
{
alutInit(0, NULL);
ALenum er;
if((er = alGetError()))
if(alerr())
{
alerr(er, "initialization failed");
conoutf("sound initialization failed!");
}
else
{
@@ -484,7 +485,7 @@ int findsound(char *name, int vol, vector<slot> &sounds)
{
loopv(sounds)
{
if(!strcmp(sounds[i].buf->name, name) /*&& (!vol || sounds[i].vol==vol)*/) return i;
if(!strcmp(sounds[i].buf->name, name) && (!vol || sounds[i].vol==vol)) return i;
}
return -1;
}
@@ -501,7 +502,8 @@ int addsound(char *name, int vol, int maxuses, bool loop, vector<slot> &sounds)
b = &buffers[n];
b->load(name);
}
sounds.add(slot(b, vol > 0 ? vol : 100, maxuses, loop));
slot s(b, vol > 0 ? vol : 500, maxuses, loop);
sounds.add(s);
return sounds.length()-1;
}
@@ -555,7 +557,7 @@ void updateplayerfootsteps(playerent *p, int sound)
{
const int footstepradius = 16, footstepalign = 15;
location *loc = findsoundloc(sound, p);
bool local = (p == player1);
bool local = (p == camera1);
if((local || (camera1->o.dist(p->o) < footstepradius && footsteps))) // is in range
{
@@ -569,7 +571,7 @@ void updateplayerfootsteps(playerent *p, int sound)
int time = lastmillis-basetime;
int speed = int(1860/p->maxspeed);
// TODO: share with model code
if(time%speed < footstepalign) playsound(sound, p);
if(time%speed < footstepalign) playsound(sound, p, NULL, NULL, local ? SP_HIGH : SP_LOW);
}
}
@@ -618,9 +620,8 @@ int soundrad(int sound)
void updatevol()
{
vec pos(player1->o.x, player1->o.y, player1->o.z+player1->eyeheight);
alListenerfv(AL_POSITION, (ALfloat *) &pos);
alListenerf(AL_GAIN, soundvol/255.0f);
alListener3f(AL_POSITION, camera1->o.x, camera1->o.y, camera1->o.z);
alListenerf(AL_GAIN, soundvol/255.0f*10.0f);
// orientation
vec o[2];
@@ -655,7 +656,7 @@ void updatevol()
VARP(maxsoundsatonce, 0, 40, 100);
void playsound(int n, physent *p, entity *ent, const vec *v)
void playsound(int n, physent *p, entity *ent, const vec *v, int priority)
{
if(nosound || !soundvol) return;
@@ -673,32 +674,91 @@ void playsound(int n, physent *p, entity *ent, const vec *v)
slot &s = sounds[n];
if(ent && s.maxuses && s.uses >= s.maxuses) return;
// get free location item
// AC sound scheduler
static bool sourcesavail = true;
location *loc = NULL;
if(locations.length() < soundchans) loc = &locations.add();
else loopv(locations) if(!locations[i].inuse)
loopv(locations) if(!locations[i].inuse) loc = &locations[i]; // get free item
if(!loc && sourcesavail) // create new and try generating a source
{
loc = &locations[i];
break;
loc = &locations.add();
if(!loc->gensource())
{
sourcesavail = false;
locations.pop();
loc = NULL;
}
}
if(!loc) return;
if(!loc) // no channels left :(
{
loc = NULL;
if(SP_LOW == priority) return;
loopv(locations) // replace stopped or lower prio sound
{
location *l = &locations[i];
if(SP_LOW == l->priority)
{
loc = l;
conoutf("ac sound sched: replaced low prio sound"); // FIXME
break;
}
}
if(!loc)
{
float dist = camera1->o.dist(p ? p->o : ent ? vec(ent->x, ent->y, ent->z) : v ? *v : camera1->o);
float score = dist - priority*10.0f;
location *farthest = NULL;
float farthestscore;
loopv(locations) // still no channel, replace far away sounds of same priority
{
location *l = &locations[i];
if(l->priority <= priority)
{
float ldist = camera1->o.dist(l->o());
float lscore = ldist - l->priority*10.0f;
if(!farthest || lscore > farthestscore)
{
farthest = l;
farthestscore = lscore;
}
}
}
if(farthestscore >= score+5.0f) // better don't play a new one than stopping a playing sound
{
conoutf("ac sound sched: replaced sound of same prio"); // FIXME
}
}
}
if(!loc)
{
conoutf("ac sound sched: sound aborted, no channel takeover possible"); // FIXME
return;
}
loc->priority = priority;
// attach to world obj
if(p) loc->attachtoworldobj(p);
else if(ent) loc->attachtoworldobj(ent);
else if(v) loc->attachtoworldobj(v);
else loc->attachtoworldobj(player1);
else loc->attachtoworldobj(camera1);
loc->assignslot(&s); // assign sound slot
loc->play();
}
void playsound(int n, int priority) { playsound(n, NULL, NULL, NULL, priority); };
void playsound(int n, physent *p, int priority) { playsound(n, p, NULL, NULL, priority); };
void playsound(int n, entity *e, int priority) { playsound(n, NULL, e, NULL, priority); };
void playsound(int n, const vec *v, int priority) { playsound(n, NULL, NULL, v, priority); };
void playsoundname(char *s, const vec *loc, int vol)
{
if(!vol) vol = 100;
int id = findsound(s, vol, gamesounds);
if(id < 0) id = addsound(s, vol, 0, false, gamesounds);
playsound(id, NULL, NULL, loc);
playsound(id, loc);
}
void sound(int n) { playsound(n); }
@@ -719,7 +779,7 @@ void voicecom(char *sound, char *text)
s_sprintfd(soundpath)("voicecom/%s", sound);
int s = findsound(soundpath, 0, gamesounds);
if(s < 0 || s < S_AFFIRMATIVE || s > S_NICESHOT) return;
playsound(s);
playsound(s, SP_HIGH);
if(s == S_NICESHOT) // public
{
addmsg(SV_VOICECOM, "ri", s);