summaryrefslogtreecommitdiff
path: root/src/client/snd_openal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/snd_openal.c')
-rw-r--r--src/client/snd_openal.c284
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;
}