[sound] Access the buffer directly from the channel

This improves the locality of reference when mixing and removes the
proxy sfx for streamed sounds.

The buffer for streamed sounds is allocated when the stream is opened
(since streamed sounds can't share buffers), and freed when the stream
is closed.

For block sounds, the buffer is reference counted (with the sfx holding
one reference, so currently block buffers never get freed), with their
reference count getting incremented on open and decremented on close.
That the reference counts get to 1 has been confirmed, so all that
should be needed is proper destruction of the sfx instances.

Still need to sort out just why channels leak across level changes.
This commit is contained in:
Bill Currie 2022-06-05 16:57:13 +09:00
parent 11fee8571c
commit 91140acfee
11 changed files with 240 additions and 205 deletions

View File

@ -56,28 +56,29 @@ struct sfx_s
{
struct snd_s *snd; //!< ownding snd_t instance
const char *name;
sfx_t *owner;
unsigned int length;
unsigned int loopstart;
unsigned length;
unsigned loopstart;
union {
sfxstream_t *stream;
sfxblock_t *block;
} data;
};
sfxbuffer_t *(*touch) (sfx_t *sfx);
sfxbuffer_t *(*retain) (sfx_t *sfx);
void (*release) (sfx_t *sfx);
sfxbuffer_t *(*getbuffer) (sfx_t *sfx);
struct wavinfo_s *(*wavinfo) (sfx_t *sfx);
struct wavinfo_s *(*wavinfo) (const sfx_t *sfx);
sfx_t *(*open) (sfx_t *sfx);
void (*close) (sfx_t *sfx);
sfxbuffer_t *(*open) (sfx_t *sfx);
};
/** paint samples into the mix buffer
This is currently used for channel-count specific mixing
\param offset offset into the mix buffer at which to start mixing
the channel
\param ch sound channel
@ -87,7 +88,7 @@ struct sfx_s
typedef void sfxpaint_t (int offset, channel_t *ch, float *buffer,
unsigned count);
/** Represent a sound sample in the mixer.
/** Represent a single output frame in the mixer.
*/
struct portable_samplepair_s {
float left; //!< left sample
@ -146,6 +147,14 @@ struct sfxbuffer_s {
unsigned size; //!< size of buffer in frames
unsigned pos; //!< position of tail within full stream
unsigned channels; //!< number of channels per frame
unsigned sfx_length; //!< total length of sfx
union { // owning instance
// the first field of both sfxstream_t and sfxblock_t is a pointer
// to sfx_t
sfx_t const * const * const sfx;
sfxstream_t *stream;
sfxblock_t *block;
};
sfxpaint_t *paint; //!< channel count specific paint function
/** Advance the position with the stream, updating the ring buffer as
necessary. Null for chached sounds.
@ -160,7 +169,7 @@ struct sfxbuffer_s {
\param pos frame position with the stream
*/
void (*setpos) (sfxbuffer_t *buffer, unsigned int pos);
sfx_t *sfx; //!< owning sfx_t instance
void (*close) (sfxbuffer_t *buffer);
/** Sample data. The block at the beginning of the buffer (size depends on
sample size)
*/
@ -170,7 +179,7 @@ struct sfxbuffer_s {
/** Representation of sound loaded that is streamed in as needed.
*/
struct sfxstream_s {
sfx_t *sfx; //!< owning sfx_t instance
const sfx_t *sfx; //!< owning sfx_t instance
void *file; //!< handle for "file" representing the stream
wavinfo_t wavinfo; //!< description of sound data
unsigned pos; //!< position of next frame full stream
@ -213,25 +222,24 @@ struct sfxstream_s {
/** Representation of sound loaded into memory as a full block.
*/
struct sfxblock_s {
sfx_t *sfx; //!< owning sfx_t instance
const sfx_t *sfx; //!< owning sfx_t instance
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 {
sfx_t *sfx; //!< sound played by this channel
sfxbuffer_t *buffer; //!< sound played by this channel
float leftvol; //!< 0-1 volume
float rightvol; //!< 0-1 volume
unsigned end; //!< end time in global paintsamples
unsigned pos; //!< sample position in sfx
unsigned looping; //!< where to loop, -1 = no looping
int pause; //!< don't update the channel at all
unsigned loopstart; //!< where to loop, -1 = no looping
int phase; //!< phase shift between l-r in samples
int oldphase; //!< phase shift between l-r in samples
byte pause; //!< don't update the channel at all
/** signal between main program and mixer thread that the channel is to be
stopped.
- both \c stop and \c done are zero: normal operation
@ -243,8 +251,8 @@ struct channel_s {
can be reused at any time.
*/
//@{
int stop;
int done;
byte stop;
byte done;
//@}
};
@ -259,7 +267,11 @@ extern portable_samplepair_t snd_paintbuffer[PAINTBUFFER_SIZE * 2];
void SND_Memory_Init_Cvars (void);
int SND_Memory_Init (void);
sfxbuffer_t *SND_Memory_AllocBuffer (unsigned samples);
void SND_Memory_FreeBuffer (sfxbuffer_t *buffer);
void SND_Memory_Free (void *ptr);
void SND_Memory_SetTag (void *ptr, int tag);
int SND_Memory_Retain (void *ptr);
int SND_Memory_Release (void *ptr);
int SND_Memory_GetRetainCount (void *ptr) __attribute__((pure));
/** \defgroup sound_render_sfx Sound sfx
\ingroup sound_render_mix
@ -281,7 +293,7 @@ void SND_SFX_Block (sfx_t *sfx, char *realname, wavinfo_t info,
\param open
*/
void SND_SFX_Stream (sfx_t *sfx, char *realname, wavinfo_t info,
sfx_t *(*open) (sfx_t *sfx));
sfxbuffer_t *(*open) (sfx_t *sfx));
/** Open a stream for playback.
\param sfx
@ -290,15 +302,15 @@ void SND_SFX_Stream (sfx_t *sfx, char *realname, wavinfo_t info,
\param seek
\param close
*/
sfx_t *SND_SFX_StreamOpen (sfx_t *sfx, void *file,
long (*read)(void *, float **),
int (*seek)(sfxstream_t *, int),
void (*close) (sfx_t *));
sfxbuffer_t *SND_SFX_StreamOpen (sfx_t *sfx, void *file,
long (*read)(void *, float **),
int (*seek)(sfxstream_t *, int),
void (*close) (sfxbuffer_t *));
/** Close a stream.
\param sfx
*/
void SND_SFX_StreamClose (sfx_t *sfx);
void SND_SFX_StreamClose (sfxstream_t *stream);
/** Pre-load a sound into the cache.
\param snd sound system state
@ -466,7 +478,7 @@ void SND_SetPaint (sfxbuffer_t *sb);
*/
///@{
unsigned SND_ResamplerFrames (sfx_t *sfx);
unsigned SND_ResamplerFrames (const sfx_t *sfx, unsigned frames);
/** Set up the various parameters that depend on the actual sample rate.
\param sb buffer to setup
@ -549,13 +561,13 @@ int SND_LoadMidi (QFile *file, sfx_t *sfx, char *realname);
\param sfx sound reference
\return pointer to sound's wavinfo
*/
wavinfo_t *SND_CacheWavinfo (sfx_t *sfx) __attribute__((pure));
wavinfo_t *SND_CacheWavinfo (const sfx_t *sfx) __attribute__((pure));
/** Retrieve wavinfo from a streamed sound.
\param sfx sound reference
\return pointer to sound's wavinfo
*/
wavinfo_t *SND_StreamWavinfo (sfx_t *sfx) __attribute__((pure));
wavinfo_t *SND_StreamWavinfo (const sfx_t *sfx) __attribute__((pure));
/** Ensure a cached sound is in memory.
\param sfx sound reference

View File

@ -283,18 +283,20 @@ flac_load (flacfile_t *ff, sfxblock_t *block)
{
float *data;
sfxbuffer_t *sb = 0;
sfx_t *sfx = block->sfx;
const sfx_t *sfx = block->sfx;
wavinfo_t *info = &block->wavinfo;
data = malloc (info->datalen);
if (!data)
goto bail;
unsigned buffer_frames = SND_ResamplerFrames (sfx);
unsigned buffer_frames = SND_ResamplerFrames (sfx, info->frames);
sb = SND_Memory_AllocBuffer (buffer_frames * info->channels);
if (!sb)
goto bail;
sb->size = buffer_frames * info->channels;
sb->sfx = sfx;
sb->channels = info->channels;
sb->sfx_length = info->frames;
sb->block = sfx->block;
if (flac_read (ff, data, info->frames) < 0)
goto bail;
SND_SetPaint (sb);
@ -358,18 +360,18 @@ flac_stream_seek (sfxstream_t *stream, int pos)
}
static void
flac_stream_close (sfx_t *sfx)
flac_stream_close (sfxbuffer_t *buffer)
{
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = buffer->stream;
flac_close (stream->file);
SND_SFX_StreamClose (sfx);
SND_SFX_StreamClose (stream);
}
static sfx_t *
static sfxbuffer_t *
flac_stream_open (sfx_t *sfx)
{
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = sfx->stream;
QFile *file;
void *f;

View File

@ -143,20 +143,20 @@ midi_stream_seek (sfxstream_t *stream, int pos)
}
static void
midi_stream_close (sfx_t *sfx)
midi_stream_close (sfxbuffer_t *buffer)
{
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = buffer->stream;
midi_file_t *mf = (midi_file_t *) stream->file;
WildMidi_Close (mf->handle);
free (mf);
SND_SFX_StreamClose (sfx);
SND_SFX_StreamClose (stream);
}
static sfx_t *
static sfxbuffer_t *
midi_stream_open (sfx_t *sfx)
{
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = sfx->stream;
QFile *file;
midi *handle;
unsigned char *local_buffer;

View File

@ -148,7 +148,8 @@ static cvar_t ambient_level_cvar = {
static void
snd_free_channel (channel_t *ch)
{
ch->sfx = 0;
sfxbuffer_t *buffer = ch->buffer;
ch->buffer = 0;
ch->stop = 0;
ch->done = 0;
int chan_ind = ch - snd_channels;
@ -157,6 +158,10 @@ snd_free_channel (channel_t *ch)
}
snd_free_channels[snd_num_free_channels++] = chan_ind;
snd_entity_channels[chan_ind] = (entchan_t) {};
if (buffer) {
buffer->close (buffer);
}
}
channel_t *
@ -164,8 +169,8 @@ SND_AllocChannel (snd_t *snd)
{
channel_t *chan;
//Sys_MaskPrintf (SYS_snd, "SND_AllocChannel: free channels: %d\n",
// snd_num_free_channels);
Sys_MaskPrintf (SYS_snd, "SND_AllocChannel: free channels: %d\n",
snd_num_free_channels);
if (!snd_num_free_channels) {
Sys_MaskPrintf (SYS_warn, "SND_AllocChannel: out of channels.\n");
return 0;
@ -181,7 +186,7 @@ SND_AllocChannel (snd_t *snd)
void
SND_ChannelStop (snd_t *snd, channel_t *chan)
{
if (!chan->sfx) {
if (!chan->buffer) {
Sys_MaskPrintf (SYS_warn, "Sound: stop called on invalid channel\n");
}
chan->stop = 1;
@ -207,7 +212,7 @@ SND_ScanChannels (snd_t *snd, int wait)
count = 0;
for (i = 0; i < MAX_CHANNELS; i++) {
ch = &snd_channels[i];
if (!ch->sfx || ch->done)
if (!ch->buffer || ch->done)
continue;
ch->stop = 1;
count++;
@ -221,7 +226,7 @@ SND_ScanChannels (snd_t *snd, int wait)
} else {
for (i = 0; i < MAX_CHANNELS; i++) {
ch = &snd_channels[i];
if (ch->sfx && ch->stop && !ch->done) {
if (ch->buffer && ch->stop && !ch->done) {
ch->done = 1;
count++;
}
@ -230,11 +235,11 @@ SND_ScanChannels (snd_t *snd, int wait)
}
for (i = 0; i < MAX_CHANNELS; i++) {
ch = &snd_channels[i];
if (!ch->sfx || !ch->done)
if (!ch->buffer || !ch->done)
continue;
ch->sfx->release (ch->sfx);
ch->sfx->close (ch->sfx);
ch->sfx = 0;
sfxbuffer_t *buffer = ch->buffer;
ch->buffer = 0;
buffer->close (buffer);
}
}
@ -419,8 +424,6 @@ s_updateAmbientSounds (snd_t *snd, const byte *ambient_sound_level)
{
float vol;
int ambient_channel;
channel_t *chan;
sfx_t *sfx;
if (!snd_ambient)
return;
@ -430,7 +433,7 @@ s_updateAmbientSounds (snd_t *snd, const byte *ambient_sound_level)
// stop all ambient channels.
for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS;
ambient_channel++) {
chan = ambient_channels[ambient_channel];
channel_t *chan = ambient_channels[ambient_channel];
if (chan) {
int chan_ind = chan - snd_channels;
spacial_t *spacial = &snd_spacialization[chan_ind];
@ -443,28 +446,28 @@ s_updateAmbientSounds (snd_t *snd, const byte *ambient_sound_level)
for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS;
ambient_channel++) {
sfx = ambient_sfx[ambient_channel];
sfx_t *sfx = ambient_sfx[ambient_channel];
if (!sfx)
continue;
chan = ambient_channels[ambient_channel];
channel_t *chan = ambient_channels[ambient_channel];
if (!chan) {
chan = ambient_channels[ambient_channel] = SND_AllocChannel (snd);
if (!chan)
continue;
}
if (!chan->sfx) {
sfx = sfx->open (sfx);
if (!sfx)
sfxbuffer_t *buffer;
if (!chan->buffer) {
buffer = sfx->open (sfx);
if (!buffer)
continue;
sfx->retain (sfx);
} else {
sfx = chan->sfx;
buffer = chan->buffer;
//sfx->retain (sfx); //FIXME why is this necessary?
}
// sfx will be written to chan->sfx later to ensure mixer doesn't use
// channel prematurely.
// buffer will be written to chan->buffer later to ensure mixer
// doesn't use channel prematurely.
int chan_ind = chan - snd_channels;
spacial_t *spacial = &snd_spacialization[chan_ind];
@ -486,7 +489,8 @@ s_updateAmbientSounds (snd_t *snd, const byte *ambient_sound_level)
}
chan->leftvol = chan->rightvol = spacial->volume;
chan->sfx = sfx;
chan->loopstart = sfx->loopstart;
chan->buffer = buffer;
}
}
@ -548,7 +552,7 @@ s_spatialize (snd_t *snd, channel_t *ch)
static inline int
s_update_channel (snd_t *snd, channel_t *ch)
{
if (!ch->sfx)
if (!ch->buffer)
return 0;
s_spatialize (snd, ch);
if (!ch->leftvol && !ch->rightvol)
@ -585,7 +589,7 @@ SND_SetListener (snd_t *snd, transform_t *ear, const byte *ambient_sound_level)
channel_t *combine = 0;
for (int i = 0; i < MAX_CHANNELS; i++) {
channel_t *ch = &snd_channels[i];
if (!ch->sfx || ch->done) {
if (!ch->buffer || ch->done) {
continue;
}
if (set_is_member (&dynamic_channels, i)
@ -597,10 +601,13 @@ SND_SetListener (snd_t *snd, transform_t *ear, const byte *ambient_sound_level)
// too quiet
continue;
}
//FIXME does this even work? probably better just to give
//static sounds random offsets (I suspect it worked just fine
//before streams were implemented)
// try to combine static sounds with a previous channel of
// the same sound effect so we don't mix five torches every
// frame see if it can just use the last one
if (combine && combine->sfx == ch->sfx) {
if (combine && combine->buffer == ch->buffer) {
s_combine_channel (combine, ch);
continue;
}
@ -608,7 +615,7 @@ SND_SetListener (snd_t *snd, transform_t *ear, const byte *ambient_sound_level)
channel_t *c = 0;
for (int j = 0; j < i; j++) {
if (set_is_member (&static_channels, j)) {
if (snd_channels[j].sfx == ch->sfx) {
if (snd_channels[j].buffer == ch->buffer) {
c = &snd_channels[j];
break;
}
@ -623,11 +630,11 @@ SND_SetListener (snd_t *snd, transform_t *ear, const byte *ambient_sound_level)
static int
snd_check_channels (snd_t *snd, channel_t *target_chan, const channel_t *check,
const sfx_t *osfx)
const sfx_t *sfx)
{
if (!check || !check->sfx || check == target_chan)
if (!check || !check->buffer || check == target_chan)
return 0;
if (check->sfx->owner == osfx->owner && !check->pos) {
if (*check->buffer->sfx == sfx && !check->pos) {
int skip = rand () % (int) (0.01 * snd->speed);
target_chan->pos = -skip;
return 1;
@ -639,15 +646,11 @@ void
SND_StartSound (snd_t *snd, int entnum, int entchannel, sfx_t *sfx,
vec4f_t origin, float vol, float attenuation)
{
int looped;
channel_t *target_chan;
sfx_t *osfx;
if (!sfx || !snd->speed)
return;
// pick a channel to play on
looped = sfx->loopstart != (unsigned) -1;
target_chan = s_pick_channel (snd, entnum, entchannel, looped);
int looped = sfx->loopstart != (unsigned) -1;
channel_t *target_chan = s_pick_channel (snd, entnum, entchannel, looped);
if (!target_chan)
return;
@ -663,7 +666,8 @@ SND_StartSound (snd_t *snd, int entnum, int entchannel, sfx_t *sfx,
};
s_spatialize (snd, target_chan);
if (!(osfx = sfx->open (sfx))) {
sfxbuffer_t *buffer;
if (!(buffer = sfx->open (sfx))) {
// because the channel was never started, it's safe to directly free it
snd_free_channel (target_chan);
return;
@ -677,16 +681,12 @@ SND_StartSound (snd_t *snd, int entnum, int entchannel, sfx_t *sfx,
if (set_is_member (&dynamic_channels, i)
|| set_is_member (&looped_channels, i)) {
channel_t *check = &snd_channels[i];
if (snd_check_channels (snd, target_chan, check, osfx))
if (snd_check_channels (snd, target_chan, check, sfx))
break;
}
}
if (!osfx->retain (osfx)) {
// because the channel was never started, it's safe to directly free it
snd_free_channel (target_chan);
return; // couldn't load the sound's data
}
target_chan->sfx = osfx;
target_chan->loopstart = sfx->loopstart;
target_chan->buffer = buffer;
set_add (looped ? &looped_channels : &dynamic_channels, chan_ind);
}
@ -716,9 +716,6 @@ void
SND_StaticSound (snd_t *snd, sfx_t *sfx, vec4f_t origin, float vol,
float attenuation)
{
channel_t *ss;
sfx_t *osfx;
if (!sfx)
return;
if (sfx->loopstart == (unsigned int) -1) {
@ -726,13 +723,15 @@ SND_StaticSound (snd_t *snd, sfx_t *sfx, vec4f_t origin, float vol,
return;
}
channel_t *ss;
if (!(ss = SND_AllocChannel (snd))) {
Sys_Printf ("ran out of channels\n");
return;
}
int ss_ind = ss - snd_channels;
if (!(osfx = sfx->open (sfx)))
sfxbuffer_t *buffer;
if (!(buffer = sfx->open (sfx)))
return;
spacial_t *spacial = &snd_spacialization[ss_ind];
@ -744,15 +743,13 @@ SND_StaticSound (snd_t *snd, sfx_t *sfx, vec4f_t origin, float vol,
s_spatialize (snd, ss);
ss->oldphase = ss->phase;
if (!osfx->retain (osfx))
return;
set_add (&static_channels, ss_ind);
snd_entity_channels[ss_ind] = (entchan_t) {
.id = SND_STATIC_ID,
.channel = 0,
};
ss->sfx = osfx;
ss->loopstart = sfx->loopstart;
ss->buffer = buffer;
}
void

View File

@ -473,11 +473,11 @@ s_channel_free (channel_t *chan)
static int
s_channel_set_sfx (channel_t *chan, sfx_t *sfx)
{
sfx_t *s = sfx->open (sfx);
if (!s) {
sfxbuffer_t *buffer = sfx->open (sfx);
if (!buffer) {
return 0;
}
chan->sfx = s;
chan->buffer = buffer;
return 1;
}
@ -505,8 +505,8 @@ s_channel_get_state (channel_t *chan)
// The mixer has finished mixing the channel (come to the end).
return chan_done;
}
if (!chan->sfx) {
// channel requires initialization
if (!chan->buffer) {
// channel has not been started yet
return chan_pending;
}
if (chan->pause) {

View File

@ -126,9 +126,37 @@ SND_Memory_AllocBuffer (unsigned samples)
}
void
SND_Memory_FreeBuffer (sfxbuffer_t *buffer)
SND_Memory_Free (void *ptr)
{
Z_Free (snd_zone, buffer);
Z_Free (snd_zone, ptr);
}
void
SND_Memory_SetTag (void *ptr, int tag)
{
Z_SetTag (snd_zone, ptr, tag);
}
int
SND_Memory_Retain (void *ptr)
{
return Z_IncRetainCount (snd_zone, ptr);
}
int
SND_Memory_Release (void *ptr)
{
int retain = Z_DecRetainCount (snd_zone, ptr);
if (!retain) {
Z_Free (snd_zone, ptr);
}
return retain;
}
int
SND_Memory_GetRetainCount (void *ptr)
{
return Z_GetRetainCount (snd_zone, ptr);
}
static sfxbuffer_t *
@ -142,34 +170,36 @@ snd_noop (sfx_t *sfx)
{
}
static sfx_t *
static sfxbuffer_t *
snd_open (sfx_t *sfx)
{
return sfx;
sfxbuffer_t *buffer = sfx->block->buffer;
SND_Memory_Retain (buffer);
return buffer;
}
static sfx_t *
static sfxbuffer_t *
snd_open_fail (sfx_t *sfx)
{
return 0;
}
sfxbuffer_t *
sfxbuffer_t * __attribute__((pure))
SND_CacheTouch (sfx_t *sfx)
{
return sfx->data.block->buffer;
return sfx->block->buffer;
}
sfxbuffer_t *
SND_CacheGetBuffer (sfx_t *sfx)
{
return sfx->data.block->buffer;
return sfx->block->buffer;
}
sfxbuffer_t *
sfxbuffer_t * __attribute__((pure))
SND_CacheRetain (sfx_t *sfx)
{
return sfx->data.block->buffer;
return sfx->block->buffer;
}
void
@ -180,13 +210,13 @@ SND_CacheRelease (sfx_t *sfx)
sfxbuffer_t *
SND_StreamGetBuffer (sfx_t *sfx)
{
return sfx->data.stream->buffer;
return sfx->stream->buffer;
}
sfxbuffer_t *
SND_StreamRetain (sfx_t *sfx)
{
return sfx->data.stream->buffer;
return sfx->stream->buffer;
}
void
@ -195,15 +225,15 @@ SND_StreamRelease (sfx_t *sfx)
}
wavinfo_t *
SND_CacheWavinfo (sfx_t *sfx)
SND_CacheWavinfo (const sfx_t *sfx)
{
return &sfx->data.block->wavinfo;
return &sfx->block->wavinfo;
}
wavinfo_t *
SND_StreamWavinfo (sfx_t *sfx)
SND_StreamWavinfo (const sfx_t *sfx)
{
return &sfx->data.stream->wavinfo;
return &sfx->stream->wavinfo;
}
static void
@ -215,8 +245,8 @@ read_samples (sfxbuffer_t *buffer, int count)
read_samples (buffer, buffer->size - buffer->head);
read_samples (buffer, count);
} else {
sfx_t *sfx = buffer->sfx;
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = buffer->stream;
const sfx_t *sfx = stream->sfx;
wavinfo_t *info = &stream->wavinfo;
float *data = buffer->data + buffer->head * info->channels;
int c;
@ -233,7 +263,7 @@ read_samples (sfxbuffer_t *buffer, int count)
}
static void
fill_buffer (sfx_t *sfx, sfxstream_t *stream, sfxbuffer_t *buffer,
fill_buffer (const sfx_t *sfx, sfxstream_t *stream, sfxbuffer_t *buffer,
wavinfo_t *info, unsigned int headpos)
{
unsigned int samples;
@ -244,11 +274,11 @@ fill_buffer (sfx_t *sfx, sfxstream_t *stream, sfxbuffer_t *buffer,
if (buffer->tail <= buffer->head)
samples += buffer->size;
if (headpos + samples > sfx->length) {
if (headpos + samples > buffer->sfx_length) {
if (sfx->loopstart == (unsigned int)-1) {
samples = sfx->length - headpos;
samples = buffer->sfx_length - headpos;
} else {
loop_samples = headpos + samples - sfx->length;
loop_samples = headpos + samples - buffer->sfx_length;
samples -= loop_samples;
}
}
@ -264,8 +294,8 @@ void
SND_StreamSetPos (sfxbuffer_t *buffer, unsigned int pos)
{
float stepscale;
sfx_t *sfx = buffer->sfx;
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = buffer->stream;
const sfx_t *sfx = stream->sfx;
wavinfo_t *info = &stream->wavinfo;
stepscale = (float) info->rate / sfx->snd->speed;
@ -282,8 +312,8 @@ SND_StreamAdvance (sfxbuffer_t *buffer, unsigned int count)
{
float stepscale;
unsigned int headpos, samples;
sfx_t *sfx = buffer->sfx;
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = buffer->stream;
const sfx_t *sfx = stream->sfx;
wavinfo_t *info = &stream->wavinfo;
stream->pos += count;
@ -300,23 +330,23 @@ SND_StreamAdvance (sfxbuffer_t *buffer, unsigned int count)
// find out where head points to in the stream
headpos = buffer->pos + samples;
if (headpos >= sfx->length) {
if (headpos >= buffer->sfx_length) {
if (sfx->loopstart == (unsigned int)-1)
headpos = sfx->length;
headpos = buffer->sfx_length;
else
headpos -= sfx->length - sfx->loopstart;
headpos -= buffer->sfx_length - sfx->loopstart;
}
if (samples < count) {
buffer->head = buffer->tail = 0;
buffer->pos += count;
if (buffer->pos > sfx->length) {
if (buffer->pos > buffer->sfx_length) {
if (sfx->loopstart == (unsigned int)-1) {
// reset the buffer and fill it incase it's needed again
buffer->pos = 0;
} else {
buffer->pos -= sfx->loopstart;
buffer->pos %= sfx->length - sfx->loopstart;
buffer->pos %= buffer->sfx_length - sfx->loopstart;
buffer->pos += sfx->loopstart;
}
stream->pos = buffer->pos;
@ -325,7 +355,7 @@ SND_StreamAdvance (sfxbuffer_t *buffer, unsigned int count)
stream->seek (stream, buffer->pos * stepscale);
} else {
buffer->pos += count;
if (buffer->pos >= sfx->length) {
if (buffer->pos >= buffer->sfx_length) {
if (sfx->loopstart == (unsigned int)-1) {
// reset the buffer and fill it in case it's needed again
headpos = buffer->pos = 0;
@ -333,7 +363,7 @@ SND_StreamAdvance (sfxbuffer_t *buffer, unsigned int count)
count = 0;
stream->seek (stream, buffer->pos * stepscale);
} else {
buffer->pos -= sfx->length - sfx->loopstart;
buffer->pos -= buffer->sfx_length - sfx->loopstart;
}
stream->pos = buffer->pos;
}
@ -355,7 +385,6 @@ SND_Load (sfx_t *sfx)
sfx->touch = sfx->retain = snd_fail;
sfx->release = snd_noop;
sfx->close = snd_noop;
sfx->open = snd_open_fail;
file = QFS_FOpenFile (sfx->name);

View File

@ -53,12 +53,12 @@ static int max_overpaint; // number of extra samples painted
/* CHANNEL MIXING */
static inline int
check_channel_end (channel_t *ch, sfx_t *sfx, int count, unsigned ltime)
check_channel_end (channel_t *ch, sfxbuffer_t *sb, int count, unsigned ltime)
{
if (count <= 0 || ltime >= ch->end) {
if (sfx->loopstart != (unsigned) -1) {
ch->pos = sfx->loopstart;
ch->end = ltime + sfx->length - ch->pos;
if (ch->loopstart != (unsigned) -1) {
ch->pos = ch->loopstart;
ch->end = ltime + sb->sfx_length - ch->pos;
} else { // channel just stopped
ch->done = 1;
return 1;
@ -103,7 +103,6 @@ SND_PaintChannels (snd_t *snd, unsigned endtime)
unsigned end, ltime;
int i, count;
channel_t *ch;
sfx_t *sfx;
sfxbuffer_t *sb;
// clear the paint buffer
@ -123,7 +122,7 @@ SND_PaintChannels (snd_t *snd, unsigned endtime)
// paint in the channels.
ch = snd_channels;
for (i = 0; i < snd_total_channels; i++, ch++) {
if (!(sfx = ch->sfx)) {
if (!(sb = ch->buffer)) {
// channel is inactive
continue;
}
@ -133,14 +132,9 @@ SND_PaintChannels (snd_t *snd, unsigned endtime)
}
if (ch->pause)
continue;
sb = sfx->getbuffer (sfx);
if (!sb) { // something went wrong with the sfx
printf ("XXXX sfx blew up!!!!\n");
continue;
}
if (!ch->end)
ch->end = snd->paintedtime + sfx->length - ch->pos;
ch->end = snd->paintedtime + sb->sfx_length - ch->pos;
ltime = snd->paintedtime;
@ -161,7 +155,7 @@ SND_PaintChannels (snd_t *snd, unsigned endtime)
ltime += count;
}
if (check_channel_end (ch, sfx, count, ltime))
if (check_channel_end (ch, sb, count, ltime))
break;
}
}
@ -473,8 +467,7 @@ SND_SetPaint (sfxbuffer_t *sb)
snd_paint_8,
};
wavinfo_t *info = sb->sfx->wavinfo (sb->sfx);
if (info->channels > 8)
Sys_Error ("illegal channel count %d", info->channels);
sb->paint = painters[info->channels];
if (sb->channels > 8 || !sb->channels)
Sys_Error ("invaliid channel count %d", sb->channels);
sb->paint = painters[sb->channels];
}

View File

@ -63,13 +63,16 @@ check_buffer_integrity (sfxbuffer_t *sb, int width, const char *func)
}
unsigned
SND_ResamplerFrames (sfx_t *sfx)
SND_ResamplerFrames (const sfx_t *sfx, unsigned frames)
{
if (frames == ~0u) {
return frames;
}
wavinfo_t *info = sfx->wavinfo (sfx);
snd_t *snd = sfx->snd;
int inrate = info->rate;
double stepscale = (double) snd->speed / inrate;
return info->frames * stepscale;
return frames * stepscale;
}
void
@ -77,8 +80,8 @@ SND_Resample (sfxbuffer_t *sb, float *data, int length)
{
int outcount;
double stepscale;
wavinfo_t *info = sb->sfx->wavinfo (sb->sfx);
snd_t *snd = sb->sfx->snd;
wavinfo_t *info = (*sb->sfx)->wavinfo (*sb->sfx);
snd_t *snd = (*sb->sfx)->snd;
int inrate = info->rate;
int outwidth;
SRC_DATA src_data;
@ -153,23 +156,19 @@ void
SND_SetupResampler (sfxbuffer_t *sb, int streamed)
{
double stepscale;
wavinfo_t *info = sb->sfx->wavinfo (sb->sfx);
snd_t *snd = sb->sfx->snd;
wavinfo_t *info = (*sb->sfx)->wavinfo (*sb->sfx);
snd_t *snd = (*sb->sfx)->snd;
int inrate = info->rate;
stepscale = (double) snd->speed / inrate;
sb->sfx->length = info->frames * stepscale;
if (info->loopstart != (unsigned int)-1)
sb->sfx->loopstart = info->loopstart * stepscale;
else
sb->sfx->loopstart = (unsigned int)-1;
sb->sfx_length = info->frames * stepscale;
sb->channels = info->channels;
if (streamed) {
int err;
sfxstream_t *stream = sb->sfx->data.stream;
sfxstream_t *stream = sb->stream;
if (snd->speed == inrate) {
stream->state = calloc (sizeof (snd_null_state_t), 1);

View File

@ -75,7 +75,12 @@ snd_sfx_free (void *_sfx, void *unused)
sfx_t *sfx = (sfx_t *) _sfx;
free ((char *) sfx->name);
sfx->name = 0;
sfx->owner = 0;
}
static void
snd_block_close (sfxbuffer_t *buffer)
{
SND_Memory_Release (buffer);
}
void
@ -84,22 +89,26 @@ SND_SFX_Block (sfx_t *sfx, char *realname, wavinfo_t info,
{
sfxblock_t *block = calloc (1, sizeof (sfxblock_t));
sfx->data.block = block;
sfx->block = block;
sfx->wavinfo = SND_CacheWavinfo;
sfx->touch = SND_CacheTouch;
sfx->retain = SND_CacheRetain;
sfx->release = SND_CacheRelease;
sfx->getbuffer = SND_CacheGetBuffer;
sfx->loopstart = SND_ResamplerFrames (sfx, info.loopstart);
sfx->length = SND_ResamplerFrames (sfx, info.frames);
block->sfx = sfx;
block->file = realname;
block->wavinfo = info;
block->buffer = load (block);
SND_Memory_Retain (block->buffer);
block->buffer->close = snd_block_close;
}
void
SND_SFX_Stream (sfx_t *sfx, char *realname, wavinfo_t info,
sfx_t *(*open) (sfx_t *sfx))
sfxbuffer_t *(*open) (sfx_t *sfx))
{
sfxstream_t *stream = calloc (1, sizeof (sfxstream_t));
sfx->open = open;
@ -107,20 +116,22 @@ SND_SFX_Stream (sfx_t *sfx, char *realname, wavinfo_t info,
sfx->touch = sfx->retain = SND_StreamRetain;
sfx->release = SND_StreamRelease;
sfx->getbuffer = SND_StreamGetBuffer;
sfx->data.stream = stream;
sfx->stream = stream;
sfx->loopstart = SND_ResamplerFrames (sfx, info.loopstart);
sfx->length = SND_ResamplerFrames (sfx, info.frames);
stream->file = realname;
stream->wavinfo = info;
}
sfx_t *
sfxbuffer_t *
SND_SFX_StreamOpen (sfx_t *sfx, void *file,
long (*read)(void *, float **),
int (*seek)(sfxstream_t *, int),
void (*close) (sfx_t *))
void (*close) (sfxbuffer_t *))
{
snd_t *snd = sfx->snd;
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = sfx->stream;
wavinfo_t *info = &stream->wavinfo;
int frames;
@ -129,55 +140,44 @@ SND_SFX_StreamOpen (sfx_t *sfx, void *file,
if (!snd->speed)
return 0;
sfx_t *new_sfx = calloc (1, sizeof (sfx_t));
new_sfx->snd = sfx->snd;
new_sfx->name = sfx->name;
new_sfx->owner = sfx;
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;
frames = snd->speed * 0.3;
frames = (frames + 255) & ~255;
stream = calloc (1, sizeof (sfxstream_t));
new_sfx->data.stream = stream;
stream->buffer = SND_Memory_AllocBuffer (frames * info->channels);
if (!stream->buffer) {
free (stream);
free (new_sfx);
return 0;
}
stream->file = file;
stream->sfx = new_sfx;
stream->sfx = sfx;
stream->ll_read = read;
stream->ll_seek = seek;
stream->wavinfo = *sfx->wavinfo (sfx);
stream->buffer->stream = stream;
stream->buffer->size = frames;
stream->buffer->advance = SND_StreamAdvance;
stream->buffer->setpos = SND_StreamSetPos;
stream->buffer->sfx = new_sfx;
stream->buffer->sfx_length = info->frames;
stream->buffer->channels = info->channels;
stream->buffer->close = close;
SND_SetPaint (stream->buffer);
SND_SetupResampler (stream->buffer, 1); // get sfx setup properly
stream->buffer->setpos (stream->buffer, 0); // pre-fill the buffer
return new_sfx;
return stream->buffer;
}
void
SND_SFX_StreamClose (sfx_t *sfx)
SND_SFX_StreamClose (sfxstream_t *stream)
{
sfxstream_t *stream = sfx->data.stream;
SND_PulldownResampler (stream);
SND_Memory_FreeBuffer (stream->buffer);
SND_Memory_Free (stream->buffer);
free (stream);
free (sfx);
}
sfx_t *
@ -196,7 +196,6 @@ SND_LoadSound (snd_t *snd, const char *name)
sfx = &snd_sfx[snd_num_sfx++];
sfx->snd = snd;
sfx->name = strdup (name);
sfx->owner = sfx;
if (SND_Load (sfx) == -1) {
snd_num_sfx--;
return 0;

View File

@ -164,18 +164,20 @@ vorbis_load (OggVorbis_File *vf, sfxblock_t *block)
{
float *data;
sfxbuffer_t *sb = 0;
sfx_t *sfx = block->sfx;
const sfx_t *sfx = block->sfx;
wavinfo_t *info = &block->wavinfo;
data = malloc (info->datalen);
if (!data)
goto bail;
unsigned buffer_frames = SND_ResamplerFrames (sfx);
unsigned buffer_frames = SND_ResamplerFrames (sfx, info->frames);
sb = SND_Memory_AllocBuffer (buffer_frames * info->channels);
if (!sb)
goto bail;
sb->size = buffer_frames * info->channels;
sb->sfx = sfx;
sb->channels = info->channels;
sb->sfx_length = info->frames;
sb->block = block;
if (vorbis_read (vf, data, info->frames, info) < 0)
goto bail;
SND_SetPaint (sb);
@ -240,9 +242,9 @@ vorbis_stream_seek (sfxstream_t *stream, int pos)
}
static void
vorbis_stream_close (sfx_t *sfx)
vorbis_stream_close (sfxbuffer_t *buffer)
{
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = buffer->stream;
vorbis_file_t *vf = (vorbis_file_t *) stream->file;
if (vf->data)
@ -250,13 +252,13 @@ vorbis_stream_close (sfx_t *sfx)
ov_clear (vf->vf);
free (vf->vf);
free (vf);
SND_SFX_StreamClose (sfx);
SND_SFX_StreamClose (stream);
}
static sfx_t *
static sfxbuffer_t *
vorbis_stream_open (sfx_t *sfx)
{
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = sfx->stream;
QFile *file;
vorbis_file_t *f;

View File

@ -57,13 +57,13 @@ typedef struct {
static sfxbuffer_t *
wav_callback_load (sfxblock_t *block)
{
sfx_t *sfx = block->sfx;
const sfx_t *sfx = block->sfx;
const char *name = (const char *) block->file;
QFile *file;
int len, fdata_ofs;
byte *data;
float *fdata;
sfxbuffer_t *buffer;
sfxbuffer_t *buffer = 0;
wavinfo_t *info = &block->wavinfo;
file = QFS_FOpenFile (name);
@ -81,12 +81,14 @@ wav_callback_load (sfxblock_t *block)
SND_Convert (data, fdata, info->frames, info->channels, info->width);
unsigned buffer_frames = SND_ResamplerFrames (sfx);
unsigned buffer_frames = SND_ResamplerFrames (sfx, info->frames);
buffer = SND_Memory_AllocBuffer (buffer_frames * info->channels);
if (!buffer)
goto bail;
buffer->size = buffer_frames * info->channels;
buffer->sfx = sfx;
buffer->channels = info->channels;
buffer->sfx_length = info->frames;
buffer->block = block;
SND_SetPaint (buffer);
SND_SetupResampler (buffer, 0);
SND_Resample (buffer, fdata, info->frames);
@ -139,22 +141,22 @@ wav_stream_seek (sfxstream_t *stream, int pos)
}
static void
wav_stream_close (sfx_t *sfx)
wav_stream_close (sfxbuffer_t *buffer)
{
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = buffer->stream;
wav_file_t *wf = (wav_file_t *) stream->file;
Qclose (wf->file);
if (wf->data)
free (wf->data);
free (wf);
SND_SFX_StreamClose (sfx);
SND_SFX_StreamClose (stream);
}
static sfx_t *
static sfxbuffer_t *
wav_stream_open (sfx_t *sfx)
{
sfxstream_t *stream = sfx->data.stream;
sfxstream_t *stream = sfx->stream;
QFile *file;
wav_file_t *wf;