engine: client: switch to dynamically allocating vox words, freeing up 320 Kb of memory

This commit is contained in:
Alibek Omarov
2026-04-06 06:55:27 +05:00
parent 729c985eaf
commit 3fffe061d5
4 changed files with 30 additions and 17 deletions

View File

@@ -171,11 +171,16 @@ S_FreeChannel
*/
void S_FreeChannel( channel_t *ch )
{
// free the currently loaded word's audio cache before nuking the channel
if( ch->words )
VOX_FreeWord( ch );
ch->sfx = NULL;
ch->name[0] = '\0';
ch->flags = 0;
ch->forced_end = ch->sample = 0.0;
ch->data = NULL;
Mem_Free2( &ch->words );
SND_CloseMouth( ch );
}
@@ -256,7 +261,7 @@ static int SND_GetChannelTimeLeft( const channel_t *ch )
if( FBitSet( ch->flags, FL_CHAN_FINISHED ) || !ch->sfx || !ch->sfx->cache )
return 0;
if( FBitSet( ch->flags, FL_CHAN_IS_SENTENCE )) // sentences are special, count all remaining words
if( ch->words ) // sentences are special, count all remaining words
{
int i;
@@ -268,7 +273,7 @@ static int SND_GetChannelTimeLeft( const channel_t *ch )
// here we count all remaining words, stopping if no sfx or sound file is available
// see VOX_LoadWord
for( i = ch->word_index + 1; i < ARRAYSIZE( ch->words ); i++ )
for( i = ch->word_index + 1; i < CVOXWORDMAX; i++ )
{
wavdata_t *sc;
int end;
@@ -440,7 +445,7 @@ static qboolean S_MaybeAlterChannel( channel_t *ch, int entnum, int entchannel,
// if no sfx passed, check if it's a sentence
if( !sfx )
{
if( !FBitSet( ch->flags, FL_CHAN_IS_SENTENCE ))
if( !ch->words )
return false;
}
else
@@ -876,7 +881,6 @@ void S_AmbientSound( const vec3_t pos, int ent, sound_t handle, float fvol, floa
// load regular or stream sound
pSource = S_LoadSound( sfx );
ch->sfx = sfx;
ClearBits( ch->flags, FL_CHAN_IS_SENTENCE );
ch->name[0] = '\0';
}
@@ -945,7 +949,7 @@ int S_GetCurrentStaticSounds( soundlist_t *pout, int size )
if( ch->entchannel != CHAN_STATIC || !ch->sfx || !ch->sfx->name[0] )
continue;
if( FBitSet( ch->flags, FL_CHAN_IS_SENTENCE ) && ch->name[0] )
if( ch->words && ch->name[0] )
Q_strncpy( pout->name, ch->name, sizeof( pout->name ));
else
Q_strncpy( pout->name, ch->sfx->name, sizeof( pout->name ));
@@ -995,7 +999,7 @@ int S_GetCurrentDynamicSounds( soundlist_t *pout, int size )
if( ch->entchannel == CHAN_STATIC && looped && !Host_IsQuakeCompatible())
continue; // never serialize static looped sounds. It will be restoring in game code
if( FBitSet( ch->flags, FL_CHAN_IS_SENTENCE ) && ch->name[0] )
if( ch->words && ch->name[0] )
Q_strncpy( pout->name, ch->name, sizeof( pout->name ));
else
Q_strncpy( pout->name, ch->sfx->name, sizeof( pout->name ));

View File

@@ -318,7 +318,7 @@ static int S_MixNormalChannels( portable_samplepair_t *dst, int end, int rate )
num_mixed_channels++;
if( FBitSet( ch->flags, FL_CHAN_IS_SENTENCE ))
if( ch->words )
{
VOX_MixChannelToBuffer( dst, ch, num_samples, rate, pitch );

View File

@@ -191,7 +191,7 @@ void VOX_SetChanVol( channel_t *ch )
{
voxword_t *word;
if( !FBitSet( ch->flags, FL_CHAN_IS_SENTENCE ) || FBitSet( ch->flags, FL_CHAN_SENTENCE_FINISHED ))
if( !ch->words || FBitSet( ch->flags, FL_CHAN_SENTENCE_FINISHED ))
return;
word = &ch->words[ch->word_index];
@@ -207,7 +207,7 @@ float VOX_ModifyPitch( channel_t *ch, float pitch )
{
voxword_t *word;
if( !FBitSet( ch->flags, FL_CHAN_IS_SENTENCE ) || FBitSet( ch->flags, FL_CHAN_SENTENCE_FINISHED ))
if( !ch->words || FBitSet( ch->flags, FL_CHAN_SENTENCE_FINISHED ))
return pitch;
word = &ch->words[ch->word_index];
@@ -449,10 +449,18 @@ void VOX_LoadSound( channel_t *ch, const char *pszin )
int i, j;
int num_words;
voxword_t default_voxword;
voxword_t words_buf[CVOXWORDMAX + 1]; // local scratch: parsed words + null terminator
if( !pszin )
return;
// free any existing words from a previous sentence on this channel
if( ch->words )
{
VOX_FreeWord( ch );
Mem_Free2( &ch->words );
}
psz = VOX_LookupString( pszin );
if( !psz )
@@ -480,12 +488,13 @@ void VOX_LoadSound( channel_t *ch, const char *pszin )
num_words = VOX_ParseString( buffer, rgpparseword );
VOX_MakeDefaultWordParams( &default_voxword );
memset( words_buf, 0, sizeof( words_buf ));
for( i = 0, j = 0; i < num_words; i++ )
{
char pathbuffer[MAX_SYSPATH];
if( !VOX_ParseWordParams( rgpparseword[i], &ch->words[j], &default_voxword ))
if( !VOX_ParseWordParams( rgpparseword[i], &words_buf[j], &default_voxword ))
continue;
if( Q_snprintf( pathbuffer, sizeof( pathbuffer ), "%s%s", szpath, rgpparseword[i] ) < 0 )
@@ -495,18 +504,19 @@ void VOX_LoadSound( channel_t *ch, const char *pszin )
}
qboolean in_cache = false;
ch->words[j].sfx = S_FindName( pathbuffer, &in_cache );
words_buf[j].sfx = S_FindName( pathbuffer, &in_cache );
if( in_cache )
SetBits( ch->words[j].flags, FL_VOXWORD_IN_CACHE );
SetBits( words_buf[j].flags, FL_VOXWORD_IN_CACHE );
j++;
}
ch->words[j].sfx = NULL;
// words_buf[j].sfx is already NULL from the memset — null terminator
ch->words = Mem_Malloc( sndpool, ( j + 1 ) * sizeof( voxword_t ));
memcpy( ch->words, words_buf, ( j + 1 ) * sizeof( voxword_t ));
ch->sfx = ch->words[0].sfx;
ch->word_index = 0;
SetBits( ch->flags, FL_CHAN_IS_SENTENCE );
VOX_LoadWord( ch );
}

View File

@@ -94,7 +94,6 @@ typedef struct rawchan_s
#define FL_CHAN_USE_LOOP BIT( 0 ) // don't loop default and local sounds
#define FL_CHAN_STATIC_SOUND BIT( 1 ) // use origin instead of fetching entnum's origin
#define FL_CHAN_LOCAL_SOUND BIT( 2 ) // it's a local menu sound (not looped, not paused)
#define FL_CHAN_IS_SENTENCE BIT( 3 ) // bit indicating vox sentence
#define FL_CHAN_SENTENCE_FINISHED BIT( 4 ) // if set, finished playing sentence
#define FL_CHAN_FINISHED BIT( 5 ) // if set, finished playing single word
@@ -123,7 +122,7 @@ typedef struct channel_s
double sample;
double forced_end;
wavdata_t *data;
voxword_t words[CVOXWORDMAX];
voxword_t *words; // dynamically allocated, (num_words + 1) entries, null sfx terminates
// TODO: add reserved bytes
} channel_t;