From 5a0e2460a36f3a2afabf6b4bc7e403e657b42092 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sun, 18 Mar 2007 10:32:01 +0000 Subject: [PATCH] whee, lockless channel management. still have a problem with unbalanced retain/release, though. --- include/QF/sound.h | 1 + include/snd_render.h | 41 +++++-- libs/audio/renderer/snd_channels.c | 166 ++++++++++++++++++++--------- libs/audio/renderer/snd_mem.c | 20 +++- libs/audio/renderer/snd_mix.c | 17 +-- libs/audio/renderer/snd_resample.c | 2 +- libs/audio/renderer/snd_sfx.c | 5 + 7 files changed, 185 insertions(+), 67 deletions(-) diff --git a/include/QF/sound.h b/include/QF/sound.h index 934c58ccc..bd0b25ff4 100644 --- a/include/QF/sound.h +++ b/include/QF/sound.h @@ -51,6 +51,7 @@ struct sfx_s struct sfxbuffer_s *(*touch) (sfx_t *sfx); struct sfxbuffer_s *(*retain) (sfx_t *sfx); + struct sfxbuffer_s *(*getbuffer) (sfx_t *sfx); struct wavinfo_s *(*wavinfo) (sfx_t *sfx); sfx_t *(*open) (sfx_t *sfx); void (*close) (sfx_t *sfx); diff --git a/include/snd_render.h b/include/snd_render.h index 8cd34f044..3f859d6c9 100644 --- a/include/snd_render.h +++ b/include/snd_render.h @@ -160,11 +160,13 @@ struct sfxblock_s { void *file; //!< handle for "file" representing the block wavinfo_t wavinfo; //!< description of sound data cache_user_t cache; //!< cached sound buffer (::sfxbuffer_s) + sfxbuffer_t *buffer; //!< pointer to cached buffer }; /** Representation of a sound being played. */ struct channel_s { + struct channel_s *next; //!< next channel in "free" list sfx_t *sfx; //!< sound played by this channel int leftvol; //!< 0-255 volume int rightvol; //!< 0-255 volume @@ -180,12 +182,18 @@ struct channel_s { int oldphase; //!< phase shift between l-r in samples /** signal between main program and mixer thread that the channel is to be stopped. - - 0 normal operation - - 1 signal from main program to mixer the channel is to be stopped - - 2 signal from the mixer to the main program indicating the mixer is - done with the channel and the main program is free to clear it. + - both \c stop and \c done are zero: normal operation + - \c stop is non-zero: main program is done with channel. must wait + for mixer to finish with channel before re-using. + - \c done is non-zero: mixer is done with channel. can be reused at + any time. + - both \c stop and \c done are non-zero: both are done with channel. + can be reused at any time. */ - int stopped; + //@{ + int stop; + int done; + //@} }; extern struct cvar_s *snd_loadas8bit; @@ -269,8 +277,9 @@ void SND_SFX_Init (void); static sounds */ //@{ -#define MAX_CHANNELS 256 //!< number of available mixing channels +#define MAX_CHANNELS 512 //!< number of available mixing channels #define MAX_DYNAMIC_CHANNELS 8 //!< number of dynamic channels +#define MAX_STATIC_CHANNELS 256 //!< number of static channels extern channel_t snd_channels[MAX_CHANNELS]; //!< pool of available channels extern int snd_total_channels; //!< number of active channels @@ -458,17 +467,33 @@ wavinfo_t *SND_StreamWavinfo (sfx_t *sfx); */ sfxbuffer_t *SND_CacheTouch (sfx_t *sfx); -/** Lock a cached sound into memory. +/** Get the pointer to the sound buffer. + \param sfx sound reference + \return sound buffer or null + \note The sound must be retained with SND_CacheRetain() for the returned + buffer to be valid. +*/ +sfxbuffer_t *SND_CacheGetBuffer (sfx_t *sfx); + +/** Lock a cached sound into memory. After calling this, SND_CacheGetBffer() + will return a valid buffer. \param sfx sound reference \return poitner to sound buffer */ sfxbuffer_t *SND_CacheRetain (sfx_t *sfx); -/** Unlock a cached sound from memory. +/** Unlock a cached sound from memory. After calling this, SND_CacheGetBffer() + will return a null buffer. \param sfx sound reference */ void SND_CacheRelease (sfx_t *sfx); +/** Get the pointer to the sound buffer. + \param sfx sound reference + \return poitner to sound buffer +*/ +sfxbuffer_t *SND_StreamGetBuffer (sfx_t *sfx); + /** Lock a streamed sound into memory. Doesn't actually do anything other than return a pointer to the buffer. \param sfx sound reference diff --git a/libs/audio/renderer/snd_channels.c b/libs/audio/renderer/snd_channels.c index 555a2c6fe..0a0f518a7 100644 --- a/libs/audio/renderer/snd_channels.c +++ b/libs/audio/renderer/snd_channels.c @@ -48,12 +48,13 @@ static __attribute__ ((used)) const char rcsid[] = #include "snd_render.h" +static channel_t *free_channels; channel_t snd_channels[MAX_CHANNELS]; int snd_total_channels; static channel_t *ambient_channels[NUM_AMBIENTS]; static channel_t *dynamic_channels[MAX_DYNAMIC_CHANNELS]; -static channel_t *static_channels[MAX_CHANNELS]; +static channel_t *static_channels[MAX_STATIC_CHANNELS]; static int snd_num_statics; static qboolean snd_ambient = 1; @@ -75,19 +76,45 @@ static cvar_t *ambient_level; channel_t * SND_AllocChannel (void) { - if (snd_total_channels < MAX_CHANNELS) - return &snd_channels[snd_total_channels++]; - return 0; + channel_t **free = &free_channels; + channel_t *chan; + + if (!*free) // definitely nothing free + return 0; + while (*free) { + if (!(*free)->sfx) // free channel + break; + if ((*free)->done) // mixer is finished with this channel + break; + if (!(*free)->stop) + Sys_Error ("SND_AllocChannel: bogus channel free list"); + free = &(*free)->next; + } + if (!*free) + return 0; + chan = *free; + *free = chan->next; + if (chan->sfx) { + chan->sfx->release (chan->sfx); + chan->sfx->close (chan->sfx); + chan->sfx = 0; // make sure mixer doesn't use channel during memset + } + + memset (chan, 0, sizeof (*chan)); + chan->next = 0; + chan->sfx = 0; + + return chan; } void SND_ChannelStop (channel_t *chan) { - chan->end = 0; - if (chan->sfx) { - chan->sfx->close (chan->sfx); - } - chan->sfx = 0; + if (chan->next) + *(int*)0=0; + chan->stop = 1; + chan->next = free_channels; + free_channels = chan; } void @@ -97,9 +124,14 @@ SND_StopAllSounds (void) snd_num_statics = 0; for (i = 0; i < MAX_CHANNELS; i++) - SND_ChannelStop (&snd_channels[i]); - - memset (snd_channels, 0, MAX_CHANNELS * sizeof (channel_t)); + if (snd_channels[i].sfx && !snd_channels[i].stop) + SND_ChannelStop (&snd_channels[i]); + for (i = 0; i < NUM_AMBIENTS; i++) + ambient_channels[i] = 0; + for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) + dynamic_channels[i] = 0; + for (i = 0; i < MAX_STATIC_CHANNELS; i++) + static_channels[i] = 0; } static void @@ -192,10 +224,10 @@ SND_Channels_Init (void) Cmd_AddCommand ("playvol", s_playvol_f, "Play selected sound effect at " "selected volume (playvol pathto/sound.wav num"); - for (i = 0; i < NUM_AMBIENTS; i++) - ambient_channels[i] = SND_AllocChannel (); - for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) - dynamic_channels[i] = SND_AllocChannel (); + for (i = 0; i < MAX_CHANNELS - 1; i++) + snd_channels[i].next = &snd_channels[i + 1]; + free_channels = &snd_channels[0]; + snd_total_channels = MAX_CHANNELS; snd_num_statics = 0; @@ -206,40 +238,54 @@ SND_Channels_Init (void) static channel_t * s_pick_channel (int entnum, int entchannel) { - int ch_idx; + int best, i; unsigned life_left; - channel_t *ch, *first_to_die; + channel_t *ch; - // Check for replacement sound, or find the best one to replace - first_to_die = 0; + for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) { + ch = dynamic_channels[i]; + if (ch) { + if (ch->done) { + SND_ChannelStop (ch); + dynamic_channels[i] = ch = 0; + } else if (!ch->sfx) { + dynamic_channels[i] = ch = 0; + } + } + if (!ch) { + dynamic_channels[i] = ch = SND_AllocChannel (); + return ch; + } + } + + best = -1; life_left = 0x7fffffff; - for (ch_idx = 0; ch_idx < MAX_DYNAMIC_CHANNELS; ch_idx++) { - ch = dynamic_channels[ch_idx]; - if (entchannel != 0 // channel 0 never overrides - && ch->entnum == entnum + for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) { + ch = dynamic_channels[i]; + if (entchannel == 0) // channel 0 never overrides + continue; + if (ch->entnum == entnum && (ch->entchannel == entchannel || entchannel == -1)) { - // always override sound from same entity - first_to_die = ch; + best = i; break; } // don't let monster sounds override player sounds if (ch->entnum == *snd_render_data.viewentity - && entnum != *snd_render_data.viewentity - && ch->sfx) + && entnum != *snd_render_data.viewentity) continue; - - if (snd_paintedtime + life_left > ch->end) { + if (ch->end - snd_paintedtime < life_left) { life_left = ch->end - snd_paintedtime; - first_to_die = ch; + best = i; } } - if (!first_to_die) - return NULL; + if (best == -1) + return 0; - SND_ChannelStop (first_to_die); + SND_ChannelStop (dynamic_channels[best]); + dynamic_channels[best] = SND_AllocChannel (); - return first_to_die; + return dynamic_channels[best]; } void @@ -276,6 +322,7 @@ s_updateAmbientSounds (void) for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) { SND_ChannelStop (ambient_channels[ambient_channel]); + ambient_channels[ambient_channel] = 0; } return; } @@ -283,12 +330,29 @@ s_updateAmbientSounds (void) for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) { chan = ambient_channels[ambient_channel]; + if (chan && chan->done) { + SND_ChannelStop (chan); + chan = ambient_channels[ambient_channel] = 0; + } sfx = ambient_sfx[ambient_channel]; if (!sfx) { - SND_ChannelStop (chan); + if (chan && chan->sfx && !chan->stop) + SND_ChannelStop (chan); + ambient_channels[ambient_channel] = 0; continue; - } else if (!chan->sfx) - chan->sfx = sfx->open (sfx); + } + + if (!chan) + chan = ambient_channels[ambient_channel] = SND_AllocChannel (); + if (!chan) + continue; + + if (!chan->sfx) + sfx = sfx->open (sfx); + else + sfx = chan->sfx; + // sfx will be written to chan->sfx later to ensure mixer doesn't use + // channel prematurely. vol = ambient_level->value * l->ambient_sound_level[ambient_channel]; if (vol < 8) @@ -308,6 +372,7 @@ s_updateAmbientSounds (void) } chan->leftvol = chan->rightvol = chan->master_vol; + chan->sfx = sfx; } } @@ -396,7 +461,8 @@ SND_SetListener (const vec3_t origin, const vec3_t forward, const vec3_t right, // update spatialization for dynamic sounds for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) - s_update_channel (dynamic_channels[i]); + if (dynamic_channels[i]) + s_update_channel (dynamic_channels[i]); // update spatialization for static sounds combine = 0; @@ -436,6 +502,7 @@ SND_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin, int ch_idx, vol; unsigned int skip; channel_t *target_chan, *check; + sfx_t *osfx; if (!sfx) return; @@ -459,26 +526,23 @@ SND_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin, return; // not audible at all // new channel - if (!sfx->retain (sfx)) { - SND_ChannelStop (target_chan); + if (!sfx->retain (sfx)) return; // couldn't load the sound's data - } - if (!(target_chan->sfx = sfx->open (sfx))) { + if (!(osfx = sfx->open (sfx))) { sfx->release (sfx); return; } target_chan->pos = 0.0; - target_chan->end = snd_paintedtime + target_chan->sfx->length; - sfx->release (sfx); + target_chan->end = snd_paintedtime + osfx->length; // if an identical sound has also been started this frame, offset the pos // a bit to keep it from just making the first one louder for (ch_idx = 0; ch_idx < MAX_DYNAMIC_CHANNELS; ch_idx++) { check = dynamic_channels[ch_idx]; - if (check == target_chan) + if (!check || check == target_chan) continue; - if (check->sfx == sfx && !check->pos) { + if (check->sfx == osfx && !check->pos) { skip = rand () % (int) (0.1 * snd_shm->speed); if (skip >= target_chan->end) skip = target_chan->end - 1; @@ -487,6 +551,7 @@ SND_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin, break; } } + target_chan->sfx = osfx; } void @@ -500,6 +565,7 @@ SND_StopSound (int entnum, int entchannel) if (ch->entnum == entnum && ch->entchannel == entchannel) { ch->end = 0; SND_ChannelStop (ch); + dynamic_channels[i] = 0; return; } } @@ -510,6 +576,7 @@ SND_StaticSound (sfx_t *sfx, const vec3_t origin, float vol, float attenuation) { channel_t *ss; + sfx_t *osfx; if (!sfx) return; @@ -532,7 +599,7 @@ SND_StaticSound (sfx_t *sfx, const vec3_t origin, float vol, return; } - if (!(ss->sfx = sfx->open (sfx))) { + if (!(osfx = sfx->open (sfx))) { sfx->release (sfx); return; } @@ -540,11 +607,12 @@ SND_StaticSound (sfx_t *sfx, const vec3_t origin, float vol, ss->master_vol = vol; ss->dist_mult = (attenuation / 64) / sound_nominal_clip_dist; ss->end = snd_paintedtime + sfx->length; - sfx->release (sfx); s_spatialize (ss); ss->oldphase = ss->phase; snd_num_statics++; + + ss->sfx = osfx; } void diff --git a/libs/audio/renderer/snd_mem.c b/libs/audio/renderer/snd_mem.c index e1caaf7a0..1d78672c5 100644 --- a/libs/audio/renderer/snd_mem.c +++ b/libs/audio/renderer/snd_mem.c @@ -83,16 +83,32 @@ SND_CacheTouch (sfx_t *sfx) return Cache_Check (&((sfxblock_t *) sfx->data)->cache); } +sfxbuffer_t * +SND_CacheGetBuffer (sfx_t *sfx) +{ + return ((sfxblock_t *) sfx->data)->buffer; +} + sfxbuffer_t * SND_CacheRetain (sfx_t *sfx) { - return Cache_TryGet (&((sfxblock_t *) sfx->data)->cache); + sfxblock_t *block = (sfxblock_t *) sfx->data; + block->buffer = Cache_TryGet (&block->cache); + return block->buffer; } void SND_CacheRelease (sfx_t *sfx) { - Cache_Release (&((sfxblock_t *) sfx->data)->cache); + sfxblock_t *block = (sfxblock_t *) sfx->data; + block->buffer = 0; + Cache_Release (&block->cache); +} + +sfxbuffer_t * +SND_StreamGetBuffer (sfx_t *sfx) +{ + return &((sfxstream_t *) sfx->data)->buffer; } sfxbuffer_t * diff --git a/libs/audio/renderer/snd_mix.c b/libs/audio/renderer/snd_mix.c index d90f0829e..fd7a277ee 100644 --- a/libs/audio/renderer/snd_mix.c +++ b/libs/audio/renderer/snd_mix.c @@ -78,9 +78,14 @@ SND_PaintChannels (unsigned int endtime) for (i = 0; i < snd_total_channels; i++, ch++) { if (!ch->sfx) continue; + if (ch->stop) { + if (!ch->done) + ch->done = 1; // acknowledge stopped signal + continue; + } if (!ch->leftvol && !ch->rightvol) continue; - sc = ch->sfx->retain (ch->sfx); + sc = ch->sfx->getbuffer (ch->sfx); if (!sc) continue; @@ -102,20 +107,18 @@ SND_PaintChannels (unsigned int endtime) } // if at end of loop, restart - if (ltime >= ch->end) { + if (!count || ltime >= ch->end) { if (ch->sfx->loopstart != (unsigned int) -1) { ch->pos = ch->sfx->loopstart; ch->end = ltime + ch->sfx->length - ch->pos; + ch->done = 2; + break; } else { // channel just stopped - ch->sfx->release (ch->sfx); - SND_ChannelStop (ch); + ch->done = 2; break; } } } - - if (ch->sfx) - ch->sfx->release (ch->sfx); } // transfer out according to DMA format diff --git a/libs/audio/renderer/snd_resample.c b/libs/audio/renderer/snd_resample.c index 23eb244ed..4def8343d 100644 --- a/libs/audio/renderer/snd_resample.c +++ b/libs/audio/renderer/snd_resample.c @@ -91,9 +91,9 @@ SND_ResampleMono (sfxbuffer_t *sc, byte *data, int length, void *prev) stepscale = (float) inrate / snd_shm->speed; outcount = length / stepscale; -// printf ("%d %d\n", length, outcount); sc->sfx->length = info->samples / stepscale; + //printf ("%s %d %g %d\n", sc->sfx->name, length, stepscale, sc->sfx->length); if (info->loopstart != (unsigned int)-1) sc->sfx->loopstart = info->loopstart / stepscale; else diff --git a/libs/audio/renderer/snd_sfx.c b/libs/audio/renderer/snd_sfx.c index 95dce81e7..1710af585 100644 --- a/libs/audio/renderer/snd_sfx.c +++ b/libs/audio/renderer/snd_sfx.c @@ -83,6 +83,7 @@ SND_SFX_Cache (sfx_t *sfx, char *realname, wavinfo_t info, sfx->touch = SND_CacheTouch; sfx->retain = SND_CacheRetain; sfx->release = SND_CacheRelease; + sfx->getbuffer = SND_CacheGetBuffer; block->sfx = sfx; block->file = realname; @@ -100,6 +101,7 @@ SND_SFX_Stream (sfx_t *sfx, char *realname, wavinfo_t info, sfx->wavinfo = SND_CacheWavinfo; sfx->touch = sfx->retain = SND_StreamRetain; sfx->release = SND_StreamRelease; + sfx->getbuffer = SND_StreamGetBuffer; sfx->data = stream; stream->file = realname; @@ -123,6 +125,7 @@ SND_SFX_StreamOpen (sfx_t *sfx, void *file, new_sfx->wavinfo = SND_CacheWavinfo; new_sfx->touch = new_sfx->retain = SND_StreamRetain; new_sfx->release = SND_StreamRelease; + new_sfx->getbuffer = SND_StreamGetBuffer; new_sfx->close = close; samples = snd_shm->speed * 0.3; @@ -142,6 +145,8 @@ SND_SFX_StreamOpen (sfx_t *sfx, void *file, stream->read = read; stream->seek = seek; + stream->wavinfo = *sfx->wavinfo (sfx); + stream->buffer.length = samples; stream->buffer.advance = SND_StreamAdvance; stream->buffer.setpos = SND_StreamSetPos;