whee, lockless channel management. still have a problem with unbalanced retain/release, though.

This commit is contained in:
Bill Currie 2007-03-18 10:32:01 +00:00 committed by Jeff Teunissen
parent 25fdbdab58
commit 5a0e2460a3
7 changed files with 185 additions and 67 deletions

View file

@ -51,6 +51,7 @@ struct sfx_s
struct sfxbuffer_s *(*touch) (sfx_t *sfx); struct sfxbuffer_s *(*touch) (sfx_t *sfx);
struct sfxbuffer_s *(*retain) (sfx_t *sfx); struct sfxbuffer_s *(*retain) (sfx_t *sfx);
struct sfxbuffer_s *(*getbuffer) (sfx_t *sfx);
struct wavinfo_s *(*wavinfo) (sfx_t *sfx); struct wavinfo_s *(*wavinfo) (sfx_t *sfx);
sfx_t *(*open) (sfx_t *sfx); sfx_t *(*open) (sfx_t *sfx);
void (*close) (sfx_t *sfx); void (*close) (sfx_t *sfx);

View file

@ -160,11 +160,13 @@ struct sfxblock_s {
void *file; //!< handle for "file" representing the block void *file; //!< handle for "file" representing the block
wavinfo_t wavinfo; //!< description of sound data wavinfo_t wavinfo; //!< description of sound data
cache_user_t cache; //!< cached sound buffer (::sfxbuffer_s) cache_user_t cache; //!< cached sound buffer (::sfxbuffer_s)
sfxbuffer_t *buffer; //!< pointer to cached buffer
}; };
/** Representation of a sound being played. /** Representation of a sound being played.
*/ */
struct channel_s { struct channel_s {
struct channel_s *next; //!< next channel in "free" list
sfx_t *sfx; //!< sound played by this channel sfx_t *sfx; //!< sound played by this channel
int leftvol; //!< 0-255 volume int leftvol; //!< 0-255 volume
int rightvol; //!< 0-255 volume int rightvol; //!< 0-255 volume
@ -180,12 +182,18 @@ struct channel_s {
int oldphase; //!< phase shift between l-r in samples int oldphase; //!< phase shift between l-r in samples
/** signal between main program and mixer thread that the channel is to be /** signal between main program and mixer thread that the channel is to be
stopped. stopped.
- 0 normal operation - both \c stop and \c done are zero: normal operation
- 1 signal from main program to mixer the channel is to be stopped - \c stop is non-zero: main program is done with channel. must wait
- 2 signal from the mixer to the main program indicating the mixer is for mixer to finish with channel before re-using.
done with the channel and the main program is free to clear it. - \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; extern struct cvar_s *snd_loadas8bit;
@ -269,8 +277,9 @@ void SND_SFX_Init (void);
static sounds 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_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 channel_t snd_channels[MAX_CHANNELS]; //!< pool of available channels
extern int snd_total_channels; //!< number of active 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); 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 \param sfx sound reference
\return poitner to sound buffer \return poitner to sound buffer
*/ */
sfxbuffer_t *SND_CacheRetain (sfx_t *sfx); 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 \param sfx sound reference
*/ */
void SND_CacheRelease (sfx_t *sfx); 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 /** Lock a streamed sound into memory. Doesn't actually do anything other than
return a pointer to the buffer. return a pointer to the buffer.
\param sfx sound reference \param sfx sound reference

View file

@ -48,12 +48,13 @@ static __attribute__ ((used)) const char rcsid[] =
#include "snd_render.h" #include "snd_render.h"
static channel_t *free_channels;
channel_t snd_channels[MAX_CHANNELS]; channel_t snd_channels[MAX_CHANNELS];
int snd_total_channels; int snd_total_channels;
static channel_t *ambient_channels[NUM_AMBIENTS]; static channel_t *ambient_channels[NUM_AMBIENTS];
static channel_t *dynamic_channels[MAX_DYNAMIC_CHANNELS]; 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 int snd_num_statics;
static qboolean snd_ambient = 1; static qboolean snd_ambient = 1;
@ -75,19 +76,45 @@ static cvar_t *ambient_level;
channel_t * channel_t *
SND_AllocChannel (void) SND_AllocChannel (void)
{ {
if (snd_total_channels < MAX_CHANNELS) channel_t **free = &free_channels;
return &snd_channels[snd_total_channels++]; channel_t *chan;
if (!*free) // definitely nothing free
return 0; 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 void
SND_ChannelStop (channel_t *chan) SND_ChannelStop (channel_t *chan)
{ {
chan->end = 0; if (chan->next)
if (chan->sfx) { *(int*)0=0;
chan->sfx->close (chan->sfx); chan->stop = 1;
} chan->next = free_channels;
chan->sfx = 0; free_channels = chan;
} }
void void
@ -97,9 +124,14 @@ SND_StopAllSounds (void)
snd_num_statics = 0; snd_num_statics = 0;
for (i = 0; i < MAX_CHANNELS; i++) for (i = 0; i < MAX_CHANNELS; i++)
if (snd_channels[i].sfx && !snd_channels[i].stop)
SND_ChannelStop (&snd_channels[i]); SND_ChannelStop (&snd_channels[i]);
for (i = 0; i < NUM_AMBIENTS; i++)
memset (snd_channels, 0, MAX_CHANNELS * sizeof (channel_t)); 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 static void
@ -192,10 +224,10 @@ SND_Channels_Init (void)
Cmd_AddCommand ("playvol", s_playvol_f, "Play selected sound effect at " Cmd_AddCommand ("playvol", s_playvol_f, "Play selected sound effect at "
"selected volume (playvol pathto/sound.wav num"); "selected volume (playvol pathto/sound.wav num");
for (i = 0; i < NUM_AMBIENTS; i++) for (i = 0; i < MAX_CHANNELS - 1; i++)
ambient_channels[i] = SND_AllocChannel (); snd_channels[i].next = &snd_channels[i + 1];
for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) free_channels = &snd_channels[0];
dynamic_channels[i] = SND_AllocChannel (); snd_total_channels = MAX_CHANNELS;
snd_num_statics = 0; snd_num_statics = 0;
@ -206,40 +238,54 @@ SND_Channels_Init (void)
static channel_t * static channel_t *
s_pick_channel (int entnum, int entchannel) s_pick_channel (int entnum, int entchannel)
{ {
int ch_idx; int best, i;
unsigned life_left; unsigned life_left;
channel_t *ch, *first_to_die; channel_t *ch;
// Check for replacement sound, or find the best one to replace for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) {
first_to_die = 0; 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; life_left = 0x7fffffff;
for (ch_idx = 0; ch_idx < MAX_DYNAMIC_CHANNELS; ch_idx++) { for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) {
ch = dynamic_channels[ch_idx]; ch = dynamic_channels[i];
if (entchannel != 0 // channel 0 never overrides if (entchannel == 0) // channel 0 never overrides
&& ch->entnum == entnum continue;
if (ch->entnum == entnum
&& (ch->entchannel == entchannel || entchannel == -1)) { && (ch->entchannel == entchannel || entchannel == -1)) {
// always override sound from same entity best = i;
first_to_die = ch;
break; break;
} }
// don't let monster sounds override player sounds // don't let monster sounds override player sounds
if (ch->entnum == *snd_render_data.viewentity if (ch->entnum == *snd_render_data.viewentity
&& entnum != *snd_render_data.viewentity && entnum != *snd_render_data.viewentity)
&& ch->sfx)
continue; continue;
if (ch->end - snd_paintedtime < life_left) {
if (snd_paintedtime + life_left > ch->end) {
life_left = ch->end - snd_paintedtime; life_left = ch->end - snd_paintedtime;
first_to_die = ch; best = i;
} }
} }
if (!first_to_die) if (best == -1)
return NULL; 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 void
@ -276,6 +322,7 @@ s_updateAmbientSounds (void)
for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS;
ambient_channel++) { ambient_channel++) {
SND_ChannelStop (ambient_channels[ambient_channel]); SND_ChannelStop (ambient_channels[ambient_channel]);
ambient_channels[ambient_channel] = 0;
} }
return; return;
} }
@ -283,12 +330,29 @@ s_updateAmbientSounds (void)
for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS;
ambient_channel++) { ambient_channel++) {
chan = ambient_channels[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]; sfx = ambient_sfx[ambient_channel];
if (!sfx) { if (!sfx) {
if (chan && chan->sfx && !chan->stop)
SND_ChannelStop (chan); SND_ChannelStop (chan);
ambient_channels[ambient_channel] = 0;
continue; 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]; vol = ambient_level->value * l->ambient_sound_level[ambient_channel];
if (vol < 8) if (vol < 8)
@ -308,6 +372,7 @@ s_updateAmbientSounds (void)
} }
chan->leftvol = chan->rightvol = chan->master_vol; chan->leftvol = chan->rightvol = chan->master_vol;
chan->sfx = sfx;
} }
} }
@ -396,6 +461,7 @@ SND_SetListener (const vec3_t origin, const vec3_t forward, const vec3_t right,
// update spatialization for dynamic sounds // update spatialization for dynamic sounds
for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++)
if (dynamic_channels[i])
s_update_channel (dynamic_channels[i]); s_update_channel (dynamic_channels[i]);
// update spatialization for static sounds // update spatialization for static sounds
@ -436,6 +502,7 @@ SND_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin,
int ch_idx, vol; int ch_idx, vol;
unsigned int skip; unsigned int skip;
channel_t *target_chan, *check; channel_t *target_chan, *check;
sfx_t *osfx;
if (!sfx) if (!sfx)
return; return;
@ -459,26 +526,23 @@ SND_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin,
return; // not audible at all return; // not audible at all
// new channel // new channel
if (!sfx->retain (sfx)) { if (!sfx->retain (sfx))
SND_ChannelStop (target_chan);
return; // couldn't load the sound's data return; // couldn't load the sound's data
}
if (!(target_chan->sfx = sfx->open (sfx))) { if (!(osfx = sfx->open (sfx))) {
sfx->release (sfx); sfx->release (sfx);
return; return;
} }
target_chan->pos = 0.0; target_chan->pos = 0.0;
target_chan->end = snd_paintedtime + target_chan->sfx->length; target_chan->end = snd_paintedtime + osfx->length;
sfx->release (sfx);
// if an identical sound has also been started this frame, offset the pos // 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 // a bit to keep it from just making the first one louder
for (ch_idx = 0; ch_idx < MAX_DYNAMIC_CHANNELS; ch_idx++) { for (ch_idx = 0; ch_idx < MAX_DYNAMIC_CHANNELS; ch_idx++) {
check = dynamic_channels[ch_idx]; check = dynamic_channels[ch_idx];
if (check == target_chan) if (!check || check == target_chan)
continue; continue;
if (check->sfx == sfx && !check->pos) { if (check->sfx == osfx && !check->pos) {
skip = rand () % (int) (0.1 * snd_shm->speed); skip = rand () % (int) (0.1 * snd_shm->speed);
if (skip >= target_chan->end) if (skip >= target_chan->end)
skip = target_chan->end - 1; skip = target_chan->end - 1;
@ -487,6 +551,7 @@ SND_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin,
break; break;
} }
} }
target_chan->sfx = osfx;
} }
void void
@ -500,6 +565,7 @@ SND_StopSound (int entnum, int entchannel)
if (ch->entnum == entnum && ch->entchannel == entchannel) { if (ch->entnum == entnum && ch->entchannel == entchannel) {
ch->end = 0; ch->end = 0;
SND_ChannelStop (ch); SND_ChannelStop (ch);
dynamic_channels[i] = 0;
return; return;
} }
} }
@ -510,6 +576,7 @@ SND_StaticSound (sfx_t *sfx, const vec3_t origin, float vol,
float attenuation) float attenuation)
{ {
channel_t *ss; channel_t *ss;
sfx_t *osfx;
if (!sfx) if (!sfx)
return; return;
@ -532,7 +599,7 @@ SND_StaticSound (sfx_t *sfx, const vec3_t origin, float vol,
return; return;
} }
if (!(ss->sfx = sfx->open (sfx))) { if (!(osfx = sfx->open (sfx))) {
sfx->release (sfx); sfx->release (sfx);
return; return;
} }
@ -540,11 +607,12 @@ SND_StaticSound (sfx_t *sfx, const vec3_t origin, float vol,
ss->master_vol = vol; ss->master_vol = vol;
ss->dist_mult = (attenuation / 64) / sound_nominal_clip_dist; ss->dist_mult = (attenuation / 64) / sound_nominal_clip_dist;
ss->end = snd_paintedtime + sfx->length; ss->end = snd_paintedtime + sfx->length;
sfx->release (sfx);
s_spatialize (ss); s_spatialize (ss);
ss->oldphase = ss->phase; ss->oldphase = ss->phase;
snd_num_statics++; snd_num_statics++;
ss->sfx = osfx;
} }
void void

View file

@ -83,16 +83,32 @@ SND_CacheTouch (sfx_t *sfx)
return Cache_Check (&((sfxblock_t *) sfx->data)->cache); return Cache_Check (&((sfxblock_t *) sfx->data)->cache);
} }
sfxbuffer_t *
SND_CacheGetBuffer (sfx_t *sfx)
{
return ((sfxblock_t *) sfx->data)->buffer;
}
sfxbuffer_t * sfxbuffer_t *
SND_CacheRetain (sfx_t *sfx) 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 void
SND_CacheRelease (sfx_t *sfx) 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 * sfxbuffer_t *

View file

@ -78,9 +78,14 @@ SND_PaintChannels (unsigned int endtime)
for (i = 0; i < snd_total_channels; i++, ch++) { for (i = 0; i < snd_total_channels; i++, ch++) {
if (!ch->sfx) if (!ch->sfx)
continue; continue;
if (ch->stop) {
if (!ch->done)
ch->done = 1; // acknowledge stopped signal
continue;
}
if (!ch->leftvol && !ch->rightvol) if (!ch->leftvol && !ch->rightvol)
continue; continue;
sc = ch->sfx->retain (ch->sfx); sc = ch->sfx->getbuffer (ch->sfx);
if (!sc) if (!sc)
continue; continue;
@ -102,20 +107,18 @@ SND_PaintChannels (unsigned int endtime)
} }
// if at end of loop, restart // if at end of loop, restart
if (ltime >= ch->end) { if (!count || ltime >= ch->end) {
if (ch->sfx->loopstart != (unsigned int) -1) { if (ch->sfx->loopstart != (unsigned int) -1) {
ch->pos = ch->sfx->loopstart; ch->pos = ch->sfx->loopstart;
ch->end = ltime + ch->sfx->length - ch->pos; ch->end = ltime + ch->sfx->length - ch->pos;
ch->done = 2;
break;
} else { // channel just stopped } else { // channel just stopped
ch->sfx->release (ch->sfx); ch->done = 2;
SND_ChannelStop (ch);
break; break;
} }
} }
} }
if (ch->sfx)
ch->sfx->release (ch->sfx);
} }
// transfer out according to DMA format // transfer out according to DMA format

View file

@ -91,9 +91,9 @@ SND_ResampleMono (sfxbuffer_t *sc, byte *data, int length, void *prev)
stepscale = (float) inrate / snd_shm->speed; stepscale = (float) inrate / snd_shm->speed;
outcount = length / stepscale; outcount = length / stepscale;
// printf ("%d %d\n", length, outcount);
sc->sfx->length = info->samples / stepscale; 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) if (info->loopstart != (unsigned int)-1)
sc->sfx->loopstart = info->loopstart / stepscale; sc->sfx->loopstart = info->loopstart / stepscale;
else else

View file

@ -83,6 +83,7 @@ SND_SFX_Cache (sfx_t *sfx, char *realname, wavinfo_t info,
sfx->touch = SND_CacheTouch; sfx->touch = SND_CacheTouch;
sfx->retain = SND_CacheRetain; sfx->retain = SND_CacheRetain;
sfx->release = SND_CacheRelease; sfx->release = SND_CacheRelease;
sfx->getbuffer = SND_CacheGetBuffer;
block->sfx = sfx; block->sfx = sfx;
block->file = realname; block->file = realname;
@ -100,6 +101,7 @@ SND_SFX_Stream (sfx_t *sfx, char *realname, wavinfo_t info,
sfx->wavinfo = SND_CacheWavinfo; sfx->wavinfo = SND_CacheWavinfo;
sfx->touch = sfx->retain = SND_StreamRetain; sfx->touch = sfx->retain = SND_StreamRetain;
sfx->release = SND_StreamRelease; sfx->release = SND_StreamRelease;
sfx->getbuffer = SND_StreamGetBuffer;
sfx->data = stream; sfx->data = stream;
stream->file = realname; stream->file = realname;
@ -123,6 +125,7 @@ SND_SFX_StreamOpen (sfx_t *sfx, void *file,
new_sfx->wavinfo = SND_CacheWavinfo; new_sfx->wavinfo = SND_CacheWavinfo;
new_sfx->touch = new_sfx->retain = SND_StreamRetain; new_sfx->touch = new_sfx->retain = SND_StreamRetain;
new_sfx->release = SND_StreamRelease; new_sfx->release = SND_StreamRelease;
new_sfx->getbuffer = SND_StreamGetBuffer;
new_sfx->close = close; new_sfx->close = close;
samples = snd_shm->speed * 0.3; samples = snd_shm->speed * 0.3;
@ -142,6 +145,8 @@ SND_SFX_StreamOpen (sfx_t *sfx, void *file,
stream->read = read; stream->read = read;
stream->seek = seek; stream->seek = seek;
stream->wavinfo = *sfx->wavinfo (sfx);
stream->buffer.length = samples; stream->buffer.length = samples;
stream->buffer.advance = SND_StreamAdvance; stream->buffer.advance = SND_StreamAdvance;
stream->buffer.setpos = SND_StreamSetPos; stream->buffer.setpos = SND_StreamSetPos;