From f67e6493c88698ccadf14fe3a4496ed178fa5df4 Mon Sep 17 00:00:00 2001 From: driAn Date: Sat, 12 Apr 2008 20:06:28 +0000 Subject: [PATCH] priority & distance based sound scheduler git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@1787 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/src/clientgame.cpp | 16 +-- source/src/clients2c.cpp | 4 +- source/src/pch.h | 5 + source/src/protos.h | 5 +- source/src/renderparticles.cpp | 4 +- source/src/sound.cpp | 222 +++++++++++++++++++++------------ source/src/sound.h | 7 ++ source/src/weapon.cpp | 19 +-- 8 files changed, 180 insertions(+), 102 deletions(-) diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index dd50b64f..dc2eaf92 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -451,7 +451,7 @@ void dokill(playerent *pl, playerent *act, bool gib) } if(gib) { - if(pl!=act && act->weaponsel->type == GUN_SNIPER) playsound(S_HEADSHOT); + if(pl!=act && act->weaponsel->type == GUN_SNIPER) playsound(S_HEADSHOT, SP_LOW); addgib(pl); } if(!m_mp(gamemode)) @@ -604,28 +604,28 @@ void flagmsg(int flag, int action) { case SV_FLAGPICKUP: { - playsound(S_FLAGPICKUP); + playsound(S_FLAGPICKUP, SP_HIGH); if(firstperson) conoutf("\f2you got the enemy flag"); else conoutf("\f2%s got %s flag", colorname(f.actor), teamstr); break; } case SV_FLAGDROP: { - playsound(S_FLAGDROP); + playsound(S_FLAGDROP, SP_HIGH); if(firstperson) conoutf("\f2you lost the flag"); else conoutf("\f2%s lost %s flag", colorname(f.actor), teamstr); break; } case SV_FLAGRETURN: { - playsound(S_FLAGRETURN); + playsound(S_FLAGRETURN, SP_HIGH); if(firstperson) conoutf("\f2you returned your flag"); else conoutf("\f2%s returned %s flag", colorname(f.actor), teamstr); break; } case SV_FLAGSCORE: { - playsound(S_FLAGSCORE); + playsound(S_FLAGSCORE, SP_HIGH); if(firstperson) { conoutf("\f2you scored"); @@ -636,7 +636,7 @@ void flagmsg(int flag, int action) } case SV_FLAGRESET: { - playsound(S_FLAGRETURN); + playsound(S_FLAGRETURN, SP_HIGH); conoutf("the server reset the flag"); break; } @@ -740,7 +740,7 @@ void displayvote(votedisplayinfo *v) DELETEP(curvote); curvote = v; conoutf("%s called a vote: %s", v->owner ? colorname(v->owner) : "", curvote->desc); - playsound(S_CALLVOTE); + playsound(S_CALLVOTE, SP_HIGH); curvote->localplayervoted = false; } @@ -768,7 +768,7 @@ void voteresult(int v) curvote->result = v; curvote->millis = lastmillis + 5000; conoutf("vote %s", v == VOTE_YES ? "passed" : "failed"); - playsound(v == VOTE_YES ? S_VOTEPASS : S_VOTEFAIL); + playsound(v == VOTE_YES ? S_VOTEPASS : S_VOTEFAIL, SP_HIGH); } } diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 9e27befc..e48cd921 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -169,12 +169,12 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) { playerent *d = getclient(getint(p)); if(d) d->lastvoicecom = lastmillis; - playsound(getint(p)); + playsound(getint(p), SP_HIGH); break; } case SV_VOICECOM: { - playsound(getint(p)); + playsound(getint(p), SP_HIGH); if(d) d->lastvoicecom = lastmillis; break; } diff --git a/source/src/pch.h b/source/src/pch.h index bb41d088..c37edbfa 100644 --- a/source/src/pch.h +++ b/source/src/pch.h @@ -48,3 +48,8 @@ #include #include +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alut.h" +#include "vorbis/vorbisfile.h" + diff --git a/source/src/protos.h b/source/src/protos.h index 477ef72e..238dda01 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -425,7 +425,10 @@ extern float floor(short x, short y); extern void updatecrouch(playerent *p, bool on); // sound -extern void playsound(int n, physent *p = NULL, entity *ent = NULL, const vec *v = NULL); +extern void playsound(int n, physent *p = NULL, entity *ent = NULL, const vec *v = NULL, int priority = SP_NORMAL); +extern void playsound(int n, int priority); +extern void playsound(int n, entity *e, int priority = SP_NORMAL); +extern void playsound(int n, const vec *v, int priority = SP_NORMAL); extern void playsoundc(int n); extern void initsound(); extern void cleansound(); diff --git a/source/src/renderparticles.cpp b/source/src/renderparticles.cpp index 02237ec5..f56bf5ec 100644 --- a/source/src/renderparticles.cpp +++ b/source/src/renderparticles.cpp @@ -612,7 +612,7 @@ bool addbullethole(vec &from, vec &to, float radius, bool noisy) o.add(ray.mul(dist)); o.add(vec(surface).mul(0.005f)); newparticle(o, surface, bulletholettl, 7); - if(noisy && bulletbouncesoundrad && o.dist(camera1->o) <= bulletbouncesoundrad) playsound(S_BULLETHIT, NULL, NULL, &o); + if(noisy && bulletbouncesoundrad && o.dist(camera1->o) <= bulletbouncesoundrad) playsound(S_BULLETHIT, &o, SP_LOW); return true; } @@ -660,6 +660,6 @@ void addshotline(dynent *pl, vec &from, vec &to) soundpos.mul(fd/(fd+td)*dist); soundpos.add(from); if(soundpos.dist(camera1->o) > bulletairsoundrad) return; // outside player radius - playsound(S_BULLETAIR1 + rnd(2), NULL, NULL, &soundpos); + playsound(S_BULLETAIR1 + rnd(2), &soundpos, SP_LOW); } diff --git a/source/src/sound.cpp b/source/src/sound.cpp index 3fc664b9..5339d6c9 100644 --- a/source/src/sound.cpp +++ b/source/src/sound.cpp @@ -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 &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 &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); diff --git a/source/src/sound.h b/source/src/sound.h index 82c7071b..cdfba575 100644 --- a/source/src/sound.h +++ b/source/src/sound.h @@ -50,3 +50,10 @@ enum S_NULL }; +// sound priorities +enum +{ + SP_LOW = 0, + SP_NORMAL, + SP_HIGH +}; \ No newline at end of file diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index 7c425632..a4fab47a 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -297,7 +297,7 @@ void splash(projectile &p, vec &v, vec &vold, playerent *notthis, int qdam) particle_splash(0, 50, 300, v); if(p.gun!=GUN_GRENADE) { - playsound(S_FEXPLODE, NULL, NULL, &v); + playsound(S_FEXPLODE, &v); // no push? } else @@ -556,7 +556,9 @@ bool weapon::modelattacking() void weapon::attacksound() { - if(info.sound != S_NULL) playsound(info.sound, owner==player1 ? NULL : owner); + if(info.sound == S_NULL) return; + bool local = (owner == player1); + playsound(info.sound, local ? NULL : owner, NULL, NULL, local ? SP_HIGH : SP_NORMAL); } bool weapon::reload() @@ -570,9 +572,10 @@ bool weapon::reload() mag += numbullets; ammo -= numbullets; + bool local = (player1 == owner); if(owner->type==ENT_BOT) playsound(info.reload, owner); - else playsoundc(info.reload); - if(owner==player1) addmsg(SV_RELOAD, "ri2", lastmillis, owner->weaponsel->type); + else playsoundc(info.reload); + if(local) addmsg(SV_RELOAD, "ri2", lastmillis, owner->weaponsel->type); return true; } @@ -645,7 +648,7 @@ void weapon::updatetimers() void weapon::onselecting() { owner->lastaction = lastmillis; - playsound(S_GUNCHANGE); + playsound(S_GUNCHANGE, owner == player1? SP_HIGH : SP_NORMAL); } void weapon::renderhudmodel() { renderhudmodel(owner->lastaction); } @@ -672,7 +675,7 @@ struct grenadeent : bounceent { static vec n(0,0,0); if(bouncestate != NADE_THROWED) owner->weapons[GUN_GRENADE]->attack(n); - playsound(S_FEXPLODE, NULL, NULL, &o); + playsound(S_FEXPLODE, &o); newprojectile(o, o, 1, owner==player1, owner, GUN_GRENADE, millis); } @@ -725,7 +728,7 @@ struct grenades : weapon (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), 0); - playsound(S_GRENADEPULL); + playsound(S_GRENADEPULL, SP_HIGH); } else if(inhandnade && attackmillis>info.attackdelay) { @@ -795,7 +798,7 @@ struct grenades : weapon if(owner==player1) { throwmillis = lastmillis; - playsound(S_GRENADETHROW); + playsound(S_GRENADETHROW, SP_HIGH); updatelastaction(player1); addmsg(SV_THROWNADE, "ri7", int(p->o.x*DMF), int(p->o.y*DMF), int(p->o.z*DMF), int(p->vel.x*DMF), int(p->vel.y*DMF), int(p->vel.z*DMF), lastmillis-p->millis); }