// Stop all channels using this sfx
for (i = 0; i < total_channels; i++)
if (channels[i].sfx == sfx)
- S_StopChannel (i);
+ S_StopChannel (i, true);
// Free it
if (sfx->fetcher != NULL && sfx->fetcher->free != NULL)
if (ch->entnum == entnum && (ch->entchannel == entchannel || entchannel == -1) )
{
// always override sound from same entity
- S_StopChannel (ch_idx);
+ S_StopChannel (ch_idx, true);
return &channels[ch_idx];
}
}
void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, vec3_t origin, float fvol, float attenuation, qboolean isstatic)
{
// Initialize the channel
+ // We MUST set sfx LAST because otherwise we could crash a threaded mixer
+ // (otherwise we'd have to call SndSys_LockRenderBuffer here)
memset (target_chan, 0, sizeof (*target_chan));
VectorCopy (origin, target_chan->origin);
- target_chan->sfx = sfx;
target_chan->flags = flags;
target_chan->pos = 0; // start of the sound
// Lock the SFX during play
S_LockSfx (sfx);
- // and finally, apply the volume
+ // finally, set the sfx pointer, so the channel becomes valid for playback
+ // and will be noticed by the mixer
+ target_chan->sfx = sfx;
+
+ // we have to set the channel volume AFTER the sfx because the function
+ // needs it for replaygain support
S_SetChannelVolume(target_chan - channels, fvol);
}
return (target_chan - channels);
}
-void S_StopChannel (unsigned int channel_ind)
+void S_StopChannel (unsigned int channel_ind, qboolean lockmutex)
{
channel_t *ch;
{
sfx_t *sfx = ch->sfx;
+ // we have to lock an audio mutex to prevent crashes if an audio mixer
+ // thread is currently mixing this channel
+ // the SndSys_LockRenderBuffer function uses such a mutex in
+ // threaded sound backends
+ if (lockmutex)
+ SndSys_LockRenderBuffer();
if (sfx->fetcher != NULL)
{
snd_fetcher_endsb_t fetcher_endsb = sfx->fetcher->endsb;
ch->fetcher_data = NULL;
ch->sfx = NULL;
+ if (lockmutex)
+ SndSys_UnlockRenderBuffer();
}
}
for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++)
if (channels[i].entnum == entnum && channels[i].entchannel == entchannel)
{
- S_StopChannel (i);
+ S_StopChannel (i, true);
return;
}
}
CDAudio_Stop();
for (i = 0; i < total_channels; i++)
- S_StopChannel (i);
+ S_StopChannel (i, true);
total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics
memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
S_MixToBuffer(&snd_renderbuffer->ring[startoffset * snd_renderbuffer->format.width * snd_renderbuffer->format.channels], nbframes);
paintedtime += nbframes;
+ snd_renderbuffer->endframe = paintedtime;
}
if (!simsound)
SndSys_UnlockRenderBuffer();
- snd_renderbuffer->endframe = endtime;
-
// Remove outdated samples from the ring buffer, if any
if (snd_renderbuffer->startframe < soundtime)
snd_renderbuffer->startframe = soundtime;
SndSys_Submit();
oldsoundtime = soundtime;
+
+ cls.soundstats.latency_milliseconds = (snd_renderbuffer->endframe - snd_renderbuffer->startframe) * 1000 / snd_renderbuffer->format.speed;
}
/*
*/
void S_Update(const matrix4x4_t *listenermatrix)
{
- unsigned int i, j, total;
+ unsigned int i, j, k;
channel_t *ch, *combine;
matrix4x4_t basematrix, rotatematrix;
combine = NULL;
// update spatialization for static and dynamic sounds
+ cls.soundstats.totalsounds = 0;
+ cls.soundstats.mixedsounds = 0;
ch = channels+NUM_AMBIENTS;
for (i=NUM_AMBIENTS ; i<total_channels; i++, ch++)
{
if (!ch->sfx)
continue;
+ cls.soundstats.totalsounds++;
// respatialize channel
SND_Spatialize(ch, i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS);
}
}
}
+ for (k = 0;k < SND_LISTENERS;k++)
+ if (ch->listener_volume[k])
+ break;
+ if (k < SND_LISTENERS)
+ cls.soundstats.mixedsounds++;
}
sound_spatialized = true;
// debugging output
if (snd_show.integer)
- {
- total = 0;
- ch = channels;
- for (i=0 ; i<total_channels; i++, ch++)
- {
- if (ch->sfx)
- {
- for (j = 0;j < SND_LISTENERS;j++)
- if (ch->listener_volume[j])
- break;
- if (j < SND_LISTENERS)
- total++;
- }
- }
-
- Con_Printf("----(%u)----\n", total);
- }
+ Con_Printf("----(%u)----\n", cls.soundstats.mixedsounds);
S_PaintAndSubmit();
}