diff options
author | Tim Angus <tim@ngus.net> | 2009-10-30 20:46:35 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2013-01-03 00:17:16 +0000 |
commit | eccd3e0b530e2b66e564770432c8f455444e4e95 (patch) | |
tree | d1f4611670e696f9c49fa7396bf998a1c1becd99 /src/client/snd_openal.c | |
parent | 8b14cc907dc8253174bf27a67ba1ee5b6b4e5fe5 (diff) |
* Merge ioq3-r1715
Diffstat (limited to 'src/client/snd_openal.c')
-rw-r--r-- | src/client/snd_openal.c | 405 |
1 files changed, 273 insertions, 132 deletions
diff --git a/src/client/snd_openal.c b/src/client/snd_openal.c index 7ba9c9a3..96068075 100644 --- a/src/client/snd_openal.c +++ b/src/client/snd_openal.c @@ -111,8 +111,10 @@ static void S_AL_ClearError( qboolean quiet ) if( quiet ) return; if(error != AL_NO_ERROR) + { Com_Printf(S_COLOR_YELLOW "WARNING: unhandled AL error: %s\n", S_AL_ErrorMsg(error)); + } } @@ -527,6 +529,7 @@ typedef struct src_s float lastTimePos; // On stopped loops, the last position in the buffer int lastSampleTime; // Time when this was stopped + vec3_t loopSpeakerPos; // Origin of the loop speaker qboolean local; // Is this local (relative to the cam) } src_t; @@ -538,6 +541,7 @@ typedef struct src_s #endif static src_t srcList[MAX_SRC]; static int srcCount = 0; +static int srcActiveCnt = 0; static qboolean alSourcesInitialised = qfalse; static vec3_t lastListenerOrigin = { 0.0f, 0.0f, 0.0f }; @@ -605,8 +609,6 @@ static void S_AL_ScaleGain(src_t *chksrc, vec3_t origin) if(chksrc->scaleGain != scaleFactor); { chksrc->scaleGain = scaleFactor; - // if(scaleFactor > 0.0f) - // Com_Printf("%f\n", scaleFactor); qalSourcef(chksrc->alSource, AL_GAIN, chksrc->scaleGain); } } @@ -662,6 +664,7 @@ qboolean S_AL_SrcInit( void ) // Clear the sources data structure memset(srcList, 0, sizeof(srcList)); srcCount = 0; + srcActiveCnt = 0; // Cap s_alSources to MAX_SRC limit = s_alSources->integer; @@ -695,6 +698,7 @@ static void S_AL_SrcShutdown( void ) { int i; + src_t *curSource; if(!alSourcesInitialised) return; @@ -702,9 +706,14 @@ void S_AL_SrcShutdown( void ) // Destroy all the sources for(i = 0; i < srcCount; i++) { - if(srcList[i].isLocked) + curSource = &srcList[i]; + + if(curSource->isLocked) Com_DPrintf( S_COLOR_YELLOW "WARNING: Source %d is locked\n", i); + if(curSource->entity > 0) + entityList[curSource->entity].srcAllocated = qfalse; + qalSourceStop(srcList[i].alSource); qalDeleteSources(1, &srcList[i].alSource); } @@ -737,7 +746,6 @@ static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t prio curSource->priority = priority; curSource->entity = entity; curSource->channel = channel; - curSource->isActive = qtrue; curSource->isPlaying = qfalse; curSource->isLocked = qfalse; curSource->isLooping = qfalse; @@ -774,6 +782,36 @@ Remove given source as loop master if it is the master and hand off master statu ================= */ +static void S_AL_SaveLoopPos(src_t *dest, ALuint alSource) +{ + int error; + + S_AL_ClearError(qfalse); + + qalGetSourcef(alSource, AL_SEC_OFFSET, &dest->lastTimePos); + if((error = qalGetError()) != AL_NO_ERROR) + { + // Old OpenAL implementations don't support AL_SEC_OFFSET + + if(error != AL_INVALID_ENUM) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Could not get time offset for alSource %d: %s\n", + alSource, S_AL_ErrorMsg(error)); + } + + dest->lastTimePos = -1; + } + else + dest->lastSampleTime = Sys_Milliseconds(); +} + +/* +================= +S_AL_NewLoopMaster +Remove given source as loop master if it is the master and hand off master status to another source in this case. +================= +*/ + static void S_AL_NewLoopMaster(src_t *rmSource, qboolean iskilled) { int index; @@ -789,7 +827,16 @@ static void S_AL_NewLoopMaster(src_t *rmSource, qboolean iskilled) if(curSfx->loopCnt) { - if(rmSource == &srcList[curSfx->masterLoopSrc]) + if(rmSource->priority == SRCPRI_ENTITY) + { + if(!iskilled && rmSource->isPlaying) + { + // only sync ambient loops... + // It makes more sense to have sounds for weapons/projectiles unsynced + S_AL_SaveLoopPos(rmSource, rmSource->alSource); + } + } + else if(rmSource == &srcList[curSfx->masterLoopSrc]) { int firstInactive = -1; @@ -803,7 +850,7 @@ static void S_AL_NewLoopMaster(src_t *rmSource, qboolean iskilled) curSource = &srcList[index]; if(curSource->sfx == rmSource->sfx && curSource != rmSource && - curSource->isActive && curSource->isLooping) + curSource->isActive && curSource->isLooping && curSource->priority == SRCPRI_AMBIENT) { if(curSource->isPlaying) { @@ -819,15 +866,22 @@ static void S_AL_NewLoopMaster(src_t *rmSource, qboolean iskilled) if(!curSfx->loopActiveCnt) { if(firstInactive < 0) - curSource = rmSource; + { + if(iskilled) + { + curSfx->masterLoopSrc = -1; + return; + } + else + curSource = rmSource; + } else curSource = &srcList[firstInactive]; if(rmSource->isPlaying) { // this was the last not stopped source, save last sample position + time - qalGetSourcef(rmSource->alSource, AL_SEC_OFFSET, &curSource->lastTimePos); - curSource->lastSampleTime = Sys_Milliseconds(); + S_AL_SaveLoopPos(curSource, rmSource->alSource); } else { @@ -890,7 +944,11 @@ static void S_AL_SrcKill(srcHandle_t src) curSource->priority = 0; curSource->entity = -1; curSource->channel = -1; - curSource->isActive = qfalse; + if(curSource->isActive) + { + curSource->isActive = qfalse; + srcActiveCnt--; + } curSource->isLocked = qfalse; curSource->isTracking = qfalse; curSource->local = qfalse; @@ -909,24 +967,55 @@ srcHandle_t S_AL_SrcAlloc( alSrcPriority_t priority, int entnum, int channel ) int weakest = -1; int weakest_time = Sys_Milliseconds(); int weakest_pri = 999; + float weakest_gain = 1000.0; + qboolean weakest_isplaying = qtrue; + int weakest_numloops = 0; + src_t *curSource; for(i = 0; i < srcCount; i++) { + curSource = &srcList[i]; + // If it's locked, we aren't even going to look at it - if(srcList[i].isLocked) + if(curSource->isLocked) continue; // Is it empty or not? - if((!srcList[i].isActive) && (empty == -1)) + if(!curSource->isActive) + { empty = i; - else if(srcList[i].priority < priority) + break; + } + + if(curSource->isPlaying) { - // If it's older or has lower priority, flag it as weak - if((srcList[i].priority < weakest_pri) || - (srcList[i].lastUsedTime < weakest_time)) + if(weakest_isplaying && curSource->priority < priority && + (curSource->priority < weakest_pri || + (!curSource->isLooping && (curSource->scaleGain < weakest_gain || curSource->lastUsedTime < weakest_time)))) { - weakest_pri = srcList[i].priority; - weakest_time = srcList[i].lastUsedTime; + // If it has lower priority, is fainter or older, flag it as weak + // the last two values are only compared if it's not a looping sound, because we want to prevent two + // loops (loops are added EVERY frame) fighting for a slot + weakest_pri = curSource->priority; + weakest_time = curSource->lastUsedTime; + weakest_gain = curSource->scaleGain; + weakest = i; + } + } + else + { + weakest_isplaying = qfalse; + + if(weakest < 0 || + knownSfx[curSource->sfx].loopCnt > weakest_numloops || + curSource->priority < weakest_pri || + curSource->lastUsedTime < weakest_time) + { + // Sources currently not playing of course have lowest priority + // also try to always keep at least one loop master for every loop sound + weakest_pri = curSource->priority; + weakest_time = curSource->lastUsedTime; + weakest_numloops = knownSfx[curSource->sfx].loopCnt; weakest = i; } } @@ -936,7 +1025,7 @@ srcHandle_t S_AL_SrcAlloc( alSrcPriority_t priority, int entnum, int channel ) // causes incorrect behaviour versus defacto baseq3 #if 0 // Is it an exact match, and not on channel 0? - if((srcList[i].entity == entnum) && (srcList[i].channel == channel) && (channel != 0)) + if((curSource->entity == entnum) && (curSource->channel == channel) && (channel != 0)) { S_AL_SrcKill(i); return i; @@ -944,22 +1033,17 @@ srcHandle_t S_AL_SrcAlloc( alSrcPriority_t priority, int entnum, int channel ) #endif } - // Do we have an empty one? - if(empty != -1) - { - S_AL_SrcKill( empty ); - return empty; - } - - // No. How about an overridable one? - if(weakest != -1) + if(empty == -1) + empty = weakest; + + if(empty >= 0) { - S_AL_SrcKill(weakest); - return weakest; + S_AL_SrcKill(empty); + srcList[empty].isActive = qtrue; + srcActiveCnt++; } - // Nothing. Return failure (cries...) - return -1; + return empty; } /* @@ -1039,7 +1123,7 @@ Necessary for i.g. Western Quake3 mod which is buggy. static qboolean S_AL_CheckInput(int entityNum, sfxHandle_t sfx) { if (entityNum < 0 || entityNum > MAX_GENTITIES) - Com_Error(ERR_DROP, "S_StartSound: bad entitynum %i", entityNum); + Com_Error(ERR_DROP, "ERROR: S_AL_CheckInput: bad entitynum %i", entityNum); if (sfx < 0 || sfx >= numSfx) { @@ -1086,49 +1170,61 @@ S_AL_StartSound Play a one-shot sound effect ================= */ -static -void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx ) +static void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx ) { vec3_t sorigin; srcHandle_t src; + src_t *curSource; - if(S_AL_CheckInput(origin ? 0 : entnum, sfx)) + if(origin) + { + if(S_AL_CheckInput(0, sfx)) + return; + + VectorCopy(origin, sorigin); + } + else + { + if(S_AL_CheckInput(entnum, sfx)) + return; + + if(S_AL_HearingThroughEntity(entnum)) + { + S_AL_StartLocalSound(sfx, entchannel); + return; + } + + VectorCopy(entityList[entnum].origin, sorigin); + } + + S_AL_SanitiseVector(sorigin); + + if((srcActiveCnt > 5 * srcCount / 3) && + (DistanceSquared(sorigin, lastListenerOrigin) >= + (s_alMaxDistance->value + s_alGraceDistance->value) * (s_alMaxDistance->value + s_alGraceDistance->value))) + { + // We're getting tight on sources and source is not within hearing distance so don't add it return; + } // Try to grab a source src = S_AL_SrcAlloc(SRCPRI_ONESHOT, entnum, entchannel); if(src == -1) return; - // Set up the effect - if( origin == NULL ) - { - if( S_AL_HearingThroughEntity( entnum ) ) - { - // Where the entity is the local player, play a local sound - S_AL_SrcSetup( src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qtrue ); - VectorClear( sorigin ); - } - else - { - S_AL_SrcSetup( src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qfalse ); - VectorCopy( entityList[ entnum ].origin, sorigin ); - } - srcList[ src ].isTracking = qtrue; - } - else - { - S_AL_SrcSetup( src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qfalse ); - VectorCopy( origin, sorigin ); - } + S_AL_SrcSetup(src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qfalse); + + curSource = &srcList[src]; - S_AL_SanitiseVector( sorigin ); - qalSourcefv( srcList[ src ].alSource, AL_POSITION, sorigin ); - S_AL_ScaleGain(&srcList[src], sorigin); + if(!origin) + curSource->isTracking = qtrue; + + qalSourcefv(curSource->alSource, AL_POSITION, sorigin ); + S_AL_ScaleGain(curSource, sorigin); // Start it playing - srcList[src].isPlaying = qtrue; - qalSourcePlay(srcList[src].alSource); + curSource->isPlaying = qtrue; + qalSourcePlay(curSource->alSource); } /* @@ -1158,6 +1254,10 @@ static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx, int src; sentity_t *sent = &entityList[ entityNum ]; src_t *curSource; + vec3_t sorigin, svelocity; + + if(S_AL_CheckInput(entityNum, sfx)) + return; // Do we need to allocate a new source for this entity if( !sent->srcAllocated ) @@ -1171,10 +1271,18 @@ static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx, return; } + curSource = &srcList[src]; + sent->startLoopingSound = qtrue; + + curSource->lastTimePos = -1.0; + curSource->lastSampleTime = Sys_Milliseconds(); } else + { src = sent->srcIndex; + curSource = &srcList[src]; + } sent->srcAllocated = qtrue; sent->srcIndex = src; @@ -1185,33 +1293,46 @@ static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx, // If this is not set then the looping sound is stopped. sent->loopAddedThisFrame = qtrue; - curSource = &srcList[src]; - // UGH // These lines should be called via S_AL_SrcSetup, but we // can't call that yet as it buffers sfxes that may change // with subsequent calls to S_AL_SrcLoop curSource->entity = entityNum; curSource->isLooping = qtrue; - curSource->isActive = qtrue; if( S_AL_HearingThroughEntity( entityNum ) ) { curSource->local = qtrue; - qalSourcefv( curSource->alSource, AL_POSITION, vec3_origin ); - qalSourcefv( curSource->alSource, AL_VELOCITY, vec3_origin ); + VectorClear(sorigin); + + qalSourcefv(curSource->alSource, AL_POSITION, sorigin); + qalSourcefv(curSource->alSource, AL_VELOCITY, sorigin); } else { curSource->local = qfalse; - qalSourcefv( curSource->alSource, AL_POSITION, (ALfloat *)sent->origin ); - qalSourcefv( curSource->alSource, AL_VELOCITY, (ALfloat *)velocity ); + if(origin) + VectorCopy(origin, sorigin); + else + VectorCopy(sent->origin, sorigin); + + S_AL_SanitiseVector(sorigin); - } + VectorCopy(sorigin, curSource->loopSpeakerPos); + + if(velocity) + { + VectorCopy(velocity, svelocity); + S_AL_SanitiseVector(svelocity); + } + else + VectorClear(svelocity); - S_AL_ScaleGain(curSource, sent->origin); + qalSourcefv( curSource->alSource, AL_POSITION, (ALfloat *)sorigin ); + qalSourcefv( curSource->alSource, AL_VELOCITY, (ALfloat *)velocity ); + } } /* @@ -1219,20 +1340,9 @@ static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx, S_AL_AddLoopingSound ================= */ -static -void S_AL_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) +static void S_AL_AddLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx) { - vec3_t sanOrigin, sanVelocity; - - if(S_AL_CheckInput(entityNum, sfx)) - return; - - VectorCopy( origin, sanOrigin ); - VectorCopy( velocity, sanVelocity ); - S_AL_SanitiseVector( sanOrigin ); - S_AL_SanitiseVector( sanVelocity ); - - S_AL_SrcLoop(SRCPRI_ENTITY, sfx, sanOrigin, sanVelocity, entityNum); + S_AL_SrcLoop(SRCPRI_ENTITY, sfx, origin, velocity, entityNum); } /* @@ -1240,27 +1350,9 @@ void S_AL_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velo S_AL_AddRealLoopingSound ================= */ -static -void S_AL_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) +static void S_AL_AddRealLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx) { - vec3_t sanOrigin, sanVelocity; - - if(S_AL_CheckInput(entityNum, sfx)) - return; - - VectorCopy( origin, sanOrigin ); - VectorCopy( velocity, sanVelocity ); - S_AL_SanitiseVector( sanOrigin ); - S_AL_SanitiseVector( sanVelocity ); - - // There are certain maps (*cough* Q3:TA mpterra*) that have large quantities - // of ET_SPEAKERS in the PVS at any given time. OpenAL can't cope with mixing - // large numbers of sounds, so this culls them by distance - if( DistanceSquared( sanOrigin, lastListenerOrigin ) > (s_alMaxDistance->value + s_alGraceDistance->value) * - (s_alMaxDistance->value + s_alGraceDistance->value) ) - return; - - S_AL_SrcLoop(SRCPRI_AMBIENT, sfx, sanOrigin, sanVelocity, entityNum); + S_AL_SrcLoop(SRCPRI_AMBIENT, sfx, origin, velocity, entityNum); } /* @@ -1288,8 +1380,8 @@ void S_AL_SrcUpdate( void ) int i; int entityNum; ALint state; - src_t *curSource; - + src_t *curSource; + for(i = 0; i < srcCount; i++) { entityNum = srcList[i].entity; @@ -1313,9 +1405,11 @@ void S_AL_SrcUpdate( void ) { sentity_t *sent = &entityList[ entityNum ]; - // If a looping effect hasn't been touched this frame, pause it + // If a looping effect hasn't been touched this frame, pause or kill it if(sent->loopAddedThisFrame) { + alSfx_t *curSfx; + // The sound has changed without an intervening removal if(curSource->isActive && !sent->startLoopingSound && curSource->sfx != sent->loopSfx) @@ -1339,39 +1433,81 @@ void S_AL_SrcUpdate( void ) sent->startLoopingSound = qfalse; } + curSfx = &knownSfx[curSource->sfx]; + + S_AL_ScaleGain(curSource, curSource->loopSpeakerPos); + if(!curSource->scaleGain) + { + if(curSource->isPlaying) + { + // Sound is mute, stop playback until we are in range again + S_AL_NewLoopMaster(curSource, qfalse); + qalSourceStop(curSource->alSource); + curSource->isPlaying = qfalse; + } + else if(!curSfx->loopActiveCnt && curSfx->masterLoopSrc < 0) + curSfx->masterLoopSrc = i; + + continue; + } + if(!curSource->isPlaying) { - alSfx_t *curSfx = &knownSfx[curSource->sfx]; + if(curSource->priority == SRCPRI_AMBIENT) + { + // If there are other ambient looping sources with the same sound, + // make sure the sound of these sources are in sync. - // If there are other looping sources with the same sound, - // make sure the sound of these sources are in sync. + if(curSfx->loopActiveCnt) + { + int offset, error; - if(curSfx->loopActiveCnt) - { - int offset; + // we already have a master loop playing, get buffer position. + S_AL_ClearError(qfalse); + qalGetSourcei(srcList[curSfx->masterLoopSrc].alSource, AL_SAMPLE_OFFSET, &offset); + if((error = qalGetError()) != AL_NO_ERROR) + { + if(error != AL_INVALID_ENUM) + { + Com_Printf(S_COLOR_YELLOW "WARNING: Cannot get sample offset from source %d: " + "%s\n", i, S_AL_ErrorMsg(error)); + } + } + else + qalSourcei(curSource->alSource, AL_SAMPLE_OFFSET, offset); + } + else if(curSfx->loopCnt && curSfx->masterLoopSrc >= 0) + { + float secofs; + + src_t *master = &srcList[curSfx->masterLoopSrc]; + // This loop sound used to be played, but all sources are stopped. Use last sample position/time + // to calculate offset so the player thinks the sources continued playing while they were inaudible. - // we already have a master loop playing, get buffer position. - qalGetSourcei(srcList[curSfx->masterLoopSrc].alSource, AL_SAMPLE_OFFSET, &offset); - qalSourcei(curSource->alSource, AL_SAMPLE_OFFSET, offset); + if(master->lastTimePos >= 0) + { + secofs = master->lastTimePos + (Sys_Milliseconds() - master->lastSampleTime) / 1000.0f; + secofs = fmodf(secofs, (float) curSfx->info.samples / curSfx->info.rate); + + qalSourcef(curSource->alSource, AL_SEC_OFFSET, secofs); + } + + // I be the master now + curSfx->masterLoopSrc = i; + } + else + curSfx->masterLoopSrc = i; } - else if(curSfx->loopCnt && curSfx->masterLoopSrc >= 0) + else if(curSource->lastTimePos >= 0) { float secofs; - src_t *master = &srcList[curSfx->masterLoopSrc]; - // This loop sound used to be played, but all sources are stopped. Use last sample position/time - // to calculate offset so the player thinks the sources continued playing while they were inaudible. - - secofs = master->lastTimePos + (Sys_Milliseconds() - master->lastSampleTime) / 1000.0f; - secofs = fmodf(secofs, curSfx->info.samples / curSfx->info.rate); + // For unsynced loops (SRCPRI_ENTITY) just carry on playing as if the sound was never stopped + secofs = curSource->lastTimePos + (Sys_Milliseconds() - curSource->lastSampleTime) / 1000.0f; + secofs = fmodf(secofs, (float) curSfx->info.samples / curSfx->info.rate); qalSourcef(curSource->alSource, AL_SEC_OFFSET, secofs); - - // I be the master now - curSfx->masterLoopSrc = i; } - else - curSfx->masterLoopSrc = i; curSfx->loopActiveCnt++; @@ -1393,12 +1529,17 @@ void S_AL_SrcUpdate( void ) } } - else if(curSource->isPlaying) + else if(curSource->priority == SRCPRI_AMBIENT) { - S_AL_NewLoopMaster(curSource, qfalse); - qalSourceStop(curSource->alSource); - curSource->isPlaying = qfalse; + if(curSource->isPlaying) + { + S_AL_NewLoopMaster(curSource, qfalse); + qalSourceStop(curSource->alSource); + curSource->isPlaying = qfalse; + } } + else + S_AL_SrcKill(i); continue; } |