From 37c4042c3dbb546c53bc5de8d556d2469b49289e Mon Sep 17 00:00:00 2001 From: havoc Date: Wed, 8 Jun 2011 18:03:19 +0000 Subject: [PATCH] eliminated S_LockSfx and S_UnlockSfx which were not thread-safe and could cause sounds to leak across multiple levels, cd play/loop now immediately unload previous music track git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@11189 d7cf8633-e32d-0410-b094-e92efae38249 --- cd_shared.c | 4 +- cl_video_jamdecode.c | 2 +- dpvsimpledecode.c | 2 +- snd_main.c | 104 +++++++++++++++---------------------------- snd_main.h | 12 +---- snd_mix.c | 8 ++-- snd_null.c | 4 +- sound.h | 4 +- 8 files changed, 52 insertions(+), 88 deletions(-) diff --git a/cd_shared.c b/cd_shared.c index 13634e5b..453aba62 100644 --- a/cd_shared.c +++ b/cd_shared.c @@ -322,7 +322,7 @@ void CDAudio_Play_byName (const char *trackname, qboolean looping, qboolean tryr if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "music/%s.ogg", trackname); // added by motorsep if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "music/cdtracks/%s.ogg", trackname); // added by motorsep } - if (FS_FileExists(filename) && (sfx = S_PrecacheSound (filename, false, true))) + if (FS_FileExists(filename) && (sfx = S_PrecacheSound (filename, false, false))) { faketrack = S_StartSound_StartPosition_Flags (-1, 0, sfx, vec3_origin, cdvolume, 0, startposition, (looping ? CHANNELFLAG_FORCELOOP : 0) | CHANNELFLAG_FULLVOLUME | CHANNELFLAG_LOCALSOUND); if (faketrack != -1) @@ -389,7 +389,7 @@ void CDAudio_Stop (void) if (faketrack != -1) { - S_StopChannel (faketrack, true); + S_StopChannel (faketrack, true, true); faketrack = -1; } else if (cdPlaying && (CDAudio_SysStop() == -1)) diff --git a/cl_video_jamdecode.c b/cl_video_jamdecode.c index 55a49837..05650016 100644 --- a/cl_video_jamdecode.c +++ b/cl_video_jamdecode.c @@ -143,7 +143,7 @@ void jam_close(void *stream) Z_Free(s->prevframedata); Z_Free(s->videopixels); if (s->sndchan != -1) - S_StopChannel(s->sndchan, true); + S_StopChannel(s->sndchan, true, true); if (s->file) FS_Close(s->file); Z_Free(s); diff --git a/dpvsimpledecode.c b/dpvsimpledecode.c index 6ace51c0..2cf5e82f 100644 --- a/dpvsimpledecode.c +++ b/dpvsimpledecode.c @@ -429,7 +429,7 @@ void dpvsimpledecode_close(void *stream) if (s->videopixels) Z_Free(s->videopixels); if (s->sndchan != -1) - S_StopChannel (s->sndchan, true); + S_StopChannel (s->sndchan, true, true); if (s->framedatablocks) hz_bitstream_read_blocks_free(s->framedatablocks); if (s->bitstream) diff --git a/snd_main.c b/snd_main.c index ec3d172c..71669fe9 100644 --- a/snd_main.c +++ b/snd_main.c @@ -310,11 +310,10 @@ static void S_SoundList_f (void) size = sfx->memsize; format = sfx->fetcher->getfmt(sfx); - Con_Printf ("%c%c%c%c(%2db, %6s) %8i : %s\n", + Con_Printf ("%c%c%c(%2db, %6s) %8i : %s\n", (sfx->loopstart < sfx->total_length) ? 'L' : ' ', (sfx->flags & SFXFLAG_STREAMED) ? 'S' : ' ', - (sfx->locks > 0) ? 'K' : ' ', - (sfx->flags & SFXFLAG_PERMANENTLOCK) ? 'P' : ' ', + (sfx->flags & SFXFLAG_MENUSOUND) ? 'P' : ' ', format->width * 8, (format->channels == 1) ? "mono" : "stereo", size, @@ -983,8 +982,8 @@ void S_FreeSfx (sfx_t *sfx, qboolean force) { unsigned int i; - // Never free a locked sfx unless forced - if (!force && (sfx->locks > 0 || (sfx->flags & SFXFLAG_PERMANENTLOCK))) + // Do not free a precached sound during purge + if (!force && (sfx->flags & (SFXFLAG_LEVELSOUND | SFXFLAG_MENUSOUND))) return; if (developer_loading.integer) @@ -1012,8 +1011,13 @@ void S_FreeSfx (sfx_t *sfx, qboolean force) // Stop all channels using this sfx for (i = 0; i < total_channels; i++) + { if (channels[i].sfx == sfx) - S_StopChannel (i, true); + { + Con_Printf("S_FreeSfx: stopping channel %i for sfx \"%s\"\n", i, sfx->name); + S_StopChannel (i, true, false); + } + } // Free it if (sfx->fetcher != NULL && sfx->fetcher->free != NULL) @@ -1036,28 +1040,21 @@ void S_ClearUsed (void) // Start the ambient sounds and make them loop for (i = 0; i < sizeof (ambient_sfxs) / sizeof (ambient_sfxs[0]); i++) { - // Precache it if it's not done (request a lock to make sure it will never be freed) + // Precache it if it's not done (and pass false for levelsound because these are permanent) if (ambient_sfxs[i] == NULL) - ambient_sfxs[i] = S_PrecacheSound (ambient_names[i], false, true); + ambient_sfxs[i] = S_PrecacheSound (ambient_names[i], false, false); if (ambient_sfxs[i] != NULL) { - // Add a lock to the SFX while playing. It will be - // removed by S_StopAllSounds at the end of the level - S_LockSfx (ambient_sfxs[i]); - channels[i].sfx = ambient_sfxs[i]; + channels[i].sfx->flags |= SFXFLAG_MENUSOUND; channels[i].flags |= CHANNELFLAG_FORCELOOP; channels[i].master_vol = 0; } } - // Remove 1 lock from all sfx with the SFXFLAG_SERVERSOUND flag, and remove the flag + // Clear SFXFLAG_LEVELSOUND flag so that sounds not precached this level will be purged for (sfx = known_sfx; sfx != NULL; sfx = sfx->next) - if (sfx->flags & SFXFLAG_SERVERSOUND) - { - S_UnlockSfx (sfx); - sfx->flags &= ~SFXFLAG_SERVERSOUND; - } + sfx->flags &= ~SFXFLAG_LEVELSOUND; } /* @@ -1070,11 +1067,12 @@ void S_PurgeUnused(void) sfx_t *sfx; sfx_t *sfxnext; - // Free all unlocked sfx + // Free all not-precached per-level sfx for (sfx = known_sfx;sfx;sfx = sfxnext) { sfxnext = sfx->next; - S_FreeSfx (sfx, false); + if (!(sfx->flags & (SFXFLAG_LEVELSOUND | SFXFLAG_MENUSOUND))) + S_FreeSfx (sfx, false); } } @@ -1084,7 +1082,7 @@ void S_PurgeUnused(void) S_PrecacheSound ================== */ -sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean serversound) +sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean levelsound) { sfx_t *sfx; @@ -1103,11 +1101,11 @@ sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean serversoun // previously missing file sfx->flags &= ~ SFXFLAG_FILEMISSING; - if (serversound && !(sfx->flags & SFXFLAG_SERVERSOUND)) - { - S_LockSfx (sfx); - sfx->flags |= SFXFLAG_SERVERSOUND; - } + // set a flag to indicate this has been precached for this level or permanently + if (levelsound) + sfx->flags |= SFXFLAG_LEVELSOUND; + else + sfx->flags |= SFXFLAG_MENUSOUND; if (!nosound.integer && snd_precache.integer) S_LoadSound(sfx, complain); @@ -1146,31 +1144,6 @@ qboolean S_IsSoundPrecached (const sfx_t *sfx) return (sfx != NULL && sfx->fetcher != NULL) || (sfx == &changevolume_sfx); } -/* -================== -S_LockSfx - -Add a lock to a SFX -================== -*/ -void S_LockSfx (sfx_t *sfx) -{ - sfx->locks++; -} - -/* -================== -S_UnlockSfx - -Remove a lock from a SFX -================== -*/ -void S_UnlockSfx (sfx_t *sfx) -{ - sfx->locks--; -} - - /* ================== S_BlockSound @@ -1221,7 +1194,7 @@ channel_t *SND_PickChannel(int entnum, int entchannel) if (ch->entnum == entnum && (ch->entchannel == entchannel || entchannel == -1) ) { // always override sound from same entity - S_StopChannel (ch_idx, true); + S_StopChannel (ch_idx, true, false); return &channels[ch_idx]; } } @@ -1258,7 +1231,7 @@ channel_t *SND_PickChannel(int entnum, int entchannel) if (first_to_die == -1) return NULL; - S_StopChannel (first_to_die, true); + S_StopChannel (first_to_die, true, false); emptychan_found: return &channels[first_to_die]; @@ -1584,7 +1557,7 @@ void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, { int channelindex = (int)(target_chan - channels); Con_Printf("S_PlaySfxOnChannel(%s): channel %i already in use?? Clearing.\n", sfx->name, channelindex); - S_StopChannel (channelindex, true); + S_StopChannel (channelindex, true, false); } // We MUST set sfx LAST because otherwise we could crash a threaded mixer // (otherwise we'd have to call SndSys_LockRenderBuffer here) @@ -1609,9 +1582,6 @@ void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, S_SetChannelVolume(target_chan - channels, fvol); SND_Spatialize_WithSfx (target_chan, isstatic, sfx); - // Lock the SFX during play - S_LockSfx (sfx); - // finally, set the sfx pointer, so the channel becomes valid for playback // and will be noticed by the mixer target_chan->sfx = sfx; @@ -1686,9 +1656,10 @@ int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f return S_StartSound_StartPosition(entnum, entchannel, sfx, origin, fvol, attenuation, 0); } -void S_StopChannel (unsigned int channel_ind, qboolean lockmutex) +void S_StopChannel (unsigned int channel_ind, qboolean lockmutex, qboolean freesfx) { channel_t *ch; + sfx_t *sfx; if (channel_ind >= total_channels) return; @@ -1701,10 +1672,9 @@ void S_StopChannel (unsigned int channel_ind, qboolean lockmutex) SndSys_LockRenderBuffer(); ch = &channels[channel_ind]; + sfx = ch->sfx; if (ch->sfx != NULL) { - sfx_t *sfx = ch->sfx; - if (sfx->fetcher != NULL) { snd_fetcher_endsb_t fetcher_endsb = sfx->fetcher->endsb; @@ -1712,14 +1682,13 @@ void S_StopChannel (unsigned int channel_ind, qboolean lockmutex) fetcher_endsb (ch->fetcher_data); } - // Remove the lock it holds - S_UnlockSfx (sfx); - ch->fetcher_data = NULL; ch->sfx = NULL; } if (lockmutex && !simsound) SndSys_UnlockRenderBuffer(); + if (freesfx) + S_FreeSfx(sfx, true); } @@ -1749,7 +1718,7 @@ void S_StopSound(int entnum, int entchannel) for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) if (channels[i].entnum == entnum && channels[i].entchannel == entchannel) { - S_StopChannel (i, true); + S_StopChannel (i, true, false); return; } } @@ -1772,7 +1741,8 @@ void S_StopAllSounds (void) size_t memsize; for (i = 0; i < total_channels; i++) - S_StopChannel (i, false); + if (channels[i].sfx) + S_StopChannel (i, false, false); total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics memset(channels, 0, MAX_CHANNELS * sizeof(channel_t)); @@ -2266,8 +2236,8 @@ qboolean S_LocalSound (const char *sound) return false; } - // Local sounds must not be freed - sfx->flags |= SFXFLAG_PERMANENTLOCK; + // menu sounds must not be freed on level change + sfx->flags |= SFXFLAG_MENUSOUND; ch_ind = S_StartSound (cl.viewentity, 0, sfx, vec3_origin, 1, 0); if (ch_ind < 0) diff --git a/snd_main.h b/snd_main.h index 7419e9ce..a166aba2 100644 --- a/snd_main.h +++ b/snd_main.h @@ -53,9 +53,9 @@ typedef struct snd_ringbuffer_s // sfx_t flags #define SFXFLAG_NONE 0 #define SFXFLAG_FILEMISSING (1 << 0) // wasn't able to load the associated sound file -#define SFXFLAG_SERVERSOUND (1 << 1) // the sfx is part of the server precache list +#define SFXFLAG_LEVELSOUND (1 << 1) // the sfx is part of the server or client precache list for this level #define SFXFLAG_STREAMED (1 << 2) // informative only. You shouldn't need to know that -#define SFXFLAG_PERMANENTLOCK (1 << 3) // can never be freed (ex: used by the client code) +#define SFXFLAG_MENUSOUND (1 << 3) // not freed during level change (menu sounds, music, etc) typedef struct snd_fetcher_s snd_fetcher_t; struct sfx_s @@ -64,11 +64,6 @@ struct sfx_s sfx_t *next; size_t memsize; // total memory used (including sfx_t and fetcher data) - // One lock is automatically granted while the sfx is - // playing (and removed when stopped). Locks can also be - int locks; // added by S_PrecacheSound. - // A SFX with no lock, no SFXFLAG_PERMANENTLOCK, and not precached after a level change is freed - unsigned int flags; // cf SFXFLAG_* defines unsigned int loopstart; // in sample frames. equals total_length if not looped unsigned int total_length; // in sample frames @@ -154,9 +149,6 @@ void S_MixToBuffer(void *stream, unsigned int frames); qboolean S_LoadSound (sfx_t *sfx, qboolean complain); -void S_LockSfx (sfx_t *sfx); -void S_UnlockSfx (sfx_t *sfx); - snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed); qboolean Snd_AppendToSndBuffer (snd_buffer_t* sb, const unsigned char *samples, unsigned int sampleframes, const snd_format_t* format); diff --git a/snd_mix.c b/snd_mix.c index cc425bac..d4975781 100644 --- a/snd_mix.c +++ b/snd_mix.c @@ -510,6 +510,8 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes) continue; if (!sfx->total_length) continue; + if (sfx->total_length < 0 || sfx->total_length > 1<<30) + Sys_Error("S_MixToBuffer: sfx corrupt\n"); ltime = 0; if (ch->pos < 0) @@ -525,15 +527,15 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes) // paint up to end of buffer or of input, whichever is lower count = sfx->total_length - ch->pos; count = bound(0, count, (int)frames - ltime); + // mix the remaining samples if (count) { SND_PaintChannel (ch, paintbuffer + ltime, count); ch->pos += count; ltime += count; } - // if at end of sfx, loop or stop the channel - if (ch->pos >= (int)sfx->total_length) + else { if (sfx->loopstart < sfx->total_length) ch->pos = sfx->loopstart; @@ -541,7 +543,7 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes) ch->pos = 0; else { - S_StopChannel (ch - channels, false); + S_StopChannel (ch - channels, false, false); break; } } diff --git a/snd_null.c b/snd_null.c index 3b8218ca..83859644 100755 --- a/snd_null.c +++ b/snd_null.c @@ -79,7 +79,7 @@ int S_StartSound_StartPosition_Flags (int entnum, int entchannel, sfx_t *sfx, ve return -1; } -void S_StopChannel (unsigned int channel_ind, qboolean lockmutex) +void S_StopChannel (unsigned int channel_ind, qboolean lockmutex, qboolean freesfx) { } @@ -100,7 +100,7 @@ void S_SetChannelVolume (unsigned int ch_ind, float fvol) { } -sfx_t *S_PrecacheSound (const char *sample, qboolean complain, qboolean serversound) +sfx_t *S_PrecacheSound (const char *sample, qboolean complain, qboolean levelsound) { return NULL; } diff --git a/sound.h b/sound.h index 9099b9cb..cb35c112 100644 --- a/sound.h +++ b/sound.h @@ -67,7 +67,7 @@ void S_UnloadAllSounds_f (void); void S_Update(const matrix4x4_t *listenermatrix); void S_ExtraUpdate (void); -sfx_t *S_PrecacheSound (const char *sample, qboolean complain, qboolean serversound); +sfx_t *S_PrecacheSound (const char *sample, qboolean complain, qboolean levelsound); float S_SoundLength(const char *name); void S_ClearUsed (void); void S_PurgeUnused (void); @@ -84,7 +84,7 @@ void S_StopSound (int entnum, int entchannel); void S_StopAllSounds (void); void S_PauseGameSounds (qboolean toggle); -void S_StopChannel (unsigned int channel_ind, qboolean lockmutex); +void S_StopChannel (unsigned int channel_ind, qboolean lockmutex, qboolean freesfx); qboolean S_SetChannelFlag (unsigned int ch_ind, unsigned int flag, qboolean value); void S_SetChannelVolume (unsigned int ch_ind, float fvol); float S_GetChannelPosition (unsigned int ch_ind); -- 2.39.2