diff options
Diffstat (limited to 'src/client/snd_openal.c')
-rw-r--r-- | src/client/snd_openal.c | 284 |
1 files changed, 220 insertions, 64 deletions
diff --git a/src/client/snd_openal.c b/src/client/snd_openal.c index be064697..7ba9c9a3 100644 --- a/src/client/snd_openal.c +++ b/src/client/snd_openal.c @@ -123,11 +123,18 @@ typedef struct alSfx_s { char filename[MAX_QPATH]; ALuint buffer; // OpenAL buffer - qboolean isDefault; // Couldn't be loaded - use default FX + snd_info_t info; // information for this sound like rate, sample count.. + + qboolean isDefault; // Couldn't be loaded - use default FX qboolean inMemory; // Sound is stored in memory qboolean isLocked; // Sound is locked (can not be unloaded) int lastUsedTime; // Time last used - int duration; // Milliseconds + + int duration; // Milliseconds + + int loopCnt; // number of loops using this sfx + int loopActiveCnt; // number of playing loops using this sfx + int masterLoopSrc; // All other sources looping this buffer are synced to this master src } alSfx_t; static qboolean alBuffersInitialised = qfalse; @@ -198,6 +205,7 @@ static sfxHandle_t S_AL_BufferFind(const char *filename) // Clear and copy the filename over ptr = &knownSfx[sfx]; memset(ptr, 0, sizeof(*ptr)); + ptr->masterLoopSrc = -1; strcpy(ptr->filename, filename); } @@ -287,26 +295,27 @@ S_AL_BufferLoad static void S_AL_BufferLoad(sfxHandle_t sfx) { ALenum error; + ALuint format; void *data; snd_info_t info; - ALuint format; + alSfx_t *curSfx = &knownSfx[sfx]; int size_per_sec; // Nothing? - if(knownSfx[sfx].filename[0] == '\0') + if(curSfx->filename[0] == '\0') return; // Player SFX - if(knownSfx[sfx].filename[0] == '*') + if(curSfx->filename[0] == '*') return; // Already done? - if((knownSfx[sfx].inMemory) || (knownSfx[sfx].isDefault)) + if((curSfx->inMemory) || (curSfx->isDefault)) return; // Try to load - data = S_CodecLoad(knownSfx[sfx].filename, &info); + data = S_CodecLoad(curSfx->filename, &info); if(!data) { S_AL_BufferUseDefault(sfx); @@ -315,19 +324,19 @@ static void S_AL_BufferLoad(sfxHandle_t sfx) size_per_sec = info.rate * info.channels * info.width; if( size_per_sec > 0 ) - knownSfx[sfx].duration = (int)(1000.0f * ((double)info.size / size_per_sec)); + curSfx->duration = (int)(1000.0f * ((double)info.size / size_per_sec)); format = S_AL_Format(info.width, info.channels); // Create a buffer S_AL_ClearError( qfalse ); - qalGenBuffers(1, &knownSfx[sfx].buffer); + qalGenBuffers(1, &curSfx->buffer); if((error = qalGetError()) != AL_NO_ERROR) { S_AL_BufferUseDefault(sfx); Z_Free(data); Com_Printf( S_COLOR_RED "ERROR: Can't create a sound buffer for %s - %s\n", - knownSfx[sfx].filename, S_AL_ErrorMsg(error)); + curSfx->filename, S_AL_ErrorMsg(error)); return; } @@ -337,10 +346,10 @@ static void S_AL_BufferLoad(sfxHandle_t sfx) // We have no data to buffer, so buffer silence byte dummyData[ 2 ] = { 0 }; - qalBufferData(knownSfx[sfx].buffer, AL_FORMAT_MONO16, (void *)dummyData, 2, 22050); + qalBufferData(curSfx->buffer, AL_FORMAT_MONO16, (void *)dummyData, 2, 22050); } else - qalBufferData(knownSfx[sfx].buffer, format, data, info.size, info.rate); + qalBufferData(curSfx->buffer, format, data, info.size, info.rate); error = qalGetError(); @@ -351,12 +360,12 @@ static void S_AL_BufferLoad(sfxHandle_t sfx) { S_AL_BufferUseDefault(sfx); Z_Free(data); - Com_Printf( S_COLOR_RED "ERROR: Out of memory loading %s\n", knownSfx[sfx].filename); + Com_Printf( S_COLOR_RED "ERROR: Out of memory loading %s\n", curSfx->filename); return; } // Try load it again - qalBufferData(knownSfx[sfx].buffer, format, data, info.size, info.rate); + qalBufferData(curSfx->buffer, format, data, info.size, info.rate); error = qalGetError(); } @@ -366,15 +375,17 @@ static void S_AL_BufferLoad(sfxHandle_t sfx) S_AL_BufferUseDefault(sfx); Z_Free(data); Com_Printf( S_COLOR_RED "ERROR: Can't fill sound buffer for %s - %s\n", - knownSfx[sfx].filename, S_AL_ErrorMsg(error)); + curSfx->filename, S_AL_ErrorMsg(error)); return; } + curSfx->info = info; + // Free the memory Z_Free(data); // Woo! - knownSfx[sfx].inMemory = qtrue; + curSfx->inMemory = qtrue; } /* @@ -473,10 +484,10 @@ int S_AL_SoundDuration( sfxHandle_t sfx ) if (sfx < 0 || sfx >= numSfx) { Com_Printf(S_COLOR_RED "ERROR: S_AL_SoundDuration: handle %i out of range\n", sfx); - return 0; - } + return 0; + } return knownSfx[sfx].duration; -} +} /* ================= @@ -497,23 +508,27 @@ ALuint S_AL_BufferGet(sfxHandle_t sfx) typedef struct src_s { - ALuint alSource; // OpenAL source object - sfxHandle_t sfx; // Sound effect in use + ALuint alSource; // OpenAL source object + sfxHandle_t sfx; // Sound effect in use - int lastUsedTime; // Last time used + int lastUsedTime; // Last time used alSrcPriority_t priority; // Priority - int entity; // Owning entity (-1 if none) - int channel; // Associated channel (-1 if none) + int entity; // Owning entity (-1 if none) + int channel; // Associated channel (-1 if none) - int isActive; // Is this source currently in use? - int isLocked; // This is locked (un-allocatable) - int isLooping; // Is this a looping effect (attached to an entity) - int isTracking; // Is this object tracking it's owner + qboolean isActive; // Is this source currently in use? + qboolean isPlaying; // Is this source currently playing, or stopped? + qboolean isLocked; // This is locked (un-allocatable) + qboolean isLooping; // Is this a looping effect (attached to an entity) + qboolean isTracking; // Is this object tracking its owner - float curGain; // gain employed if source is within maxdistance. - float scaleGain; // Last gain value for this source. 0 if muted. - - qboolean local; // Is this local (relative to the cam) + float curGain; // gain employed if source is within maxdistance. + float scaleGain; // Last gain value for this source. 0 if muted. + + float lastTimePos; // On stopped loops, the last position in the buffer + int lastSampleTime; // Time when this was stopped + + qboolean local; // Is this local (relative to the cam) } src_t; #ifdef MACOS_X @@ -530,7 +545,7 @@ typedef struct sentity_s { vec3_t origin; - int srcAllocated; // If a src_t has been allocated to this entity + qboolean srcAllocated; // If a src_t has been allocated to this entity int srcIndex; qboolean loopAddedThisFrame; @@ -723,6 +738,7 @@ static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t prio curSource->entity = entity; curSource->channel = channel; curSource->isActive = qtrue; + curSource->isPlaying = qfalse; curSource->isLocked = qfalse; curSource->isLooping = qfalse; curSource->isTracking = qfalse; @@ -753,42 +769,131 @@ static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t prio /* ================= +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; + src_t *curSource = NULL; + alSfx_t *curSfx; + + curSfx = &knownSfx[rmSource->sfx]; + + if(rmSource->isPlaying) + curSfx->loopActiveCnt--; + if(iskilled) + curSfx->loopCnt--; + + if(curSfx->loopCnt) + { + if(rmSource == &srcList[curSfx->masterLoopSrc]) + { + int firstInactive = -1; + + // Only if rmSource was the master and if there are still playing loops for + // this sound will we need to find a new master. + + if(iskilled || curSfx->loopActiveCnt) + { + for(index = 0; index < srcCount; index++) + { + curSource = &srcList[index]; + + if(curSource->sfx == rmSource->sfx && curSource != rmSource && + curSource->isActive && curSource->isLooping) + { + if(curSource->isPlaying) + { + curSfx->masterLoopSrc = index; + break; + } + else if(firstInactive < 0) + firstInactive = index; + } + } + } + + if(!curSfx->loopActiveCnt) + { + if(firstInactive < 0) + 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(); + } + else + { + // second case: all loops using this sound have stopped due to listener being of of range, + // and now the inactive master gets deleted. Just move over the soundpos settings to the + // new master. + curSource->lastTimePos = rmSource->lastTimePos; + curSource->lastSampleTime = rmSource->lastSampleTime; + } + } + } + } + else + curSfx->masterLoopSrc = -1; +} + +/* +================= S_AL_SrcKill ================= */ static void S_AL_SrcKill(srcHandle_t src) { + src_t *curSource = &srcList[src]; + // I'm not touching it. Unlock it first. - if(srcList[src].isLocked) + if(curSource->isLocked) return; - // Stop it if it's playing - if(srcList[src].isActive) - qalSourceStop(srcList[src].alSource); + // Remove the entity association and loop master status + if(curSource->isLooping) + { + curSource->isLooping = qfalse; + + if(curSource->entity != -1) + { + sentity_t *curEnt = &entityList[curSource->entity]; + + curEnt->srcAllocated = qfalse; + curEnt->srcIndex = -1; + curEnt->loopAddedThisFrame = qfalse; + curEnt->startLoopingSound = qfalse; + } + + S_AL_NewLoopMaster(curSource, qtrue); + } - // Remove the entity association - if((srcList[src].isLooping) && (srcList[src].entity != -1)) + // Stop it if it's playing + if(curSource->isPlaying) { - int ent = srcList[src].entity; - entityList[ent].srcAllocated = qfalse; - entityList[ent].srcIndex = -1; - entityList[ent].loopAddedThisFrame = qfalse; - entityList[ent].startLoopingSound = qfalse; + qalSourceStop(curSource->alSource); + curSource->isPlaying = qfalse; } // Remove the buffer - qalSourcei(srcList[src].alSource, AL_BUFFER, 0); - - srcList[src].sfx = 0; - srcList[src].lastUsedTime = 0; - srcList[src].priority = 0; - srcList[src].entity = -1; - srcList[src].channel = -1; - srcList[src].isActive = qfalse; - srcList[src].isLocked = qfalse; - srcList[src].isLooping = qfalse; - srcList[src].isTracking = qfalse; - srcList[src].local = qfalse; + qalSourcei(curSource->alSource, AL_BUFFER, 0); + + curSource->sfx = 0; + curSource->lastUsedTime = 0; + curSource->priority = 0; + curSource->entity = -1; + curSource->channel = -1; + curSource->isActive = qfalse; + curSource->isLocked = qfalse; + curSource->isTracking = qfalse; + curSource->local = qfalse; } /* @@ -970,6 +1075,7 @@ void S_AL_StartLocalSound(sfxHandle_t sfx, int channel) S_AL_SrcSetup(src, sfx, SRCPRI_LOCAL, -1, channel, qtrue); // Start it playing + srcList[src].isPlaying = qtrue; qalSourcePlay(srcList[src].alSource); } @@ -1021,6 +1127,7 @@ void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx S_AL_ScaleGain(&srcList[src], sorigin); // Start it playing + srcList[src].isPlaying = qtrue; qalSourcePlay(srcList[src].alSource); } @@ -1075,7 +1182,7 @@ static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx, sent->loopPriority = priority; sent->loopSfx = sfx; - // If this is not set then the looping sound is removed + // If this is not set then the looping sound is stopped. sent->loopAddedThisFrame = qtrue; curSource = &srcList[src]; @@ -1195,9 +1302,9 @@ void S_AL_SrcUpdate( void ) continue; // Update source parameters - if((s_alGain->modified)||(s_volume->modified)) + if((s_alGain->modified) || (s_volume->modified)) curSource->curGain = s_alGain->value * s_volume->value; - if((s_alRolloff->modified)&&(!curSource->local)) + if((s_alRolloff->modified) && (!curSource->local)) qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value); if(s_alMinDistance->modified) qalSourcef(curSource->alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value); @@ -1206,13 +1313,16 @@ void S_AL_SrcUpdate( void ) { sentity_t *sent = &entityList[ entityNum ]; - // If a looping effect hasn't been touched this frame, kill it + // If a looping effect hasn't been touched this frame, pause it if(sent->loopAddedThisFrame) { // The sound has changed without an intervening removal if(curSource->isActive && !sent->startLoopingSound && curSource->sfx != sent->loopSfx) { + S_AL_NewLoopMaster(curSource, qtrue); + + curSource->isPlaying = qfalse; qalSourceStop(curSource->alSource); qalSourcei(curSource->alSource, AL_BUFFER, 0); sent->startLoopingSound = qtrue; @@ -1224,10 +1334,50 @@ void S_AL_SrcUpdate( void ) S_AL_SrcSetup(i, sent->loopSfx, sent->loopPriority, entityNum, -1, curSource->local); curSource->isLooping = qtrue; + + knownSfx[curSource->sfx].loopCnt++; + sent->startLoopingSound = qfalse; + } + + if(!curSource->isPlaying) + { + alSfx_t *curSfx = &knownSfx[curSource->sfx]; + + // 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; + + // 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); + } + 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. + + secofs = master->lastTimePos + (Sys_Milliseconds() - master->lastSampleTime) / 1000.0f; + secofs = fmodf(secofs, 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++; + qalSourcei(curSource->alSource, AL_LOOPING, AL_TRUE); + curSource->isPlaying = qtrue; qalSourcePlay(curSource->alSource); - - sent->startLoopingSound = qfalse; } // Update locality @@ -1241,9 +1391,14 @@ void S_AL_SrcUpdate( void ) qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_FALSE); qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value); } + + } + else if(curSource->isPlaying) + { + S_AL_NewLoopMaster(curSource, qfalse); + qalSourceStop(curSource->alSource); + curSource->isPlaying = qfalse; } - else - S_AL_SrcKill( i ); continue; } @@ -1252,6 +1407,7 @@ void S_AL_SrcUpdate( void ) qalGetSourcei(curSource->alSource, AL_SOURCE_STATE, &state); if(state == AL_STOPPED) { + curSource->isPlaying = qfalse; S_AL_SrcKill(i); continue; } |