From abaf390dfc74c88b537c2895dce29e6469523e0a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 23 Dec 2025 02:34:07 +0500 Subject: [PATCH] engine: client: introduce a new mixer --- engine/client/avi/avi_ffmpeg.c | 2 +- engine/client/cl_game.c | 2 +- engine/client/cl_parse.c | 13 +- engine/client/cl_render.c | 2 +- engine/client/client.h | 3 +- engine/client/s_dsp.c | 273 +++---- engine/client/s_load.c | 58 +- engine/client/s_main.c | 344 ++++---- engine/client/s_mix.c | 1163 ++++++++++------------------ engine/client/s_mouth.c | 113 +-- engine/client/s_stream.c | 26 +- engine/client/s_utils.c | 76 +- engine/client/s_vox.c | 128 ++- engine/client/sound.h | 169 ++-- engine/client/soundlib/snd_main.c | 2 +- engine/client/vox.h | 37 - engine/common/common.h | 2 +- engine/common/soundlib/snd_utils.c | 2 +- 18 files changed, 954 insertions(+), 1461 deletions(-) delete mode 100644 engine/client/vox.h diff --git a/engine/client/avi/avi_ffmpeg.c b/engine/client/avi/avi_ffmpeg.c index 68fcb03a..e5479242 100644 --- a/engine/client/avi/avi_ffmpeg.c +++ b/engine/client/avi/avi_ffmpeg.c @@ -263,7 +263,7 @@ static void AVI_StreamAudio( movie_state_t *Avi ) // keep the same semantics, when S_RAW_SOUND_SOUNDTRACK doesn't play if S_StartStreaming wasn't enabled qboolean disable_stream = Avi->entnum == S_RAW_SOUND_SOUNDTRACK ? !s_listener.streaming : false; - if( !dma.initialized || disable_stream || s_listener.paused || !Avi->cached_audio ) + if( !dma.initialized || disable_stream || cl.paused || !Avi->cached_audio ) return; ch = S_FindRawChannel( Avi->entnum, true ); diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index f9ee4dbc..2bc817ab 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -4038,7 +4038,7 @@ qboolean CL_LoadProgs( const char *name ) for( i = 0; i < ARRAYSIZE( cdll_exports ); i++ ) { if( *(cdll_exports[i].func) != NULL ) - continue; // already gott through 'F' or 'GetClientAPI' + continue; // already got through 'F' or 'GetClientAPI' // functions are cleared before all the extensions are evaluated if(( *(cdll_exports[i].func) = (void *)COM_GetProcAddress( clgame.hInstance, cdll_exports[i].name )) == NULL ) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index be45673e..4f2b4c5a 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -364,15 +364,12 @@ CL_ParseSoundFade */ void CL_ParseSoundFade( sizebuf_t *msg ) { - float fadePercent, fadeOutSeconds; - float holdTime, fadeInSeconds; + int fade_percent = MSG_ReadByte( msg ); + int hold_time = MSG_ReadByte( msg ); + int fade_out_seconds = MSG_ReadByte( msg ); + int fade_in_seconds = MSG_ReadByte( msg ); - fadePercent = (float)MSG_ReadByte( msg ); - holdTime = (float)MSG_ReadByte( msg ); - fadeOutSeconds = (float)MSG_ReadByte( msg ); - fadeInSeconds = (float)MSG_ReadByte( msg ); - - S_FadeClientVolume( fadePercent, fadeOutSeconds, holdTime, fadeInSeconds ); + S_SoundFade( fade_percent, hold_time, fade_out_seconds, fade_in_seconds ); } /* diff --git a/engine/client/cl_render.c b/engine/client/cl_render.c index 9eda599c..2217d0bf 100644 --- a/engine/client/cl_render.c +++ b/engine/client/cl_render.c @@ -279,7 +279,7 @@ static render_api_t gRenderAPI = (void*)CL_ModelHandle, Sys_FloatTime, Cvar_Set, - S_FadeMusicVolume, + S_MusicFade, COM_SetRandomSeed, }; diff --git a/engine/client/client.h b/engine/client/client.h index 73dbb624..be952c8b 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1146,8 +1146,7 @@ void S_EndRegistration( void ); void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float fvol, float attn, int pitch, int flags, double sample, double end, int wordIndex ); void S_StartSound( const vec3_t pos, int ent, int chan, sound_t sfx, float vol, float attn, int pitch, int flags ); void S_AmbientSound( const vec3_t pos, int ent, sound_t handle, float fvol, float attn, int pitch, int flags ); -void S_FadeClientVolume( float fadePercent, float fadeOutSeconds, float holdTime, float fadeInSeconds ); -void S_FadeMusicVolume( float fadePercent ); +void S_SoundFade( int fade_percent, int hold_time, int fade_out_seconds, int fade_in_seconds ); void S_StartLocalSound( const char *name, float volume, qboolean reliable ); void SND_UpdateSound( void ); void S_ExtraUpdate( void ); diff --git a/engine/client/s_dsp.c b/engine/client/s_dsp.c index f2955dd2..bac996bc 100644 --- a/engine/client/s_dsp.c +++ b/engine/client/s_dsp.c @@ -18,22 +18,12 @@ GNU General Public License for more details. #include "client.h" #include "sound.h" -#define MAX_DELAY 0.4f -#define MAX_ROOM_TYPES ARRAYSIZE( rgsxpre ) - -#define MONODLY 0 -#define MAX_MONO_DELAY 0.4f - -#define REVERBPOS 1 -#define MAX_REVERB_DELAY 0.1f - -#define STEREODLY 3 -#define MAX_STEREO_DELAY 0.1f - -#define REVERB_XFADE 32 - -#define MAXDLY (STEREODLY + 1) -#define MAXLP 10 +#define MAX_ROOM_TYPES ARRAYSIZE( rgsxpre ) +#define MAX_MONO_DELAY 0.4f +#define MAX_REVERB_DELAY 0.1f +#define REVERB_XFADE 32 +#define MAX_STEREO_DELAY 0.1f +#define MAXLP 10 typedef struct sx_preset_s { @@ -154,7 +144,6 @@ static const sx_preset_t rgsxpre_hlalpha052[] = static const sx_preset_t *ptable = rgsxpre; // cvars -static CVAR_DEFINE_AUTO( dsp_off, "0", FCVAR_ARCHIVE, "disable DSP processing (deprecated)" ); static CVAR_DEFINE_AUTO( room_off, "0", FCVAR_ARCHIVE, "disable DSP processing (GoldSrc compatible cvar)" ); static CVAR_DEFINE_AUTO( dsp_coeff_table, "0", FCVAR_ARCHIVE, "select DSP coefficient table: 0 for release or 1 for alpha 0.52" ); static CVAR_DEFINE_AUTO( room_type, "0", 0, "current room type preset" ); @@ -190,9 +179,10 @@ static int sxmod1cur, sxmod2cur; static int sxmod1, sxmod2; static int sxhires; -static portable_samplepair_t *paintto = NULL; +static dly_t monodly; +static dly_t reverbdly[2]; +static dly_t stereodly; -static dly_t rgsxdly[MAXDLY]; // stereo is last static int rgsxlp[MAXLP]; static void SX_Profiling_f( void ); @@ -220,8 +210,14 @@ Starts sound crackling system */ void SX_Init( void ) { - memset( rgsxdly, 0, sizeof( rgsxdly )); - memset( rgsxlp, 0, sizeof( rgsxlp )); + dly_t nulldly = { 0 }; + + monodly = nulldly; + reverbdly[0] = nulldly; + reverbdly[1] = nulldly; + stereodly = nulldly; + + memset( rgsxlp, 0, sizeof( rgsxlp )); sxamodr = sxamodl = sxamodrt = sxamodlt = 255; idsp_dma_speed = SOUND_11k; @@ -232,7 +228,6 @@ void SX_Init( void ) sxmod1cur = sxmod1 = 350 * ( idsp_dma_speed / SOUND_11k ); sxmod2cur = sxmod2 = 450 * ( idsp_dma_speed / SOUND_11k ); - Cvar_RegisterVariable( &dsp_off ); Cvar_RegisterVariable( &room_off ); Cvar_RegisterVariable( &dsp_coeff_table ); @@ -252,7 +247,7 @@ void SX_Init( void ) Cvar_RegisterVariable( &sxste_delay ); - Cmd_AddCommand( "dsp_profile", SX_Profiling_f, "dsp stress-test, first argument is room_type" ); + Cmd_AddRestrictedCommand( "dsp_profile", SX_Profiling_f, "dsp stress-test, first argument is room_type" ); SX_ReloadRoomFX(); } @@ -264,14 +259,12 @@ DLY_Free Free memory allocated for DSP =========== */ -static void DLY_Free( int idelay ) +static void DLY_Free( dly_t *dly ) { - Assert( idelay >= 0 && idelay < MAXDLY ); - - if( rgsxdly[idelay].lpdelayline ) + if( dly->lpdelayline ) { - Z_Free( rgsxdly[idelay].lpdelayline ); - rgsxdly[idelay].lpdelayline = NULL; + Mem_Free( dly->lpdelayline ); + dly->lpdelayline = NULL; } } @@ -284,10 +277,10 @@ Stop DSP processor */ void SX_Free( void ) { - int i; - - for( i = 0; i <= 3; i++ ) - DLY_Free( i ); + DLY_Free( &monodly ); + DLY_Free( &reverbdly[0] ); + DLY_Free( &reverbdly[1] ); + DLY_Free( &stereodly ); Cmd_RemoveCommand( "dsp_profile" ); } @@ -300,17 +293,10 @@ DLY_Init Initialize dly =========== */ -static int DLY_Init( int idelay, float delay ) +static void DLY_Init( dly_t *cur, float delay ) { - dly_t *cur; + DLY_Free( cur ); // free dly if it's allocated - // DLY_Init called anytime with constants. So valid it in debug builds only. - Assert( idelay >= 0 && idelay < MAXDLY ); - Assert( delay > 0.0f && delay <= MAX_DELAY ); - - DLY_Free( idelay ); // free dly if it's allocated - - cur = &rgsxdly[idelay]; cur->cdelaysamplesmax = ((int)(delay * idsp_dma_speed) << sxhires) + 1; cur->lpdelayline = (int *)Mem_Calloc( sndpool, cur->cdelaysamplesmax * sizeof( int )); cur->xfade = 0; @@ -324,9 +310,6 @@ static int DLY_Init( int idelay, float delay ) cur->idelayinput = 0; cur->idelayoutput = cur->cdelaysamplesmax - cur->delaysamples; // NOTE: delaysamples must be set!!! - - - return 1; } /* @@ -354,15 +337,15 @@ Update stereo processor settings if we are in new room */ static void DLY_CheckNewStereoDelayVal( void ) { - dly_t *const dly = &rgsxdly[STEREODLY]; - float delay = sxste_delay.value; + dly_t *dly = &stereodly; + float delay = sxste_delay.value; if( !FBitSet( sxste_delay.flags, FCVAR_CHANGED )) return; if( delay == 0 ) { - DLY_Free( STEREODLY ); + DLY_Free( dly ); } else { @@ -375,7 +358,7 @@ static void DLY_CheckNewStereoDelayVal( void ) if( !dly->lpdelayline ) { dly->delaysamples = samples; - DLY_Init( STEREODLY, MAX_STEREO_DELAY ); + DLY_Init( dly, MAX_STEREO_DELAY ); } if( dly->delaysamples != samples ) @@ -389,7 +372,7 @@ static void DLY_CheckNewStereoDelayVal( void ) dly->modcur = dly->mod = 0; if( dly->delaysamples == 0 ) - DLY_Free( STEREODLY ); + DLY_Free( dly ); } } @@ -400,11 +383,10 @@ DLY_DoStereoDelay Do stereo processing ============= */ -static void DLY_DoStereoDelay( int count ) +static void DLY_DoStereoDelay( portable_samplepair_t *paint, int count ) { - int delay, samplexf; - dly_t *const dly = &rgsxdly[STEREODLY]; - portable_samplepair_t *paint = paintto; + dly_t *dly = &stereodly; + int delay, samplexf; if( !dly->lpdelayline ) return; // inactive @@ -443,7 +425,7 @@ static void DLY_DoStereoDelay( int count ) } // save left value to delay line - dly->lpdelayline[dly->idelayinput] = CLIP( paint->left ); + dly->lpdelayline[dly->idelayinput] = CLIP16( paint->left ); // paint new delay value paint->left = delay; @@ -467,14 +449,14 @@ Update delay processor settings if we are in new room */ static void DLY_CheckNewDelayVal( void ) { - float delay = sxdly_delay.value; - dly_t *const dly = &rgsxdly[MONODLY]; + dly_t *dly = &monodly; + float delay = sxdly_delay.value; if( FBitSet( sxdly_delay.flags, FCVAR_CHANGED )) { if( delay == 0 ) { - DLY_Free( MONODLY ); + DLY_Free( dly ); } else { @@ -483,7 +465,7 @@ static void DLY_CheckNewDelayVal( void ) // init dly if( !dly->lpdelayline ) - DLY_Init( MONODLY, MAX_MONO_DELAY ); + DLY_Init( dly, MAX_MONO_DELAY ); if( dly->lpdelayline ) { @@ -495,7 +477,7 @@ static void DLY_CheckNewDelayVal( void ) dly->idelayoutput = dly->cdelaysamplesmax - dly->delaysamples; if( !dly->delaysamples ) - DLY_Free( MONODLY ); + DLY_Free( dly ); } } @@ -511,11 +493,10 @@ DLY_DoDelay Do delay processing ============= */ -static void DLY_DoDelay( int count ) +static void DLY_DoDelay( portable_samplepair_t *paint, int count ) { - dly_t *const dly = &rgsxdly[MONODLY]; - portable_samplepair_t *paint = paintto; - int delay; + dly_t *dly = &monodly; + int delay; if( !dly->lpdelayline || !count ) return; // inactive @@ -529,7 +510,7 @@ static void DLY_DoDelay( int count ) { // calculate delayed value from average int val = (( paint->left + paint->right ) >> 1 ) + (( dly->delayfeedback * delay ) >> 8); - val = CLIP( val ); + val = CLIP16( val ); if( dly->lp ) // lowpass { @@ -542,8 +523,8 @@ static void DLY_DoDelay( int count ) val >>= 2; - paint->left = CLIP( paint->left + val ); - paint->right = CLIP( paint->right + val ); + paint->left = CLIP16( paint->left + val ); + paint->right = CLIP16( paint->right + val ); } else { @@ -562,32 +543,32 @@ RVB_SetUpDly Set up dly for reverb =========== */ -static void RVB_SetUpDly( int pos, float delay, int kmod ) +static void RVB_SetUpDly( dly_t *dly, float delay, int kmod ) { int samples; delay = Q_min( delay, MAX_REVERB_DELAY ); samples = (int)(delay * idsp_dma_speed) << sxhires; - if( !rgsxdly[pos].lpdelayline ) + if( !dly->lpdelayline ) { - rgsxdly[pos].delaysamples = samples; - DLY_Init( pos, MAX_REVERB_DELAY ); + dly->delaysamples = samples; + DLY_Init( dly, MAX_REVERB_DELAY ); } - rgsxdly[pos].modcur = rgsxdly[pos].mod = (int)(kmod * idsp_dma_speed / SOUND_11k) << sxhires; + dly->modcur = dly->mod = (int)(kmod * idsp_dma_speed / SOUND_11k) << sxhires; // set up crossfade, if delay has changed - if( rgsxdly[pos].delaysamples != samples ) + if( dly->delaysamples != samples ) { - rgsxdly[pos].idelayoutputxf = rgsxdly[pos].idelayinput - samples; - if( rgsxdly[pos].idelayoutputxf < 0 ) - rgsxdly[pos].idelayoutputxf += rgsxdly[pos].cdelaysamplesmax; - rgsxdly[pos].xfade = REVERB_XFADE; + dly->idelayoutputxf = dly->idelayinput - samples; + if( dly->idelayoutputxf < 0 ) + dly->idelayoutputxf += dly->cdelaysamplesmax; + dly->xfade = REVERB_XFADE; } - if( !rgsxdly[pos].delaysamples ) - DLY_Free( pos ); + if( !dly->delaysamples ) + DLY_Free( dly ); } @@ -600,21 +581,21 @@ Update reverb settings if we are in new room */ static void RVB_CheckNewReverbVal( void ) { - dly_t *const dly1 = &rgsxdly[REVERBPOS]; - dly_t *const dly2 = &rgsxdly[REVERBPOS + 1]; - float delay = sxrvb_size.value; + dly_t *dly1 = &reverbdly[0]; + dly_t *dly2 = &reverbdly[1]; + float delay = sxrvb_size.value; if( FBitSet( sxrvb_size.flags, FCVAR_CHANGED )) { if( delay == 0.0f ) { - DLY_Free( REVERBPOS ); - DLY_Free( REVERBPOS + 1 ); + DLY_Free( dly1 ); + DLY_Free( dly2 ); } else { - RVB_SetUpDly( REVERBPOS, sxrvb_size.value, 500 ); - RVB_SetUpDly( REVERBPOS+1, sxrvb_size.value * 0.71f, 700 ); + RVB_SetUpDly( dly1, delay, 500 ); + RVB_SetUpDly( dly2, delay * 0.71f, 700 ); } } @@ -669,7 +650,7 @@ static int RVB_DoReverbForOneDly( dly_t *dly, const int vlr, const portable_samp if( delay ) { val = vlr + ( ( dly->delayfeedback * delay ) >> 8 ); - val = CLIP( val ); + val = CLIP16( val ); } else val = vlr; @@ -703,12 +684,11 @@ RVB_DoReverb Do reverberation processing =========== */ -static void RVB_DoReverb( int count ) +static void RVB_DoReverb( portable_samplepair_t *paint, int count ) { - dly_t *const dly1 = &rgsxdly[REVERBPOS]; - dly_t *const dly2 = &rgsxdly[REVERBPOS+1]; - portable_samplepair_t *paint = paintto; - int vlr, voutm; + dly_t *dly1 = &reverbdly[0]; + dly_t *dly2 = &reverbdly[1]; + int vlr, voutm; if( !dly1->lpdelayline ) return; @@ -724,8 +704,8 @@ static void RVB_DoReverb( int count ) voutm /= 6; // alpha else voutm = (11 * voutm) >> 6; - paint->left = CLIP( paint->left + voutm ); - paint->right = CLIP( paint->right + voutm ); + paint->left = CLIP16( paint->left + voutm ); + paint->right = CLIP16( paint->right + voutm ); } } @@ -736,16 +716,14 @@ RVB_DoAMod Do amplification modulation processing =========== */ -static void RVB_DoAMod( int count ) +static void RVB_DoAMod( portable_samplepair_t *paint, int count ) { - portable_samplepair_t *paint = paintto; - if( !sxmod_lowpass.value && !sxmod_mod.value ) return; for( ; count; count--, paint++ ) { - portable_samplepair_t res = *paint; + portable_samplepair_t res = *paint; if( sxmod_lowpass.value ) { @@ -797,57 +775,13 @@ static void RVB_DoAMod( int count ) sxamodr--; } - paint->left = CLIP(res.left); - paint->right = CLIP(res.right); + paint->left = CLIP16( res.left ); + paint->right = CLIP16( res.right ); } } -/* -=========== -DSP_Process - -(xash dsp interface) -=========== -*/ -void DSP_Process( portable_samplepair_t *pbfront, int sampleCount ) +static void SX_CheckPresets( void ) { - if( dsp_off.value || room_off.value || !sampleCount ) - return; - - // preset is already installed by CheckNewDspPresets - paintto = pbfront; - - RVB_DoAMod( sampleCount ); - RVB_DoReverb( sampleCount ); - DLY_DoDelay( sampleCount ); - DLY_DoStereoDelay( sampleCount ); -} - -/* -=========== -DSP_ClearState - -(xash dsp interface) -=========== -*/ -void DSP_ClearState( void ) -{ - Cvar_DirectSet( &room_type, "0" ); - SX_ReloadRoomFX(); -} - -/* -=========== -CheckNewDspPresets - -(xash dsp interface) -=========== -*/ -void CheckNewDspPresets( void ) -{ - if( dsp_off.value || room_off.value ) - return; - if( FBitSet( dsp_coeff_table.flags, FCVAR_CHANGED )) { switch( (int)dsp_coeff_table.value ) @@ -869,9 +803,7 @@ void CheckNewDspPresets( void ) ClearBits( dsp_coeff_table.flags, FCVAR_CHANGED ); } - if( s_listener.waterlevel > 2 ) - idsp_room = roomwater_type.value; - else idsp_room = room_type.value; + idsp_room = cl.local.waterlevel > 2 ? roomwater_type.value : room_type.value; // don't pass invalid presets idsp_room = bound( 0, idsp_room, MAX_ROOM_TYPES ); @@ -887,9 +819,7 @@ void CheckNewDspPresets( void ) if( idsp_room != room_typeprev ) { - const sx_preset_t *cur; - - cur = ptable + idsp_room; + const sx_preset_t *cur = &ptable[idsp_room]; Cvar_DirectSetValue( &sxmod_lowpass, cur->room_lp ); Cvar_DirectSetValue( &sxmod_mod, cur->room_mod ); @@ -913,6 +843,39 @@ void CheckNewDspPresets( void ) ClearBits( sxste_delay.flags, FCVAR_CHANGED ); } +/* +=========== +SX_RoomFX + +(xash dsp interface) +=========== +*/ +void SX_RoomFX( portable_samplepair_t *paint, int num_samples ) +{ + if( room_off.value || !num_samples ) + return; + + SX_CheckPresets(); + + RVB_DoAMod( paint, num_samples ); + RVB_DoReverb( paint, num_samples ); + DLY_DoDelay( paint, num_samples ); + DLY_DoStereoDelay( paint, num_samples ); +} + +/* +=========== +SX_ClearState + +(xash dsp interface) +=========== +*/ +void SX_ClearState( void ) +{ + Cvar_DirectSet( &room_type, "0" ); + SX_ReloadRoomFX(); +} + static void SX_Profiling_f( void ) { portable_samplepair_t testbuffer[512]; @@ -920,7 +883,7 @@ static void SX_Profiling_f( void ) double start, end; int i, calls; - for( i = 0; i < 512; i++ ) + for( i = 0; i < ARRAYSIZE( testbuffer ); i++ ) { testbuffer[i].left = COM_RandomLong( 0, 3000 ); testbuffer[i].right = COM_RandomLong( 0, 3000 ); @@ -930,7 +893,7 @@ static void SX_Profiling_f( void ) { Cvar_DirectSetValue( &room_type, Q_atof( Cmd_Argv( 1 ))); SX_ReloadRoomFX(); - CheckNewDspPresets(); // we just need idsp_room immediately, for message below + SX_CheckPresets(); // we just need idsp_room immediately, for message below } Con_Printf( "Profiling 10000 calls to DSP. Sample count is 512, room_type is %i\n", idsp_room ); @@ -938,7 +901,7 @@ static void SX_Profiling_f( void ) start = Platform_DoubleTime(); for( calls = 10000; calls; calls-- ) { - DSP_Process( testbuffer, 512 ); + SX_RoomFX( testbuffer, 512 ); } end = Platform_DoubleTime(); @@ -948,6 +911,6 @@ static void SX_Profiling_f( void ) { Cvar_DirectSetValue( &room_type, oldroom ); SX_ReloadRoomFX(); - CheckNewDspPresets(); + SX_CheckPresets(); } } diff --git a/engine/client/s_load.c b/engine/client/s_load.c index 9d95500c..4dbed9e3 100644 --- a/engine/client/s_load.c +++ b/engine/client/s_load.c @@ -21,14 +21,16 @@ GNU General Public License for more details. // than could actually be referenced during gameplay, // because we don't want to free anything until we are // sure we won't need it. -#define MAX_SFX 8192 -#define MAX_SFX_HASH (MAX_SFX/4) +#define MAX_SFX 8192 +#define MAX_SFX_HASH (MAX_SFX/4) -static int s_numSfx = 0; -static sfx_t s_knownSfx[MAX_SFX]; -static sfx_t *s_sfxHashList[MAX_SFX_HASH]; -static string s_sentenceImmediateName; // keep dummy sentence name -qboolean s_registering = false; +static int s_numSfx = 0; +static sfx_t s_knownSfx[MAX_SFX]; +static sfx_t *s_sfxHashList[MAX_SFX_HASH]; +static qboolean s_registering = false; + +#define SENTENCE_INDEX -99999 // unique sentence index +static string s_sentenceImmediateName; // keep dummy sentence name /* ================= @@ -70,36 +72,6 @@ void S_SoundList_f( void ) Con_Printf( "\n" ); } -// return true if char 'c' is one of 1st 2 characters in pch -qboolean S_TestSoundChar( const char *pch, char c ) -{ - char *pcht = (char *)pch; - int i; - - if( !pch || !*pch ) - return false; - - // check first 2 characters - for( i = 0; i < 2; i++ ) - { - if( *pcht == c ) - return true; - pcht++; - } - return false; -} - -// return pointer to first valid character in file name -char *S_SkipSoundChar( const char *pch ) -{ - char *pcht = (char *)pch; - - // check first character - if( *pcht == '!' ) - pcht++; - return pcht; -} - /* ================= S_CreateDefaultSound @@ -107,13 +79,13 @@ S_CreateDefaultSound */ static wavdata_t *S_CreateDefaultSound( void ) { - wavdata_t *sc; uint samples = SOUND_DMA_SPEED; uint channels = 1; uint width = 2; size_t size = samples * width * channels; - sc = Mem_Calloc( sndpool, sizeof( wavdata_t ) + size ); + wavdata_t *sc = Mem_Calloc( sndpool, sizeof( wavdata_t ) + size ); + sc->width = width; sc->channels = channels; sc->rate = SOUND_DMA_SPEED; @@ -150,7 +122,8 @@ wavdata_t *S_LoadSound( sfx_t *sfx ) if( sfx->name[0] == '*' ) sc = FS_LoadSound( sfx->name + 1, NULL, 0 ); - else sc = FS_LoadSound( sfx->name, NULL, 0 ); + else + sc = FS_LoadSound( sfx->name, NULL, 0 ); } if( !sc ) sc = S_CreateDefaultSound(); @@ -167,16 +140,13 @@ wavdata_t *S_LoadSound( sfx_t *sfx ) return sfx->cache; } -// ======================================================================= -// Load a sound -// ======================================================================= /* ================== S_FindName ================== */ -sfx_t *S_FindName( const char *pname, int *pfInCache ) +sfx_t *S_FindName( const char *pname, qboolean *pfInCache ) { sfx_t *sfx; uint i, hash; diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 7cc8f7cf..c8e55d97 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -20,24 +20,36 @@ GNU General Public License for more details. #include "pm_local.h" #include "platform/platform.h" -dma_t dma; +// structure used for fading in and out client sound volume. +struct +{ + int start_percent; + int percent; + double start_time; + int out_seconds; + int hold_time; + int in_seconds; +} soundfade; + +dma_t dma; poolhandle_t sndpool; -static soundfade_t soundfade; -channel_t channels[MAX_CHANNELS]; -sound_t ambient_sfx[NUM_AMBIENTS]; -rawchan_t *raw_channels[MAX_RAW_CHANNELS]; -qboolean snd_ambient = false; -qboolean snd_fade_sequence = false; -listener_t s_listener; -int total_channels; -int soundtime; // sample PAIRS -int paintedtime; // sample PAIRS +sound_t ambient_sfx[NUM_AMBIENTS]; +qboolean snd_ambient = false; +qboolean snd_fade_sequence = false; +listener_t s_listener; + +channel_t channels[MAX_CHANNELS]; +rawchan_t *raw_channels[MAX_RAW_CHANNELS]; +int total_channels; + +int soundtime; // sample PAIRS +int paintedtime; // sample PAIRS static CVAR_DEFINE( s_volume, "volume", "0.7", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "sound volume" ); CVAR_DEFINE( s_musicvolume, "MP3Volume", "1.0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "background music volume" ); static CVAR_DEFINE( s_mixahead, "_snd_mixahead", "0.12", FCVAR_FILTERABLE, "how much sound to mix ahead of time" ); static CVAR_DEFINE_AUTO( s_show, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "show playing sounds" ); -CVAR_DEFINE_AUTO( s_lerping, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "apply interpolation to sound output (deprecated)" ); +CVAR_DEFINE_AUTO( s_lerping, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "apply interpolation to sound output" ); static CVAR_DEFINE( s_ambient_level, "ambient_level", "0.3", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "volume of environment noises (water and wind)" ); static CVAR_DEFINE( s_ambient_fade, "ambient_fade", "1000", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "rate of volume fading when client is moving" ); static CVAR_DEFINE_AUTO( s_combine_sounds, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "combine channels with same sounds" ); @@ -68,26 +80,31 @@ float S_GetMasterVolume( void ) return 0.0f; } - if( !s_listener.inmenu && soundfade.percent != 0 ) + // apparently goldsrc doesn't care about soundfade + // despite this, let's keep support for it + if( cls.key_dest != key_menu && soundfade.percent != 0 ) { - scale = bound( 0.0f, soundfade.percent / 100.0f, 1.0f ); + scale = soundfade.percent / 100.f; + scale = bound( 0.0f, scale, 1.0f ); scale = 1.0f - scale; } + return s_volume.value * scale; } /* ================= -S_FadeClientVolume +S_SoundFade ================= */ -void S_FadeClientVolume( float fadePercent, float fadeOutSeconds, float holdTime, float fadeInSeconds ) +void S_SoundFade( int fade_percent, int hold_time, int fade_out_seconds, int fade_in_seconds ) { - soundfade.starttime = cl.mtime[0]; - soundfade.initial_percent = fadePercent; - soundfade.fadeouttime = fadeOutSeconds; - soundfade.holdtime = holdTime; - soundfade.fadeintime = fadeInSeconds; + soundfade.start_percent = fade_percent; + soundfade.hold_time = hold_time; + soundfade.out_seconds = fade_out_seconds; + soundfade.in_seconds = fade_in_seconds; + + soundfade.start_time = host.realtime; } /* @@ -97,7 +114,7 @@ S_IsClient */ static qboolean S_IsClient( int entnum ) { - return ( entnum == s_listener.entnum ); + return entnum == s_listener.entnum; } @@ -115,10 +132,10 @@ void S_FreeChannel( channel_t *ch ) ch->sfx = NULL; ch->name[0] = '\0'; ch->use_loop = false; - ch->isSentence = false; - - // clear mixer - memset( &ch->pMixer, 0, sizeof( ch->pMixer )); + ch->is_sentence = false; + ch->forced_end = ch->sample = 0.0; + ch->data = NULL; + ch->finished = false; SND_CloseMouth( ch ); } @@ -130,52 +147,39 @@ S_UpdateSoundFade */ static void S_UpdateSoundFade( void ) { - float f, totaltime, elapsed; - - // determine current fade value. - // assume no fading remains - soundfade.percent = 0; - - totaltime = soundfade.fadeouttime + soundfade.fadeintime + soundfade.holdtime; - - elapsed = cl.mtime[0] - soundfade.starttime; - - // clock wrapped or reset (BUG) or we've gone far enough - if( elapsed < 0.0f || elapsed >= totaltime || totaltime <= 0.0f ) - return; - - // We are in the fade time, so determine amount of fade. - if( soundfade.fadeouttime > 0.0f && ( elapsed < soundfade.fadeouttime )) + if( host.realtime < soundfade.in_seconds + soundfade.out_seconds + soundfade.hold_time + soundfade.start_time ) { - // ramp up - f = elapsed / soundfade.fadeouttime; - } - else if( elapsed <= ( soundfade.fadeouttime + soundfade.holdtime )) // Inside the hold time - { - // stay - f = 1.0f; + float f = host.realtime - soundfade.start_time; + + if( soundfade.out_seconds && ( f < soundfade.out_seconds )) + { + soundfade.percent = soundfade.start_percent * ( f / soundfade.out_seconds ); + } + else if ( f >= soundfade.hold_time + soundfade.out_seconds ) + { + f = f - ( soundfade.hold_time + soundfade.out_seconds ); + soundfade.percent = soundfade.start_percent * ( 1.0f - f / (float)soundfade.out_seconds ); + } + else + { + soundfade.percent = soundfade.start_percent; + } + + if( snd_fade_sequence ) + { + S_MusicFade( soundfade.percent ); + + if( soundfade.percent >= 100 ) + { + S_StopAllSounds( false ); + S_StopBackgroundTrack(); + snd_fade_sequence = false; + } + } } else { - // ramp down - f = ( elapsed - ( soundfade.fadeouttime + soundfade.holdtime ) ) / soundfade.fadeintime; - f = 1.0f - f; // backward interpolated... - } - - // spline it. - f = -( cos( M_PI * f ) - 1 ) / 2; - f = bound( 0.0f, f, 1.0f ); - - soundfade.percent = soundfade.initial_percent * f; - - if( snd_fade_sequence ) - S_FadeMusicVolume( soundfade.percent ); - - if( snd_fade_sequence && soundfade.percent == 100.0f ) - { - S_StopAllSounds( false ); - S_StopBackgroundTrack(); - snd_fade_sequence = false; + soundfade.percent = 0; } } @@ -190,14 +194,11 @@ exceptions). */ static qboolean SND_FStreamIsPlaying( sfx_t *sfx ) { - int ch_idx; - - for( ch_idx = NUM_AMBIENTS; ch_idx < MAX_DYNAMIC_CHANNELS; ch_idx++ ) + for( int i = NUM_AMBIENTS; i < MAX_DYNAMIC_CHANNELS; i++ ) { - if( channels[ch_idx].sfx == sfx ) + if( channels[i].sfx == sfx ) return true; } - return false; } @@ -212,22 +213,22 @@ static int SND_GetChannelTimeLeft( const channel_t *ch ) { int remaining; - if( ch->pMixer.finished || !ch->sfx || !ch->sfx->cache ) + if( ch->finished || !ch->sfx || !ch->sfx->cache ) return 0; - if( ch->isSentence ) // sentences are special, count all remaining words + if( ch->is_sentence ) // sentences are special, count all remaining words { int i; - if( !ch->currentWord ) + if( ch->sentence_finished ) return 0; // current word - remaining = ch->currentWord->forcedEndSample - ch->currentWord->sample; + remaining = ch->forced_end - ch->sample; // here we count all remaining words, stopping if no sfx or sound file is available // see VOX_LoadWord - for( i = ch->wordIndex + 1; i < ARRAYSIZE( ch->words ); i++ ) + for( i = ch->word_index + 1; i < ARRAYSIZE( ch->words ); i++ ) { wavdata_t *sc; int end; @@ -248,12 +249,9 @@ static int SND_GetChannelTimeLeft( const channel_t *ch ) } else { - int curpos; - int samples; + int samples = ch->sfx->cache->samples; + int curpos = S_AdjustLoopedSamplePosition( ch->sfx->cache, ch->sample, ch->use_loop ); - // handle position looping - samples = ch->sfx->cache->samples; - curpos = S_ConvertLoopedPosition( ch->sfx->cache, ch->pMixer.sample, ch->use_loop ); remaining = bound( 0, samples - curpos, samples ); } @@ -391,6 +389,44 @@ channel_t *SND_PickStaticChannel( const vec3_t pos, sfx_t *sfx ) return ch; } +static qboolean S_MaybeAlterChannel( channel_t *ch, int entnum, int entchannel, int flags, const sfx_t *sfx, int pitch, int vol ) +{ + if( !ch->sfx ) + return false; + + if( ch->entnum != entnum ) + return false; + + // if no sfx passed, check if it's a sentence + if( !sfx ) + { + if( !ch->is_sentence ) + return false; + } + else + { + if( ch->sfx != sfx ) + return false; + } + + if( ch->entchannel != entchannel ) + return false; + + if( FBitSet( flags, SND_STOP )) + { + S_FreeChannel( ch ); + return true; + } + + if( FBitSet( flags, SND_CHANGE_PITCH )) + ch->basePitch = pitch; + + if( FBitSet( flags, SND_CHANGE_VOL )) + ch->master_vol = vol; + + return true; +} + /* ================= S_AlterChannel @@ -403,7 +439,7 @@ returns TRUE if sound was altered, returns FALSE if sound was not found (sound is not playing) ================= */ -static int S_AlterChannel( int entnum, int channel, sfx_t *sfx, int vol, int pitch, int flags ) +static int S_AlterChannel( int entnum, int channel, const sfx_t *sfx, int vol, int pitch, int flags ) { channel_t *ch; int i; @@ -413,24 +449,14 @@ static int S_AlterChannel( int entnum, int channel, sfx_t *sfx, int vol, int pit // This is a sentence name. // For sentences: assume that the entity is only playing one sentence // at a time, so we can just shut off - // any channel that has ch->isSentence >= 0 and matches the entnum. + // any channel that has ch->is_sentence >= 0 and matches the entnum. for( i = NUM_AMBIENTS, ch = channels + NUM_AMBIENTS; i < total_channels; i++, ch++ ) { - if( ch->entnum == entnum && ch->entchannel == channel && ch->sfx && ch->isSentence ) - { - if( flags & SND_CHANGE_PITCH ) - ch->basePitch = pitch; - - if( flags & SND_CHANGE_VOL ) - ch->master_vol = vol; - - if( flags & SND_STOP ) - S_FreeChannel( ch ); - + if( S_MaybeAlterChannel( ch, entnum, channel, flags, NULL, pitch, vol )) return true; - } } + // channel not found return false; @@ -439,20 +465,10 @@ static int S_AlterChannel( int entnum, int channel, sfx_t *sfx, int vol, int pit // regular sound or streaming sound for( i = NUM_AMBIENTS, ch = channels + NUM_AMBIENTS; i < total_channels; i++, ch++ ) { - if( ch->entnum == entnum && ch->entchannel == channel && ch->sfx == sfx ) - { - if( flags & SND_CHANGE_PITCH ) - ch->basePitch = pitch; - - if( flags & SND_CHANGE_VOL ) - ch->master_vol = vol; - - if( flags & SND_STOP ) - S_FreeChannel( ch ); - + if( S_MaybeAlterChannel( ch, entnum, channel, flags, sfx, pitch, vol )) return true; - } } + return false; } @@ -595,7 +611,7 @@ void S_StartSound( const vec3_t pos, int ent, int chan, sound_t handle, float fv target_chan->entnum = ent; target_chan->entchannel = chan; target_chan->basePitch = pitch; - target_chan->isSentence = false; + target_chan->is_sentence = false; target_chan->sfx = sfx; pSource = NULL; @@ -693,7 +709,7 @@ void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float target_chan->entnum = ent; target_chan->entchannel = chan; target_chan->basePitch = pitch; - target_chan->isSentence = false; + target_chan->is_sentence = false; target_chan->sfx = sfx; pSource = NULL; @@ -712,12 +728,12 @@ void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float if( wordIndex != 0 ) { VOX_FreeWord( target_chan ); // release first loaded word - target_chan->wordIndex = wordIndex; // restore current word + target_chan->word_index = wordIndex; // restore current word VOX_LoadWord( target_chan ); - if( target_chan->currentWord ) + if( !target_chan->sentence_finished ) { - target_chan->sfx = target_chan->words[target_chan->wordIndex].sfx; + target_chan->sfx = target_chan->words[target_chan->word_index].sfx; sfx = target_chan->sfx; pSource = sfx->cache; } @@ -747,8 +763,8 @@ void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float // so we should keep all sounds an actual and waiting for player spawn. // apply the sample offests - target_chan->pMixer.sample = sample; - target_chan->pMixer.forcedEndSample = end; + target_chan->sample = sample; + target_chan->forced_end = end; // Init client entity mouth movement vars SND_InitMouth( ent, chan ); @@ -817,7 +833,7 @@ 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; - ch->isSentence = false; + ch->is_sentence = false; ch->name[0] = '\0'; } @@ -846,7 +862,7 @@ void S_AmbientSound( const vec3_t pos, int ent, sound_t handle, float fvol, floa S_StartLocalSound ================== */ -void S_StartLocalSound( const char *name, float volume, qboolean reliable ) +void S_StartLocalSound( const char *name, float volume, qboolean reliable ) { sound_t sfxHandle; int flags = (SND_LOCALSOUND|SND_STOP_LOOPING); @@ -878,7 +894,7 @@ int S_GetCurrentStaticSounds( soundlist_t *pout, int size ) { if( channels[i].entchannel == CHAN_STATIC && channels[i].sfx && channels[i].sfx->name[0] ) { - if( channels[i].isSentence && channels[i].name[0] ) + if( channels[i].is_sentence && channels[i].name[0] ) Q_strncpy( pout->name, channels[i].name, sizeof( pout->name )); else Q_strncpy( pout->name, channels[i].sfx->name, sizeof( pout->name )); pout->entnum = channels[i].entnum; @@ -888,9 +904,9 @@ int S_GetCurrentStaticSounds( soundlist_t *pout, int size ) pout->looping = ( channels[i].use_loop && FBitSet( channels[i].sfx->cache->flags, SOUND_LOOPED )); pout->pitch = channels[i].basePitch; pout->channel = channels[i].entchannel; - pout->wordIndex = channels[i].wordIndex; - pout->samplePos = channels[i].pMixer.sample; - pout->forcedEnd = channels[i].pMixer.forcedEndSample; + pout->wordIndex = channels[i].word_index; + pout->samplePos = channels[i].sample; + pout->forcedEnd = channels[i].forced_end; sounds_left--; pout++; @@ -925,7 +941,7 @@ int S_GetCurrentDynamicSounds( soundlist_t *pout, int size ) if( channels[i].entchannel == CHAN_STATIC && looped && !Host_IsQuakeCompatible()) continue; // never serialize static looped sounds. It will be restoring in game code - if( channels[i].isSentence && channels[i].name[0] ) + if( channels[i].is_sentence && channels[i].name[0] ) Q_strncpy( pout->name, channels[i].name, sizeof( pout->name )); else Q_strncpy( pout->name, channels[i].sfx->name, sizeof( pout->name )); pout->entnum = (channels[i].entnum < 0) ? 0 : channels[i].entnum; @@ -934,9 +950,9 @@ int S_GetCurrentDynamicSounds( soundlist_t *pout, int size ) pout->attenuation = channels[i].dist_mult * SND_CLIP_DISTANCE; pout->pitch = channels[i].basePitch; pout->channel = channels[i].entchannel; - pout->wordIndex = channels[i].wordIndex; - pout->samplePos = channels[i].pMixer.sample; - pout->forcedEnd = channels[i].pMixer.forcedEndSample; + pout->wordIndex = channels[i].word_index; + pout->samplePos = channels[i].sample; + pout->forcedEnd = channels[i].forced_end; pout->looping = looped; sounds_left--; @@ -1014,13 +1030,13 @@ static void S_UpdateAmbientSounds( void ) // don't adjust volume too fast if( chan->master_vol < vol ) { - chan->master_vol += round( s_listener.frametime * s_ambient_fade.value ); + chan->master_vol += round( cl_clientframetime() * s_ambient_fade.value ); if( chan->master_vol > vol ) chan->master_vol = vol; } else if( chan->master_vol > vol ) { - chan->master_vol -= round( s_listener.frametime * s_ambient_fade.value ); + chan->master_vol -= round( cl_clientframetime() * s_ambient_fade.value ); if( chan->master_vol < vol ) chan->master_vol = vol; } @@ -1330,10 +1346,11 @@ static void S_ClearBuffer( void ) S_ClearRawChannels(); SNDDMA_BeginPainting (); - if( dma.buffer ) memset( dma.buffer, 0, dma.samples * 2 ); + if( dma.buffer ) + memset( dma.buffer, 0, dma.samples * 2 ); SNDDMA_Submit (); - MIX_ClearAllPaintBuffers( PAINTBUFFER_SIZE, true ); + S_ClearBuffers( PAINTBUFFER_SIZE ); } /* @@ -1370,7 +1387,7 @@ void S_StopAllSounds( qboolean ambient ) S_FreeChannel( &channels[i] ); } - DSP_ClearState(); + SX_ClearState(); // clear all the channels memset( channels, 0, sizeof( channels )); @@ -1452,7 +1469,7 @@ static void S_UpdateChannels( void ) endtime -= ( endtime - paintedtime ) & 0x3; } - MIX_PaintChannels( endtime ); + S_PaintChannels( endtime ); SNDDMA_Submit(); } @@ -1511,12 +1528,6 @@ void SND_UpdateSound( void ) // release raw-channels that no longer used more than 10 secs S_FreeIdleRawChannels(); - s_listener.frametime = (cl.time - cl.oldtime); - s_listener.waterlevel = cl.local.waterlevel; - s_listener.active = CL_IsInGame(); - s_listener.inmenu = cls.key_dest == key_menu; - s_listener.paused = cl.paused; - // update general area ambient sound sources S_UpdateAmbientSounds(); @@ -1761,6 +1772,25 @@ static void S_StopSound_f( void ) S_StopAllSounds( true ); } +/* +================= +S_SoundFade_f +================= +*/ +static void S_Fade_f( void ) +{ + int hold_time = 5; + + if( Cmd_Argc() == 2 ) + { + hold_time = Q_atof( Cmd_Argv( 1 )); // was float + hold_time = bound( 1, hold_time, 60 ); + } + + S_SoundFade( 100, 1, hold_time, 0 ); + snd_fade_sequence = true; +} + /* ================= S_SoundFade_f @@ -1768,13 +1798,34 @@ S_SoundFade_f */ static void S_SoundFade_f( void ) { - int c = Cmd_Argc(); - float fadeTime = 5.0f; + int c = Cmd_Argc(); + int fade_percent; + int hold_time; + int fade_out_seconds; + int fade_in_seconds; - if( c == 2 ) - fadeTime = bound( 1.0f, atof( Cmd_Argv( 1 )), 60.0f ); + if( c != 3 && c != 5 ) + { + Con_Printf( S_USAGE "soundfade: [out] [in]\n" ); + return; + } - S_FadeClientVolume( 100.0f, fadeTime, 1.0f, 0.0f ); + fade_percent = Q_atoi( Cmd_Argv( 1 )); + fade_percent = bound( 0, fade_percent, 100 ); + + hold_time = Q_atoi( Cmd_Argv( 2 )); + hold_time = Q_min( hold_time, 255 ); + + if( c == 5 ) + { + fade_out_seconds = Q_atoi( Cmd_Argv( 3 )); + fade_out_seconds = Q_min( fade_out_seconds, 255 ); + + fade_in_seconds = Q_atoi( Cmd_Argv( 4 )); + fade_in_seconds = Q_min( fade_in_seconds, 255 ); + } + + S_SoundFade( fade_percent, hold_time, fade_out_seconds, fade_in_seconds ); snd_fade_sequence = true; } @@ -1856,7 +1907,8 @@ qboolean S_Init( void ) Cmd_AddCommandWithFlags( "music", S_Music_f, "starting a background track", CMD_OVERRIDABLE ); Cmd_AddCommand( "soundlist", S_SoundList_f, "display loaded sounds" ); Cmd_AddCommand( "s_info", S_SoundInfo_f, "print sound system information" ); - Cmd_AddCommand( "s_fade", S_SoundFade_f, "fade all sounds then stop all" ); + Cmd_AddCommand( "s_fade", S_Fade_f, "fade all sounds then stop all" ); + Cmd_AddCommand( "soundfade", S_SoundFade_f, "fade all sounds then stop all (goldsrc compatible)" ); Cmd_AddCommand( "+voicerecord", S_VoiceRecordStart_f, "start voice recording" ); Cmd_AddCommand( "-voicerecord", S_VoiceRecordStop_f, "stop voice recording" ); Cmd_AddCommand( "spk", S_SayReliable_f, "reliable play a specified sententce" ); @@ -1877,9 +1929,7 @@ qboolean S_Init( void ) // clear ambient sounds memset( ambient_sfx, 0, sizeof( ambient_sfx )); - MIX_InitAllPaintbuffers (); SX_Init (); - S_InitScaletable (); S_StopAllSounds ( true ); S_InitSounds (); VOX_Init (); @@ -1902,6 +1952,7 @@ void S_Shutdown( void ) Cmd_RemoveCommand( "soundlist" ); Cmd_RemoveCommand( "s_info" ); Cmd_RemoveCommand( "s_fade" ); + Cmd_RemoveCommand( "soundfade" ); Cmd_RemoveCommand( "+voicerecord" ); Cmd_RemoveCommand( "-voicerecord" ); Cmd_RemoveCommand( "speak" ); @@ -1914,6 +1965,5 @@ void S_Shutdown( void ) SX_Free (); SNDDMA_Shutdown (); - MIX_FreeAllPaintbuffers (); Mem_FreePool( &sndpool ); } diff --git a/engine/client/s_mix.c b/engine/client/s_mix.c index 0770d963..e93b3252 100644 --- a/engine/client/s_mix.c +++ b/engine/client/s_mix.c @@ -17,743 +17,388 @@ GNU General Public License for more details. #include "sound.h" #include "client.h" -enum +static portable_samplepair_t roombuffer[(PAINTBUFFER_SIZE+1)], paintbuffer[(PAINTBUFFER_SIZE+1)]; + +#define S_MakeMixMono( x ) \ + static void S_MixMono ## x( portable_samplepair_t *pbuf, const int *volume, const void *buf, int num_samples ) \ + { \ + const int##x##_t *data = buf; \ + for( int i = 0; i < num_samples; i++ ) \ + { \ + pbuf[i].left += ( data[i] * volume[0] ) >> ( x - 8 ); \ + pbuf[i].right += ( data[i] * volume[1] ) >> ( x - 8 ); \ + } \ + } \ + +#define S_MakeMixStereo( x ) \ + static void S_MixStereo ## x( portable_samplepair_t *pbuf, const int *volume, const void *buf, int num_samples ) \ + { \ + const int##x##_t *data = buf; \ + for( int i = 0; i < num_samples; i++ ) \ + { \ + pbuf[i].left += ( data[i * 2 + 0] * volume[0] ) >> ( x - 8 ); \ + pbuf[i].right += ( data[i * 2 + 1] * volume[1] ) >> ( x - 8 ); \ + } \ + } \ + +#define S_MakeMixMonoPitch( x ) \ + static void S_MixMonoPitch ## x( portable_samplepair_t *pbuf, const int *volume, const void *buf, double offset_frac, double rate_scale, int num_samples ) \ + { \ + const int##x##_t *data = buf; \ + uint sample_idx = 0; \ + for( int i = 0; i < num_samples; i++ ) \ + { \ + pbuf[i].left += ( data[sample_idx] * volume[0] ) >> ( x - 8 ); \ + pbuf[i].right += ( data[sample_idx] * volume[1] ) >> ( x - 8 ); \ + offset_frac += rate_scale; \ + sample_idx += (uint)offset_frac; \ + offset_frac -= (uint)offset_frac; \ + } \ + } \ + +#define S_MakeMixStereoPitch( x ) \ + static void S_MixStereoPitch ## x( portable_samplepair_t *pbuf, const int *volume, const void *buf, double offset_frac, double rate_scale, int num_samples ) \ + { \ + const int##x##_t *data = buf; \ + uint sample_idx = 0; \ + for( int i = 0; i < num_samples; i++ ) \ + { \ + pbuf[i].left += ( data[sample_idx+0] * volume[0] ) >> ( x - 8 ); \ + pbuf[i].right += ( data[sample_idx+1] * volume[1] ) >> ( x - 8 ); \ + offset_frac += rate_scale; \ + sample_idx += (uint)offset_frac << 1; \ + offset_frac -= (uint)offset_frac; \ + } \ + } \ + +S_MakeMixMono( 8 ) +S_MakeMixMono( 16 ) +S_MakeMixStereo( 8 ) +S_MakeMixStereo( 16 ) +S_MakeMixMonoPitch( 8 ) +S_MakeMixMonoPitch( 16 ) +S_MakeMixStereoPitch( 8 ) +S_MakeMixStereoPitch( 16 ) + +static void S_MixAudio( portable_samplepair_t *pbuf, const int *pvol, const void *buf, int channels, int width, double offset_frac, double rate_scale, int num_samples ) { - IPAINTBUFFER = 0, - IROOMBUFFER, - ISTREAMBUFFER, - IVOICEBUFFER, - CPAINTBUFFERS, -}; - -#define CCHANVOLUMES 2 - -#define SND_SCALE_BITS 7 -#define SND_SCALE_SHIFT ( 8 - SND_SCALE_BITS ) -#define SND_SCALE_LEVELS ( 1 << SND_SCALE_BITS ) - -// fixed point stuff for real-time resampling -#define FIX_BITS 28 -#define FIX_SCALE ( 1U << FIX_BITS ) -#define FIX_MASK (( 1U << FIX_BITS ) - 1 ) -#define FIX_FLOAT( a ) ((uint)(( a ) * FIX_SCALE )) -#define FIX( a ) (((uint)( a )) << FIX_BITS ) -#define FIX_INTPART( a ) (((uint)( a )) >> FIX_BITS ) -#define FIX_FRACPART( a ) (( a ) & FIX_MASK ) - -typedef struct -{ - qboolean factive; // if true, mix to this paintbuffer using flags - portable_samplepair_t *pbuf; // front stereo mix buffer, for 2 or 4 channel mixing -} paintbuffer_t; - -static portable_samplepair_t *g_curpaintbuffer; -static portable_samplepair_t streambuffer[(PAINTBUFFER_SIZE+1)]; -static portable_samplepair_t paintbuffer[(PAINTBUFFER_SIZE+1)]; -static portable_samplepair_t roombuffer[(PAINTBUFFER_SIZE+1)]; -static portable_samplepair_t voicebuffer[(PAINTBUFFER_SIZE+1)]; -static paintbuffer_t paintbuffers[CPAINTBUFFERS]; - -static int snd_scaletable[SND_SCALE_LEVELS][256]; -void S_InitScaletable( void ) -{ - int i, j; - - for( i = 0; i < SND_SCALE_LEVELS; i++ ) + if( Q_equal( rate_scale, 1.0 )) { - for( j = 0; j < 256; j++ ) - snd_scaletable[i][j] = ((signed char)j) * i * (1<> 1) - 1); - - while( lpaintedtime < endtime ) - { - // handle recirculating buffer issues - lpos = lpaintedtime & sampleMask; - - snd_out = (short *)pbuf + (lpos << 1); - - snd_linear_count = (dma.samples>>1) - lpos; - if( lpaintedtime + snd_linear_count > endtime ) - snd_linear_count = endtime - lpaintedtime; - - snd_linear_count <<= 1; - - // write a linear blast of samples - for( i = 0; i < snd_linear_count; i += 2 ) + if( channels == 1 ) { - val = (snd_p[i+0] * 256) >> 8; - - if( val > 0x7fff ) snd_out[i+0] = 0x7fff; - else if( val < (short)0x8000 ) - snd_out[i+0] = (short)0x8000; - else snd_out[i+0] = val; - - val = (snd_p[i+1] * 256) >> 8; - if( val > 0x7fff ) snd_out[i+1] = 0x7fff; - else if( val < (short)0x8000 ) - snd_out[i+1] = (short)0x8000; - else snd_out[i+1] = val; - } - - snd_p += snd_linear_count; - lpaintedtime += (snd_linear_count >> 1); - } -} - -//=============================================================================== -// Mix buffer (paintbuffer) management routines -//=============================================================================== -// Activate a paintbuffer. All active paintbuffers are mixed in parallel within -// MIX_MixChannelsToPaintbuffer, according to flags -static void MIX_ActivatePaintbuffer( int ipaintbuffer ) -{ - Assert( ipaintbuffer < CPAINTBUFFERS ); - paintbuffers[ipaintbuffer].factive = true; -} - -static void MIX_SetCurrentPaintbuffer( int ipaintbuffer ) -{ - Assert( ipaintbuffer < CPAINTBUFFERS ); - g_curpaintbuffer = paintbuffers[ipaintbuffer].pbuf; - Assert( g_curpaintbuffer != NULL ); -} - -static int MIX_GetCurrentPaintbufferIndex( void ) -{ - int i; - - for( i = 0; i < CPAINTBUFFERS; i++ ) - { - if( g_curpaintbuffer == paintbuffers[i].pbuf ) - return i; - } - return 0; -} - -static paintbuffer_t *MIX_GetCurrentPaintbufferPtr( void ) -{ - int ipaint = MIX_GetCurrentPaintbufferIndex(); - - Assert( ipaint < CPAINTBUFFERS ); - return &paintbuffers[ipaint]; -} - -// Don't mix into any paintbuffers -static void MIX_DeactivateAllPaintbuffers( void ) -{ - int i; - - for( i = 0; i < CPAINTBUFFERS; i++ ) - paintbuffers[i].factive = false; -} - -// return pointer to front paintbuffer pbuf, given index -static portable_samplepair_t *MIX_GetPFrontFromIPaint( int ipaintbuffer ) -{ - Assert( ipaintbuffer < CPAINTBUFFERS ); - return paintbuffers[ipaintbuffer].pbuf; -} - -static paintbuffer_t *MIX_GetPPaintFromIPaint( int ipaint ) -{ - Assert( ipaint < CPAINTBUFFERS ); - return &paintbuffers[ipaint]; -} - -void MIX_FreeAllPaintbuffers( void ) -{ - // clear paintbuffer structs - memset( paintbuffers, 0, CPAINTBUFFERS * sizeof( paintbuffer_t )); -} - -// Initialize paintbuffers array, set current paint buffer to main output buffer IPAINTBUFFER -void MIX_InitAllPaintbuffers( void ) -{ - // clear paintbuffer structs - memset( paintbuffers, 0, CPAINTBUFFERS * sizeof( paintbuffer_t )); - - paintbuffers[IPAINTBUFFER].pbuf = paintbuffer; - paintbuffers[IROOMBUFFER].pbuf = roombuffer; - paintbuffers[ISTREAMBUFFER].pbuf = streambuffer; - paintbuffers[IVOICEBUFFER].pbuf = voicebuffer; - - MIX_SetCurrentPaintbuffer( IPAINTBUFFER ); -} - -/* -=============================================================================== - -CHANNEL MIXING - -=============================================================================== -*/ -static void S_PaintMonoFrom8( portable_samplepair_t *pbuf, int *volume, byte *pData, int outCount ) -{ - int *lscale, *rscale; - int i, data; - - lscale = snd_scaletable[volume[0] >> SND_SCALE_SHIFT]; - rscale = snd_scaletable[volume[1] >> SND_SCALE_SHIFT]; - - for( i = 0; i < outCount; i++ ) - { - data = pData[i]; - pbuf[i].left += lscale[data]; - pbuf[i].right += rscale[data]; - } -} - -static void S_PaintStereoFrom8( portable_samplepair_t *pbuf, int *volume, byte *pData, int outCount ) -{ - int *lscale, *rscale; - uint left, right; - word *data; - int i; - - lscale = snd_scaletable[volume[0] >> SND_SCALE_SHIFT]; - rscale = snd_scaletable[volume[1] >> SND_SCALE_SHIFT]; - data = (word *)pData; - - for( i = 0; i < outCount; i++, data++ ) - { - left = (byte)((*data & 0x00FF)); - right = (byte)((*data & 0xFF00) >> 8); - pbuf[i].left += lscale[left]; - pbuf[i].right += rscale[right]; - } -} - -static void S_PaintMonoFrom16( portable_samplepair_t *pbuf, int *volume, short *pData, int outCount ) -{ - int left, right; - int i, data; - - for( i = 0; i < outCount; i++ ) - { - data = pData[i]; - left = ( data * volume[0]) >> 8; - right = (data * volume[1]) >> 8; - pbuf[i].left += left; - pbuf[i].right += right; - } -} - -static void S_PaintStereoFrom16( portable_samplepair_t *pbuf, int *volume, short *pData, int outCount ) -{ - uint *data; - int left, right; - int i; - - data = (uint *)pData; - - for( i = 0; i < outCount; i++, data++ ) - { - left = (signed short)((*data & 0x0000FFFF)); - right = (signed short)((*data & 0xFFFF0000) >> 16); - - left = (left * volume[0]) >> 8; - right = (right * volume[1]) >> 8; - - pbuf[i].left += left; - pbuf[i].right += right; - } -} - -static void S_Mix8MonoTimeCompress( portable_samplepair_t *pbuf, int *volume, byte *pData, uint inputOffset, uint rateScale, int outCount, int timecompress ) -{ -} - -static void S_Mix8Mono( portable_samplepair_t *pbuf, int *volume, byte *pData, uint inputOffset, uint rateScale, int outCount, int timecompress ) -{ - int i; - uint sampleIndex = 0; - uint sampleFrac = inputOffset; - int *lscale, *rscale; - - if( timecompress != 0 ) - { - S_Mix8MonoTimeCompress( pbuf, volume, pData, inputOffset, rateScale, outCount, timecompress ); -// return; - } - - // Not using pitch shift? - if( rateScale == FIX( 1 )) - { - S_PaintMonoFrom8( pbuf, volume, pData, outCount ); - return; - } - - lscale = snd_scaletable[volume[0] >> SND_SCALE_SHIFT]; - rscale = snd_scaletable[volume[1] >> SND_SCALE_SHIFT]; - - for( i = 0; i < outCount; i++ ) - { - pbuf[i].left += lscale[pData[sampleIndex]]; - pbuf[i].right += rscale[pData[sampleIndex]]; - sampleFrac += rateScale; - sampleIndex += FIX_INTPART( sampleFrac ); - sampleFrac = FIX_FRACPART( sampleFrac ); - } -} - -static void S_Mix8Stereo( portable_samplepair_t *pbuf, int *volume, byte *pData, uint inputOffset, uint rateScale, int outCount ) -{ - int i; - uint sampleIndex = 0; - uint sampleFrac = inputOffset; - int *lscale, *rscale; - - // Not using pitch shift? - if( rateScale == FIX( 1 )) - { - S_PaintStereoFrom8( pbuf, volume, pData, outCount ); - return; - } - - lscale = snd_scaletable[volume[0] >> SND_SCALE_SHIFT]; - rscale = snd_scaletable[volume[1] >> SND_SCALE_SHIFT]; - - for( i = 0; i < outCount; i++ ) - { - pbuf[i].left += lscale[pData[sampleIndex+0]]; - pbuf[i].right += rscale[pData[sampleIndex+1]]; - sampleFrac += rateScale; - sampleIndex += FIX_INTPART( sampleFrac )<<1; - sampleFrac = FIX_FRACPART( sampleFrac ); - } -} - -static void S_Mix16Mono( portable_samplepair_t *pbuf, int *volume, short *pData, uint inputOffset, uint rateScale, int outCount ) -{ - int i; - uint sampleIndex = 0; - uint sampleFrac = inputOffset; - - // Not using pitch shift? - if( rateScale == FIX( 1 )) - { - S_PaintMonoFrom16( pbuf, volume, pData, outCount ); - return; - } - - for( i = 0; i < outCount; i++ ) - { - pbuf[i].left += (volume[0] * (int)( pData[sampleIndex] ))>>8; - pbuf[i].right += (volume[1] * (int)( pData[sampleIndex] ))>>8; - sampleFrac += rateScale; - sampleIndex += FIX_INTPART( sampleFrac ); - sampleFrac = FIX_FRACPART( sampleFrac ); - } -} - -static void S_Mix16Stereo( portable_samplepair_t *pbuf, int *volume, short *pData, uint inputOffset, uint rateScale, int outCount ) -{ - int i; - uint sampleIndex = 0; - uint sampleFrac = inputOffset; - - // Not using pitch shift? - if( rateScale == FIX( 1 )) - { - S_PaintStereoFrom16( pbuf, volume, pData, outCount ); - return; - } - - for( i = 0; i < outCount; i++ ) - { - pbuf[i].left += (volume[0] * (int)( pData[sampleIndex+0] ))>>8; - pbuf[i].right += (volume[1] * (int)( pData[sampleIndex+1] ))>>8; - sampleFrac += rateScale; - sampleIndex += FIX_INTPART(sampleFrac)<<1; - sampleFrac = FIX_FRACPART(sampleFrac); - } -} - -static void S_MixChannel( channel_t *pChannel, void *pData, int outputOffset, uint inputOffset, uint fracRate, int outCount, int timecompress ) -{ - int pvol[CCHANVOLUMES]; - paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr(); - wavdata_t *pSource = pChannel->sfx->cache; - portable_samplepair_t *pbuf; - - Assert( pSource != NULL ); - - pvol[0] = bound( 0, pChannel->leftvol, 255 ); - pvol[1] = bound( 0, pChannel->rightvol, 255 ); - pbuf = ppaint->pbuf + outputOffset; - - if( pSource->channels == 1 ) - { - if( pSource->width == 1 ) - S_Mix8Mono( pbuf, pvol, pData, inputOffset, fracRate, outCount, timecompress ); - else S_Mix16Mono( pbuf, pvol, (short *)pData, inputOffset, fracRate, outCount ); - } - else - { - if( pSource->width == 1 ) - S_Mix8Stereo( pbuf, pvol, pData, inputOffset, fracRate, outCount ); - else S_Mix16Stereo( pbuf, pvol, (short *)pData, inputOffset, fracRate, outCount ); - } -} - -int S_MixDataToDevice( channel_t *pChannel, int sampleCount, int outRate, int outOffset, int timeCompress ) -{ - // save this to compute total output - int startingOffset = outOffset; - float inputRate = ( pChannel->pitch * pChannel->sfx->cache->rate ); - float rate = inputRate / outRate; - - // shouldn't be playing this if finished, but return if we are - if( pChannel->pMixer.finished ) - return 0; - - // If we are terminating this wave prematurely, then make sure we detect the limit - if( pChannel->pMixer.forcedEndSample ) - { - // how many total input samples will we need? - int samplesRequired = (int)(sampleCount * rate); - - // will this hit the end? - if( pChannel->pMixer.sample + samplesRequired >= pChannel->pMixer.forcedEndSample ) - { - // yes, mark finished and truncate the sample request - pChannel->pMixer.finished = true; - sampleCount = (int)((pChannel->pMixer.forcedEndSample - pChannel->pMixer.sample) / rate ); - } - } - - while( sampleCount > 0 ) - { - int availableSamples, outSampleCount; - wavdata_t *pSource = pChannel->sfx->cache; - qboolean use_loop = pChannel->use_loop; - void *pData = NULL; - double sampleFrac; - int i, j; - - // compute number of input samples required - double end = pChannel->pMixer.sample + rate * sampleCount; - int inputSampleCount = (int)(ceil( end ) - floor( pChannel->pMixer.sample )); - - availableSamples = S_GetOutputData( pSource, &pData, pChannel->pMixer.sample, inputSampleCount, use_loop ); - - // none available, bail out - if( !availableSamples ) break; - - sampleFrac = pChannel->pMixer.sample - floor( pChannel->pMixer.sample ); - - if( availableSamples < inputSampleCount ) - { - // how many samples are there given the number of input samples and the rate. - outSampleCount = (int)ceil(( availableSamples - sampleFrac ) / rate ); + if( width == 1 ) + S_MixMono8( pbuf, pvol, buf, num_samples ); + else + S_MixMono16( pbuf, pvol, buf, num_samples ); } else { - outSampleCount = sampleCount; + if( width == 1 ) + S_MixStereo8( pbuf, pvol, buf, num_samples ); + else + S_MixStereo16( pbuf, pvol, buf, num_samples ); } - - // Verify that we won't get a buffer overrun. - Assert( floor( sampleFrac + rate * ( outSampleCount - 1 )) <= availableSamples ); - - // save current paintbuffer - j = MIX_GetCurrentPaintbufferIndex(); - - for( i = 0; i < CPAINTBUFFERS; i++ ) + } + else + { + if( channels == 1 ) { - if( !paintbuffers[i].factive ) - continue; - - // mix chan into all active paintbuffers - MIX_SetCurrentPaintbuffer( i ); - - S_MixChannel( pChannel, pData, outOffset, FIX_FLOAT( sampleFrac ), FIX_FLOAT( rate ), outSampleCount, timeCompress ); + if( width == 1 ) + S_MixMonoPitch8( pbuf, pvol, buf, offset_frac, rate_scale, num_samples ); + else + S_MixMonoPitch16( pbuf, pvol, buf, offset_frac, rate_scale, num_samples ); + } + else + { + if( width == 1 ) + S_MixStereoPitch8( pbuf, pvol, buf, offset_frac, rate_scale, num_samples ); + else + S_MixStereoPitch16( pbuf, pvol, buf, offset_frac, rate_scale, num_samples ); } - - MIX_SetCurrentPaintbuffer( j ); - - pChannel->pMixer.sample += outSampleCount * rate; - outOffset += outSampleCount; - sampleCount -= outSampleCount; } - - // Did we run out of samples? if so, mark finished - if( sampleCount > 0 ) - { - pChannel->pMixer.finished = true; - } - - // total number of samples mixed !!! at the output clock rate !!! - return outOffset - startingOffset; } -static qboolean S_ShouldContinueMixing( channel_t *ch ) +static int S_AdjustNumSamples( channel_t *chan, int num_samples, double rate, double timecompress_rate ) { - if( ch->isSentence ) + if( chan->finished ) + return 0; + + // if channel is set to end at specific sample, + // detect if it's the last mixing pass and truncate + if( chan->forced_end ) { - if( ch->currentWord ) - return true; - return false; + // calculate the last sample position + double end_sample = chan->sample + rate * num_samples * timecompress_rate; + + if( end_sample >= chan->forced_end ) + { + chan->finished = true; + return floor(( chan->forced_end - chan->sample ) / ( rate * timecompress_rate )); + } } - return !ch->pMixer.finished; + return num_samples; } -// Mix all channels into active paintbuffers until paintbuffer is full or 'endtime' is reached. -// endtime: time in 44khz samples to mix -// rate: ignore samples which are not natively at this rate (for multipass mixing/filtering) -// if rate == SOUND_ALL_RATES then mix all samples this pass -// flags: if SOUND_MIX_DRY, then mix only samples with channel flagged as 'dry' -// outputRate: target mix rate for all samples. Note, if outputRate = SOUND_DMA_SPEED, then -// this routine will fill the paintbuffer to endtime. Otherwise, fewer samples are mixed. -// if( endtime - paintedtime ) is not aligned on boundaries of 4, -// we'll miss data if outputRate < SOUND_DMA_SPEED! -static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate ) +static int S_MixChannelToBuffer( portable_samplepair_t *pbuf, channel_t *chan, int num_samples, int out_rate, double pitch, int offset, int timecompress ) { - channel_t *ch; - wavdata_t *pSource; - int i, sampleCount; - qboolean bZeroVolume; - qboolean local = Host_IsLocalGame(); - - // mix each channel into paintbuffer - ch = channels; - - // validate parameters - Assert( outputRate <= SOUND_DMA_SPEED ); - - // make sure we're not discarding data - Assert( !(( endtime - paintedtime ) & 0x3 ) || ( outputRate == SOUND_DMA_SPEED )); - - // 44k: try to mix this many samples at outputRate - sampleCount = ( endtime - paintedtime ) / ( SOUND_DMA_SPEED / outputRate ); - - if( sampleCount <= 0 ) return; - - for( i = 0; i < total_channels; i++, ch++ ) + const int initial_offset = offset; + const int pvol[2] = { - if( !ch->sfx ) continue; + bound( 0, chan->leftvol, 255 ), + bound( 0, chan->rightvol, 255 ), + }; + double rate = pitch * chan->sfx->cache->rate / (double)out_rate; + + // timecompress at 100% is skipping the entire sfx, so mark as finished and exit + if( timecompress >= 100 ) + { + chan->finished = true; + return 0; + } + + double timecompress_rate = 1 / ( 1 - timecompress / 100.0 ); + + num_samples = S_AdjustNumSamples( chan, num_samples, rate, timecompress_rate ); + if( num_samples == 0 ) + return 0; + + while( num_samples > 0 ) + { + // calculate the last sample position + double end_sample = chan->sample + rate * num_samples * timecompress_rate; + + // and get total amount of samples we want + int request_num_samples = (int)(ceil( end_sample ) - floor( chan->sample )); + + // get sample pointer and also amount of samples available + const void *audio = NULL; + int available = S_RetrieveAudioSamples( chan->sfx->cache, &audio, chan->sample, request_num_samples, chan->use_loop ); + + // no samples available, exit + if( !available ) + break; + + double sample_frac = chan->sample - floor( chan->sample ); + + // this is how much data we output + int out_count = num_samples; + if( request_num_samples > available ) // but we can't write more than we have + out_count = (int)ceil(( available - sample_frac ) / ( rate )); + + const wavdata_t *wav = chan->sfx->cache; + S_MixAudio( pbuf + offset, pvol, audio, wav->channels, wav->width, sample_frac, rate, out_count ); + + chan->sample += out_count * rate * timecompress_rate; + offset += out_count; + num_samples -= out_count; + } + + // samples couldn't be retrieved, mark as finished + if( num_samples > 0 ) + chan->finished = true; + + // total amount of samples mixed + return offset - initial_offset; +} + +static int VOX_MixChannelToBuffer( portable_samplepair_t *pbuf, channel_t *chan, int num_samples, int out_rate, double pitch ) +{ + int offset = 0; + + if( chan->sentence_finished ) + return 0; + + while( num_samples > 0 && !chan->sentence_finished ) + { + int outputCount = S_MixChannelToBuffer( pbuf, chan, num_samples, out_rate, pitch, offset, chan->words[chan->word_index].timecompress ); + + offset += outputCount; + num_samples -= outputCount; + + // if we finished load a next word + if( chan->finished ) + { + VOX_FreeWord( chan ); + chan->word_index++; + VOX_LoadWord( chan ); + + if( !chan->sentence_finished ) + chan->sfx = chan->words[chan->word_index].sfx; + } + } + + return offset; +} + +static int S_MixNormalChannels( portable_samplepair_t *dst, int end, int rate ) +{ + const qboolean local = Host_IsLocalGame(); + const qboolean ingame = CL_IsInGame(); + const int num_samples = ( end - paintedtime ) / ( SOUND_DMA_SPEED / rate ); + + // FWGS feature: make everybody sound like chipmunks when we're going fast + const float pitch_mult = ( sys_timescale.value + 1 ) / 2; + + int num_mixed_channels = 0; + + if( num_samples <= 0 ) + return num_mixed_channels; + + if( cl.background && cls.key_dest == key_console ) + return num_mixed_channels; // no sounds in console with background map + + for( int i = 0; i < total_channels; i++ ) + { + channel_t *ch = &channels[i]; + + if( !ch->sfx ) + continue; - // NOTE: background map is allow both type sounds: menu and game if( !cl.background ) { if( cls.key_dest == key_console && ch->localsound ) { // play, playvol } - else if(( s_listener.inmenu || s_listener.paused ) && !ch->localsound && local ) + else if(( cls.key_dest == key_menu || cl.paused ) && !ch->localsound && local ) { // play only local sounds, keep pause for other continue; } - else if( !s_listener.inmenu && !s_listener.active && !ch->staticsound ) + else if( cls.key_dest != key_menu && !ingame && !ch->staticsound ) { // play only ambient sounds, keep pause for other continue; } } - else if( cls.key_dest == key_console ) - continue; // silent mode in console - pSource = S_LoadSound( ch->sfx ); + wavdata_t *sc = S_LoadSound( ch->sfx ); - // Don't mix sound data for sounds with zero volume. If it's a non-looping sound, - // just remove the sound when its volume goes to zero. - - bZeroVolume = !ch->leftvol && !ch->rightvol; - - if( !bZeroVolume ) - { - // this values matched with GoldSrc - if( ch->leftvol < 8 && ch->rightvol < 8 ) - bZeroVolume = true; - } - - if( !pSource || ( bZeroVolume && !FBitSet( pSource->flags, SOUND_LOOPED ))) - { - if( !pSource ) - { - S_FreeChannel( ch ); - continue; - } - } - else if( bZeroVolume ) + if( !sc ) { + S_FreeChannel( ch ); continue; } - // multipass mixing - only mix samples of specified sample rate - switch( rate ) + // if the sound is unaudible, skip it + // if it's also not looping, free it + if( ch->leftvol < 8 && ch->rightvol < 8 ) { - case SOUND_11k: - case SOUND_22k: - case SOUND_44k: - if( rate != pSource->rate ) - continue; - break; - default: break; + if( !FBitSet( sc->flags, SOUND_LOOPED ) || !ch->use_loop ) + S_FreeChannel( ch ); + + continue; } - // get playback pitch - if( ch->isSentence ) - ch->pitch = VOX_ModifyPitch( ch, ch->basePitch * 0.01f ); - else ch->pitch = ch->basePitch * 0.01f; + if( rate != sc->rate ) + continue; - ch->pitch *= ( sys_timescale.value + 1 ) / 2; - - if( CL_GetEntityByIndex( ch->entnum ) && ( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_STREAM )) + if( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_STREAM ) { - if( pSource->width == 1 ) - SND_MoveMouth8( ch, pSource, sampleCount ); - else SND_MoveMouth16( ch, pSource, sampleCount ); + cl_entity_t *ent = CL_GetEntityByIndex( ch->entnum ); + + if( ent != NULL ) + { + if( sc->width == 1 ) + SND_MoveMouth8( &ent->mouth, ch->sample, sc, num_samples, ch->use_loop ); + else + SND_MoveMouth16( &ent->mouth, ch->sample, sc, num_samples, ch->use_loop ); + } } - // mix channel to all active paintbuffers. - // NOTE: must be called once per channel only - consecutive calls retrieve additional data. - if( ch->isSentence ) - VOX_MixDataToDevice( ch, sampleCount, outputRate, 0 ); - else S_MixDataToDevice( ch, sampleCount, outputRate, 0, 0 ); + double pitch = VOX_ModifyPitch( ch, ch->basePitch * 0.01 ) * pitch_mult; - if( !S_ShouldContinueMixing( ch )) + num_mixed_channels++; + + if( ch->is_sentence ) { - S_FreeChannel( ch ); + VOX_MixChannelToBuffer( dst, ch, num_samples, rate, pitch ); + + if( ch->sentence_finished ) + S_FreeChannel( ch ); + } + else + { + S_MixChannelToBuffer( dst, ch, num_samples, rate, pitch, 0, 0 ); + + if( ch->finished ) + S_FreeChannel( ch ); } } + + return num_mixed_channels; } -static void S_MixBufferUpsample2x( int count, portable_samplepair_t *pbuffer ) +static int S_AverageSample( int a, int b ) { - int upCount = count<<1; - int i, j; + return ( a >> 1 ) + ( b >> 1 ) + ((( a & 1 ) + ( b & 1 )) >> 1 ); +} - // reverse through buffer, duplicating contents for 'count' samples - for( i = upCount - 1, j = count - 1; j >= 0; i-=2, j-- ) +static void S_UpsampleBuffer( portable_samplepair_t *dst, size_t num_samples ) +{ + if( s_lerping.value ) { - pbuffer[i] = pbuffer[j]; - pbuffer[i-1] = pbuffer[j]; + // copy even positions and average odd + for( size_t i = num_samples - 1; i > 0; i-- ) + { + dst[i * 2] = dst[i]; + + dst[i * 2 + 1].left = S_AverageSample( dst[i].left, dst[i - 1].left ); + dst[i * 2 + 1].right = S_AverageSample( dst[i].right, dst[i - 1].right ); + } } -} - -// zero out all paintbuffers -void MIX_ClearAllPaintBuffers( int SampleCount, qboolean clearFilters ) -{ - int count = Q_min( SampleCount, PAINTBUFFER_SIZE ); - int i; - - // zero out all paintbuffer data (ignore sampleCount) - for( i = 0; i < CPAINTBUFFERS; i++ ) + else { - if( paintbuffers[i].pbuf != NULL ) - memset( paintbuffers[i].pbuf, 0, (count+1) * sizeof( portable_samplepair_t )); - } -} - -// mixes pbuf1 + pbuf2 into pbuf3, count samples -// fgain is output gain 0-1.0 -// NOTE: pbuf3 may equal pbuf1 or pbuf2! -static void MIX_MixPaintbuffers( int ibuf1, int ibuf2, int ibuf3, int count, float fgain ) -{ - portable_samplepair_t *pbuf1, *pbuf2, *pbuf3; - int i, gain; - - gain = 256 * fgain; - - Assert( count <= PAINTBUFFER_SIZE ); - Assert( ibuf1 < CPAINTBUFFERS ); - Assert( ibuf2 < CPAINTBUFFERS ); - Assert( ibuf3 < CPAINTBUFFERS ); - - pbuf1 = paintbuffers[ibuf1].pbuf; - pbuf2 = paintbuffers[ibuf2].pbuf; - pbuf3 = paintbuffers[ibuf3].pbuf; - - if( !gain ) - { - // do not mix buf2 into buf3, just copy - if( pbuf1 != pbuf3 ) - memcpy( pbuf3, pbuf1, sizeof( *pbuf1 ) * count ); - return; + // copy into even and odd positions + for( size_t i = num_samples - 1; i > 0; i-- ) + { + dst[i * 2] = dst[i]; + dst[i * 2 + 1] = dst[i]; + } } - // destination buffer stereo - average n chans down to stereo + dst[1] = dst[0]; +} - // destination 2ch: - // pb1 2ch + pb2 2ch -> pb3 2ch - // pb1 2ch + pb2 (4ch->2ch) -> pb3 2ch - // pb1 (4ch->2ch) + pb2 (4ch->2ch) -> pb3 2ch +static int S_MixNormalChannelsToRoombuffer( int end, int count ) +{ + // for room buffer we only support CD rates like 11k, 22k, and 44k + // TODO: 48k output would require support from platform-specific backends first + // until there is no real usecase, let's keep it simple + int num_mixed_channels = S_MixNormalChannels( roombuffer, end, SOUND_11k ); - // mix front channels - for( i = 0; i < count; i++ ) + if( dma.format.speed >= SOUND_22k ) { - pbuf3[i].left = pbuf1[i].left; - pbuf3[i].right = pbuf1[i].right; - pbuf3[i].left += (pbuf2[i].left * gain) >> 8; - pbuf3[i].right += (pbuf2[i].right * gain) >> 8; + if( num_mixed_channels > 0 ) + S_UpsampleBuffer( roombuffer, count / ( SOUND_22k / SOUND_11k )); + + num_mixed_channels += S_MixNormalChannels( roombuffer, end, SOUND_22k ); } -} -static void MIX_CompressPaintbuffer( int ipaint, int count ) -{ - portable_samplepair_t *pbuf; - paintbuffer_t *ppaint; - int i; - - ppaint = MIX_GetPPaintFromIPaint( ipaint ); - pbuf = ppaint->pbuf; - - for( i = 0; i < count; i++, pbuf++ ) + if( dma.format.speed >= SOUND_44k ) { - pbuf->left = CLIP( pbuf->left ); - pbuf->right = CLIP( pbuf->right ); + if( num_mixed_channels > 0 ) + S_UpsampleBuffer( roombuffer, count / ( SOUND_44k / SOUND_22k )); + + num_mixed_channels += S_MixNormalChannels( roombuffer, end, SOUND_44k ); } + + return num_mixed_channels; } -static void S_MixUpsample( int sampleCount, int filtertype ) +static int S_MixRawChannels( int end ) { - paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr(); + int num_room_channels = 0; - (void)filtertype; - - S_MixBufferUpsample2x( sampleCount, ppaint->pbuf ); -} - -static void MIX_MixRawSamplesBuffer( int end ) -{ - portable_samplepair_t *pbuf, *roombuf, *streambuf, *voicebuf; - uint i, j, stop; - - roombuf = MIX_GetPFrontFromIPaint( IROOMBUFFER ); - streambuf = MIX_GetPFrontFromIPaint( ISTREAMBUFFER ); - voicebuf = MIX_GetPFrontFromIPaint( IVOICEBUFFER ); - - if( s_listener.paused ) return; + if( cl.paused ) + return 0; // paint in the raw channels - for( i = 0; i < MAX_RAW_CHANNELS; i++ ) + for( size_t i = 0; i < ARRAYSIZE( raw_channels ); i++ ) { // copy from the streaming sound source rawchan_t *ch = raw_channels[i]; - qboolean stream; - qboolean is_voice; if( !ch ) continue; @@ -762,128 +407,144 @@ static void MIX_MixRawSamplesBuffer( int end ) if( !ch->leftvol && !ch->rightvol ) continue; - is_voice = (ch->entnum > 0 && ch->entnum <= MAX_CLIENTS) || - (ch->entnum == VOICE_LOOPBACK_INDEX) || - (ch->entnum == VOICE_LOCALCLIENT_INDEX); + qboolean is_voice = CL_IsPlayerIndex( ch->entnum ) + || ch->entnum == VOICE_LOOPBACK_INDEX + || ch->entnum == VOICE_LOCALCLIENT_INDEX; - stream = ch->entnum == S_RAW_SOUND_BACKGROUNDTRACK || CL_IsPlayerIndex( ch->entnum ); - - if( is_voice ) - pbuf = voicebuf; - else if( stream ) - pbuf = streambuf; - else - pbuf = roombuf; - - stop = (end < ch->s_rawend) ? end : ch->s_rawend; - - for( j = paintedtime; j < stop; j++ ) + portable_samplepair_t *pbuf; + if( is_voice || ch->entnum == S_RAW_SOUND_BACKGROUNDTRACK ) { - pbuf[j-paintedtime].left += ( ch->rawsamples[j & ( ch->max_samples - 1 )].left * ch->leftvol ) >> 8; - pbuf[j-paintedtime].right += ( ch->rawsamples[j & ( ch->max_samples - 1 )].right * ch->rightvol ) >> 8; + // for streams we don't have fancy things like volume controls + // or DSP processing or upsampling, so paint it directly into result buffer + pbuf = paintbuffer; + } + else + { + pbuf = roombuffer; + num_room_channels++; + } + + uint stop = (end < ch->s_rawend) ? end : ch->s_rawend; + const uint mask = ch->max_samples - 1; + + for( size_t i = 0, j = paintedtime; j < stop; i++, j++ ) + { + pbuf[i].left += ( ch->rawsamples[j & mask].left * ch->leftvol ) >> 8; + pbuf[i].right += ( ch->rawsamples[j & mask].right * ch->rightvol ) >> 8; } if( ch->entnum > 0 ) { + cl_entity_t *ent = CL_GetEntityByIndex( ch->entnum ); int pos = paintedtime & ( ch->max_samples - 1 ); + int count = bound( 0, ch->max_samples - pos, stop - paintedtime ); - SND_MoveMouthRaw( ch, &ch->rawsamples[pos], bound( 0, ch->max_samples - pos, stop - paintedtime )); + if( ent ) + SND_MoveMouthRaw( &ent->mouth, &ch->rawsamples[pos], count ); + } + } + + return num_room_channels; +} + +static void S_MixBufferWithGain( portable_samplepair_t *dst, const portable_samplepair_t *src, size_t count, int gain ) +{ + if( gain == 256 ) + { + for( size_t i = 0; i < count; i++ ) + { + dst[i].left += src[i].left; + dst[i].right += src[i].right; + } + } + else + { + for( size_t i = 0; i < count; i++ ) + { + dst[i].left += ( src[i].left * gain ) >> 8; + dst[i].right += ( src[i].right * gain ) >> 8; } } } -// upsample and mix sounds into final 44khz versions of: -// IROOMBUFFER, IFACINGBUFFER, IFACINGAWAY -// dsp fx are then applied to these buffers by the caller. -// caller also remixes all into final IPAINTBUFFER output. -static void MIX_UpsampleAllPaintbuffers( int end, int count ) +static void S_WriteLinearBlastStereo16( short *snd_out, const int *snd_p, size_t count ) { - // 11khz sounds are mixed into 3 buffers based on distance from listener, and facing direction - // These buffers are facing, facingaway, room - // These 3 mixed buffers are then each upsampled to 22khz. - - // 22khz sounds are mixed into the 3 buffers based on distance from listener, and facing direction - // These 3 mixed buffers are then each upsampled to 44khz. - - // 44khz sounds are mixed into the 3 buffers based on distance from listener, and facing direction - - MIX_DeactivateAllPaintbuffers(); - - // only mix to roombuffer if dsp fx are on KDB: perf - MIX_ActivatePaintbuffer( IROOMBUFFER ); // operates on MIX_MixChannelsToPaintbuffer - - // mix 11khz sounds: - MIX_MixChannelsToPaintbuffer( end, SOUND_11k, SOUND_11k ); - -#if SOUND_DMA_SPEED >= SOUND_22k - // upsample all 11khz buffers by 2x - // only upsample roombuffer if dsp fx are on KDB: perf - MIX_SetCurrentPaintbuffer( IROOMBUFFER ); // operates on MixUpSample - S_MixUpsample( count / ( SOUND_DMA_SPEED / SOUND_11k ), s_lerping.value ); - - // mix 22khz sounds: - MIX_MixChannelsToPaintbuffer( end, SOUND_22k, SOUND_22k ); -#endif - -#if SOUND_DMA_SPEED >= SOUND_44k - // upsample all 22khz buffers by 2x - // only upsample roombuffer if dsp fx are on KDB: perf - MIX_SetCurrentPaintbuffer( IROOMBUFFER ); - S_MixUpsample( count / ( SOUND_DMA_SPEED / SOUND_22k ), s_lerping.value ); - - // mix all 44khz sounds to all active paintbuffers - MIX_MixChannelsToPaintbuffer( end, SOUND_44k, SOUND_DMA_SPEED ); -#endif - - // mix raw samples from the video streams - MIX_MixRawSamplesBuffer( end ); - - MIX_DeactivateAllPaintbuffers(); - MIX_SetCurrentPaintbuffer( IPAINTBUFFER ); + for( size_t i = 0; i < count; i += 2 ) + { + snd_out[i+0] = CLIP16( snd_p[i+0] ); + snd_out[i+1] = CLIP16( snd_p[i+1] ); + } } -void MIX_PaintChannels( int endtime ) +static void S_TransferPaintBuffer( const portable_samplepair_t *src, int endtime ) { - int end, count; + const int *snd_p = (const int *)src; + const int sampleMask = ((dma.samples >> 1) - 1); + int lpaintedtime = paintedtime; - CheckNewDspPresets(); + SNDDMA_BeginPainting (); + + while( lpaintedtime < endtime ) + { + // handle recirculating buffer issues + int lpos = lpaintedtime & sampleMask; + + short *snd_out = (short *)dma.buffer + (lpos << 1); + + int snd_linear_count = (dma.samples>>1) - lpos; + if( lpaintedtime + snd_linear_count > endtime ) + snd_linear_count = endtime - lpaintedtime; + + snd_linear_count <<= 1; + + // write a linear blast of samples + S_WriteLinearBlastStereo16( snd_out, snd_p, snd_linear_count ); + + snd_p += snd_linear_count; + lpaintedtime += (snd_linear_count >> 1); + } + + SNDDMA_Submit(); +} + +void S_ClearBuffers( int num_samples ) +{ + const size_t num_bytes = ( num_samples + 1 ) * sizeof( portable_samplepair_t ); + + memset( roombuffer, 0, num_bytes ); + memset( paintbuffer, 0, num_bytes ); +} + +void S_PaintChannels( int endtime ) +{ + int gain = S_GetMasterVolume() * 256; while( paintedtime < endtime ) { // if paintbuffer is smaller than DMA buffer - end = endtime; - if( endtime - paintedtime > PAINTBUFFER_SIZE ) + int end = endtime; + if( end - paintedtime > PAINTBUFFER_SIZE ) end = paintedtime + PAINTBUFFER_SIZE; - // number of 44khz samples to mix into paintbuffer, up to paintbuffer size - count = end - paintedtime; + const int num_samples = end - paintedtime; - // clear the all mix buffers - MIX_ClearAllPaintBuffers( count, false ); + S_ClearBuffers( num_samples ); - MIX_UpsampleAllPaintbuffers( end, count ); + int room_channels = S_MixNormalChannelsToRoombuffer( end, num_samples ); - // process all sounds with DSP - if( cls.key_dest != key_menu ) - DSP_Process( MIX_GetPFrontFromIPaint( IROOMBUFFER ), count ); + room_channels += S_MixRawChannels( end ); - // add music or soundtrack from movie (no dsp) - MIX_MixPaintbuffers( IPAINTBUFFER, IROOMBUFFER, IPAINTBUFFER, count, S_GetMasterVolume() ); + // now process DSP and mix result into paintbuffer + if( room_channels > 0 ) + { + if( cls.key_dest != key_menu ) + SX_RoomFX( roombuffer, num_samples ); - // add music or soundtrack from movie (no dsp) - MIX_MixPaintbuffers( IPAINTBUFFER, ISTREAMBUFFER, IPAINTBUFFER, count, 1.0f ); - - // voice chat - MIX_MixPaintbuffers( IPAINTBUFFER, IVOICEBUFFER, IPAINTBUFFER, count, 1.0f ); - - // clip all values > 16 bit down to 16 bit - MIX_CompressPaintbuffer( IPAINTBUFFER, count ); - - // transfer IPAINTBUFFER paintbuffer out to DMA buffer - MIX_SetCurrentPaintbuffer( IPAINTBUFFER ); + S_MixBufferWithGain( paintbuffer, roombuffer, num_samples, gain ); + } // transfer out according to DMA format - S_TransferPaintBuffer( end ); + S_TransferPaintBuffer( paintbuffer, end ); paintedtime = end; } } diff --git a/engine/client/s_mouth.c b/engine/client/s_mouth.c index 716fe612..f40af0ac 100644 --- a/engine/client/s_mouth.c +++ b/engine/client/s_mouth.c @@ -20,48 +20,31 @@ GNU General Public License for more details. #define CAVGSAMPLES 10 -void SND_InitMouth( int entnum, int entchannel ) +static void SND_CommitMouthValue( mouth_t *mouth, int savg, int scount ) { - if(( entchannel == CHAN_VOICE || entchannel == CHAN_STREAM ) && entnum > 0 ) + mouth->sndavg += savg; + mouth->sndcount = (byte)scount; + + if( mouth->sndcount >= CAVGSAMPLES ) { - SND_ForceInitMouth( entnum ); + mouth->mouthopen = mouth->sndavg / CAVGSAMPLES; + mouth->sndavg = 0; + mouth->sndcount = 0; } } -void SND_CloseMouth( channel_t *ch ) +void SND_MoveMouth8( mouth_t *mouth, int pos, const wavdata_t *sc, int count, qboolean use_loop ) { - if( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_STREAM ) - { - SND_ForceCloseMouth( ch->entnum ); - } -} - -void SND_MoveMouth8( channel_t *ch, wavdata_t *pSource, int count ) -{ - cl_entity_t *clientEntity; - signed char *pdata = NULL; - mouth_t *pMouth = NULL; - int scount, pos = 0; + const int8_t *pdata = NULL; + int scount; int savg, data; uint i; - clientEntity = CL_GetEntityByIndex( ch->entnum ); - if( !clientEntity ) return; - - pMouth = &clientEntity->mouth; - - if( ch->isSentence ) - { - if( ch->currentWord ) - pos = ch->currentWord->sample; - } - else pos = ch->pMixer.sample; - - count = S_GetOutputData( pSource, (void**)&pdata, pos, count, ch->use_loop ); + count = S_RetrieveAudioSamples( sc, (const void **)&pdata, pos, count, use_loop ); if( pdata == NULL ) return; i = 0; - scount = pMouth->sndcount; + scount = mouth->sndcount; savg = 0; while( i < count && scount < CAVGSAMPLES ) @@ -73,43 +56,21 @@ void SND_MoveMouth8( channel_t *ch, wavdata_t *pSource, int count ) scount++; } - pMouth->sndavg += savg; - pMouth->sndcount = (byte)scount; - - if( pMouth->sndcount >= CAVGSAMPLES ) - { - pMouth->mouthopen = pMouth->sndavg / CAVGSAMPLES; - pMouth->sndavg = 0; - pMouth->sndcount = 0; - } + SND_CommitMouthValue( mouth, savg, scount ); } -void SND_MoveMouth16( channel_t *ch, wavdata_t *pSource, int count ) +void SND_MoveMouth16( mouth_t *mouth, int pos, const wavdata_t *sc, int count, qboolean use_loop ) { - cl_entity_t *clientEntity; - short *pdata = NULL; - mouth_t *pMouth = NULL; + const int16_t *pdata = NULL; int savg, data; - int scount, pos = 0; + int scount; uint i; - clientEntity = CL_GetEntityByIndex( ch->entnum ); - if( !clientEntity ) return; - - pMouth = &clientEntity->mouth; - - if( ch->isSentence ) - { - if( ch->currentWord ) - pos = ch->currentWord->sample; - } - else pos = ch->pMixer.sample; - - count = S_GetOutputData( pSource, (void**)&pdata, pos, count, ch->use_loop ); + count = S_RetrieveAudioSamples( sc, (const void **)&pdata, pos, count, use_loop ); if( pdata == NULL ) return; i = 0; - scount = pMouth->sndcount; + scount = mouth->sndcount; savg = 0; while( i < count && scount < CAVGSAMPLES ) @@ -122,15 +83,7 @@ void SND_MoveMouth16( channel_t *ch, wavdata_t *pSource, int count ) scount++; } - pMouth->sndavg += savg; - pMouth->sndcount = (byte)scount; - - if( pMouth->sndcount >= CAVGSAMPLES ) - { - pMouth->mouthopen = pMouth->sndavg / CAVGSAMPLES; - pMouth->sndavg = 0; - pMouth->sndcount = 0; - } + SND_CommitMouthValue( mouth, savg, scount ); } void SND_ForceInitMouth( int entnum ) @@ -157,29 +110,19 @@ void SND_ForceCloseMouth( int entnum ) clientEntity->mouth.mouthopen = 0; } -void SND_MoveMouthRaw( rawchan_t *ch, portable_samplepair_t *pData, int count ) +void SND_MoveMouthRaw( mouth_t *mouth, const portable_samplepair_t *buf, int count ) { - cl_entity_t *clientEntity; - mouth_t *pMouth = NULL; int savg, data; int scount = 0; uint i; - clientEntity = CL_GetEntityByIndex( ch->entnum ); - if( !clientEntity ) return; - - pMouth = &clientEntity->mouth; - - if( pData == NULL ) - return; - i = 0; - scount = pMouth->sndcount; + scount = mouth->sndcount; savg = 0; while ( i < count && scount < CAVGSAMPLES ) { - data = pData[i].left; // mono sound anyway + data = buf[i].left; // mono sound anyway data = ( bound( -32767, data, 0x7ffe ) >> 8 ); savg += abs( data ); @@ -187,13 +130,5 @@ void SND_MoveMouthRaw( rawchan_t *ch, portable_samplepair_t *pData, int count ) scount++; } - pMouth->sndavg += savg; - pMouth->sndcount = (byte)scount; - - if ( pMouth->sndcount >= CAVGSAMPLES ) - { - pMouth->mouthopen = pMouth->sndavg / CAVGSAMPLES; - pMouth->sndavg = 0; - pMouth->sndcount = 0; - } + SND_CommitMouthValue( mouth, savg, scount ); } diff --git a/engine/client/s_stream.c b/engine/client/s_stream.c index 0b6fcad8..b691bf4c 100644 --- a/engine/client/s_stream.c +++ b/engine/client/s_stream.c @@ -18,8 +18,18 @@ GNU General Public License for more details. #include "client.h" #include "soundlib.h" -static bg_track_t s_bgTrack; -static musicfade_t musicfade; // controlled by game dlls +static struct +{ + string current; // a currently playing track + string loopName; // may be empty + stream_t *stream; + int source; // may be game, menu, etc +} s_bgTrack; + +static struct +{ + float percent; +} musicfade; // controlled by game dlls /* ================= @@ -41,12 +51,12 @@ void S_PrintBackgroundTrackState( void ) /* ================= -S_FadeMusicVolume +S_MusicFade ================= */ -void S_FadeMusicVolume( float fadePercent ) +void S_MusicFade( float fade_percent ) { - musicfade.percent = bound( 0.0f, fadePercent, 100.0f ); + musicfade.percent = bound( 0.0f, fade_percent, 100.0f ); } /* @@ -64,7 +74,7 @@ float S_GetMusicVolume( void ) return 0.0f; } - if( !s_listener.inmenu && musicfade.percent != 0 ) + if( cls.key_dest != key_menu && musicfade.percent != 0 ) { scale = bound( 0.0f, musicfade.percent / 100.0f, 1.0f ); scale = 1.0f - scale; @@ -128,7 +138,7 @@ void S_StopBackgroundTrack( void ) if( !s_bgTrack.stream ) return; FS_FreeStream( s_bgTrack.stream ); - memset( &s_bgTrack, 0, sizeof( bg_track_t )); + memset( &s_bgTrack, 0, sizeof( s_bgTrack )); memset( &musicfade, 0, sizeof( musicfade )); } @@ -191,7 +201,7 @@ void S_StreamBackgroundTrack( void ) return; // don't bother playing anything if musicvolume is 0 - if( !s_musicvolume.value || s_listener.paused || s_listener.stream_paused ) + if( !s_musicvolume.value || cl.paused || s_listener.stream_paused ) return; if( !cl.background ) diff --git a/engine/client/s_utils.c b/engine/client/s_utils.c index 0f2a8d3e..3efcd749 100644 --- a/engine/client/s_utils.c +++ b/engine/client/s_utils.c @@ -16,68 +16,46 @@ GNU General Public License for more details. #include "common.h" #include "sound.h" -//----------------------------------------------------------------------------- -// Purpose: wrap the position wrt looping -// Input : samplePosition - absolute position -// Output : int - looped position -//----------------------------------------------------------------------------- -int S_ConvertLoopedPosition( wavdata_t *pSource, int samplePosition, qboolean use_loop ) +int S_AdjustLoopedSamplePosition( const wavdata_t *source, int current_sample, qboolean enable_looping ) { - // if the wave is looping and we're past the end of the sample - // convert to a position within the loop - // At the end of the loop, we return a short buffer, and subsequent call - // will loop back and get the rest of the buffer - if( FBitSet( pSource->flags, SOUND_LOOPED ) && samplePosition >= pSource->samples && use_loop ) + // check if looping is enabled and we've exceeeded the sample boundary + if( enable_looping && FBitSet( source->flags, SOUND_LOOPED ) && current_sample >= source->samples ) { - // size of loop - int loopSize = pSource->samples - pSource->loopStart; + // adjust position relative to loop start + current_sample -= source->loop_start; - // subtract off starting bit of the wave - samplePosition -= pSource->loopStart; - - if( loopSize ) - { - // "real" position in memory (mod off extra loops) - samplePosition = pSource->loopStart + ( samplePosition % loopSize ); - } - // ERROR? if no loopSize + // apply modulo to wrap within loop bounds + int loop_range = source->samples - source->loop_start; + if( loop_range > 0 ) + current_sample = source->loop_start + ( current_sample % loop_range ); } - return samplePosition; + return current_sample; } -int S_GetOutputData( wavdata_t *pSource, void **pData, int samplePosition, int sampleCount, qboolean use_loop ) +int S_RetrieveAudioSamples( const wavdata_t *source, const void **output_buffer, int start_position, int num_samples, qboolean enable_looping ) { - int totalSampleCount; - int sampleSize; + // handle looping + start_position = S_AdjustLoopedSamplePosition( source, start_position, enable_looping ); - // handle position looping - samplePosition = S_ConvertLoopedPosition( pSource, samplePosition, use_loop ); + // calculate how many samples are available from current position + int available_samples = Q_max( 0, source->samples - start_position ); - // how many samples are available (linearly not counting looping) - totalSampleCount = pSource->samples - samplePosition; + // limit requested samples to available samples + if( num_samples > available_samples ) + num_samples = available_samples; - // may be asking for a sample out of range, clip at zero - if( totalSampleCount < 0 ) totalSampleCount = 0; + // if none, exit early + if( num_samples <= 0 ) + return 0; - // clip max output samples to max available - if( sampleCount > totalSampleCount ) - sampleCount = totalSampleCount; + // calculate the size of each sample frame + int frame_size = Q_max( 1, source->width * source->channels ); - sampleSize = pSource->width * pSource->channels; + // convert sample position to byte offset + start_position *= frame_size; - // this can never be zero -- other functions divide by this. - // This should never happen, but avoid crashing - if( sampleSize <= 0 ) sampleSize = 1; + *output_buffer = source->buffer + start_position; - // byte offset in sample database - samplePosition *= sampleSize; - - // if we are returning some samples, store the pointer - if( sampleCount ) - { - *pData = pSource->buffer + samplePosition; - } - - return sampleCount; + return num_samples; } diff --git a/engine/client/s_vox.c b/engine/client/s_vox.c index 76c649bd..c94a3249 100644 --- a/engine/client/s_vox.c +++ b/engine/client/s_vox.c @@ -126,7 +126,7 @@ static int S_TrimEnd( const wavdata_t *wav, int end ) static void S_TrimStartEndTimes( channel_t *ch, wavdata_t *wav, int start, int end ) { - ch->pMixer.sample = start = S_TrimStart( wav, start ); + ch->sample = start = S_TrimStart( wav, start ); // don't overrun the buffer while trimming end if( end == 0 ) @@ -135,76 +135,51 @@ static void S_TrimStartEndTimes( channel_t *ch, wavdata_t *wav, int start, int e if( end < start ) end = start; - ch->pMixer.forcedEndSample = S_TrimEnd( wav, end ); -} - -// return number of samples mixed -int VOX_MixDataToDevice( channel_t *pchan, int sampleCount, int outputRate, int outputOffset ) -{ - // save this to compute total output - int startingOffset = outputOffset; - - if( !pchan->currentWord ) - return 0; - - while( sampleCount > 0 && pchan->currentWord ) - { - int timeCompress = pchan->words[pchan->wordIndex].timecompress; - int outputCount = S_MixDataToDevice( pchan, sampleCount, outputRate, outputOffset, timeCompress ); - - outputOffset += outputCount; - sampleCount -= outputCount; - - // if we finished load a next word - if( pchan->currentWord->finished ) - { - VOX_FreeWord( pchan ); - pchan->wordIndex++; - VOX_LoadWord( pchan ); - - if( pchan->currentWord ) - { - pchan->sfx = pchan->words[pchan->wordIndex].sfx; - } - } - } - return outputOffset - startingOffset; + ch->forced_end = S_TrimEnd( wav, end ); } void VOX_LoadWord( channel_t *ch ) { - const voxword_t *word = &ch->words[ch->wordIndex]; - wavdata_t *data; - int start, end, samples; + ch->sentence_finished = true; + + if( ch->word_index < 0 || ch->word_index >= CVOXWORDMAX ) + return; + + const voxword_t *word = &ch->words[ch->word_index]; if( !word->sfx ) return; - data = S_LoadSound( word->sfx ); + wavdata_t *data = S_LoadSound( word->sfx ); if( !data ) return; - ch->currentWord = &ch->pMixer; - ch->currentWord->pData = data; + ch->sentence_finished = false; + ch->data = data; - samples = data->samples; - start = word->start; - end = word->end; + int start = word->start; + int end = word->end; - if( end <= start ) end = 0; + if( end <= start ) + end = 0; - S_TrimStartEndTimes( ch, data, start * 0.01f * samples, end * 0.01f * samples ); + S_TrimStartEndTimes( ch, data, round( start * 0.01f * data->samples ), round( end * 0.01f * data->samples )); } void VOX_FreeWord( channel_t *ch ) { - voxword_t *word = &ch->words[ch->wordIndex]; + // TODO: don't set random fields to zero lol, was memset before + ch->sample = ch->forced_end = 0.0; + ch->finished = false; + ch->data = NULL; - ch->currentWord = NULL; - memset( &ch->pMixer, 0, sizeof( ch->pMixer )); + if( ch->word_index < 0 || ch->word_index >= CVOXWORDMAX ) + return; - if( !word->sfx || word->fKeepCached ) + voxword_t *word = &ch->words[ch->word_index]; + + if( !word->sfx || word->in_cache ) return; FS_FreeSound( word->sfx->cache ); @@ -215,10 +190,11 @@ void VOX_FreeWord( channel_t *ch ) void VOX_SetChanVol( channel_t *ch ) { voxword_t *word; - if( !ch->currentWord ) + + if( !ch->is_sentence || ch->sentence_finished ) return; - word = &ch->words[ch->wordIndex]; + word = &ch->words[ch->word_index]; if( word->volume == 100 ) return; @@ -230,12 +206,13 @@ void VOX_SetChanVol( channel_t *ch ) float VOX_ModifyPitch( channel_t *ch, float pitch ) { voxword_t *word; - if( !ch->currentWord ) + + if( !ch->is_sentence || ch->sentence_finished ) return pitch; - word = &ch->words[ch->wordIndex]; + word = &ch->words[ch->word_index]; - if( word->pitch < 0 ) + if( word->pitch == PITCH_NORM ) return pitch; pitch += ( word->pitch - PITCH_NORM ) * 0.01f; @@ -374,21 +351,18 @@ static int VOX_ParseString( char *psz, char *rgpparseword[CVOXWORDMAX] ) return i; } -static qboolean VOX_ParseWordParams( char *psz, voxword_t *pvoxword, qboolean fFirst ) +static qboolean VOX_ParseWordParams( char *psz, voxword_t *pvoxword ) { int len, i; char sznum[8], *pszsave = psz; - static voxword_t voxwordDefault; - - if( fFirst ) - { - voxwordDefault.fKeepCached = 0; - voxwordDefault.pitch = -1; - voxwordDefault.volume = 100; - voxwordDefault.start = 0; - voxwordDefault.end = 100; - voxwordDefault.timecompress = 0; - } + voxword_t voxwordDefault = { + .volume = 100, + .pitch = 100, + .start = 0, + .end = 100, + .timecompress = 0, + .in_cache = false, + }; *pvoxword = voxwordDefault; @@ -446,6 +420,11 @@ static qboolean VOX_ParseWordParams( char *psz, voxword_t *pvoxword, qboolean fF } } + // validate some of the parameters + pvoxword->start = bound( 0, pvoxword->start, 100 ); + pvoxword->end = bound( 0, pvoxword->end, 100 ); + pvoxword->timecompress = bound( 0, pvoxword->timecompress, 100 ); + // no actual word but new defaults if( Q_strlen( pszsave ) == 0 ) { @@ -462,6 +441,7 @@ void VOX_LoadSound( channel_t *ch, const char *pszin ) char *rgpparseword[CVOXWORDMAX] = { 0 }; const char *psz; int i, j; + int num_words; if( !pszin ) return; @@ -490,13 +470,13 @@ void VOX_LoadSound( channel_t *ch, const char *pszin ) } Q_strncpy( buffer, psz, sizeof( buffer )); - VOX_ParseString( buffer, rgpparseword ); + num_words = VOX_ParseString( buffer, rgpparseword ); - for( i = 0, j = 0; i < CVOXWORDMAX && rgpparseword[i]; i++ ) + for( i = 0, j = 0; i < num_words; i++ ) { char pathbuffer[MAX_SYSPATH]; - if( !VOX_ParseWordParams( rgpparseword[i], &ch->words[j], i == 0 )) + if( !VOX_ParseWordParams( rgpparseword[i], &ch->words[j] )) continue; if( Q_snprintf( pathbuffer, sizeof( pathbuffer ), "%s%s", szpath, rgpparseword[i] ) < 0 ) @@ -505,15 +485,17 @@ void VOX_LoadSound( channel_t *ch, const char *pszin ) return; } - ch->words[j].sfx = S_FindName( pathbuffer, &ch->words[j].fKeepCached ); + qboolean in_cache = false; + ch->words[j].sfx = S_FindName( pathbuffer, &in_cache ); + ch->words[j].in_cache = in_cache; j++; } ch->words[j].sfx = NULL; ch->sfx = ch->words[0].sfx; - ch->wordIndex = 0; - ch->isSentence = true; + ch->word_index = 0; + ch->is_sentence = true; VOX_LoadWord( ch ); } diff --git a/engine/client/sound.h b/engine/client/sound.h index 07b316ba..62c7f4a0 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -20,31 +20,24 @@ extern poolhandle_t sndpool; #include "xash3d_mathlib.h" -#define XASH_AUDIO_CD_QUALITY 1 // some platforms might need this - // sound engine rate defines -#if XASH_AUDIO_CD_QUALITY #define SOUND_11k 11025 // 11khz sample rate #define SOUND_22k 22050 // 22khz sample rate #define SOUND_44k 44100 // 44khz sample rate -#else // XASH_AUDIO_CD_QUALITY -#define SOUND_11k 12000 // 11khz sample rate -#define SOUND_22k 24000 // 22khz sample rate -#define SOUND_44k 48000 // 44khz sample rate -#endif // XASH_AUDIO_CD_QUALITY #define SOUND_DMA_SPEED SOUND_44k // hardware playback rate -// NOTE: clipped sound at 32760 to avoid overload -#define CLIP( x ) (( x ) > 32760 ? 32760 : (( x ) < -32760 ? -32760 : ( x ))) - -#define PAINTBUFFER_SIZE 1024 // 44k: was 512 +#define PAINTBUFFER_SIZE 1024 #define S_RAW_SOUND_IDLE_SEC 10 // time interval for idling raw sound before it's freed #define S_RAW_SOUND_BACKGROUNDTRACK -2 #define S_RAW_SOUND_SOUNDTRACK -1 #define S_RAW_SAMPLES_PRECISION_BITS 14 +#define CVOXWORDMAX 64 + +#define CLIP16( x ) bound( SHRT_MIN + 8, x, SHRT_MAX - 8 ) + typedef struct { int left; @@ -61,21 +54,16 @@ typedef struct sfx_s struct sfx_s *hashNext; } sfx_t; -// structure used for fading in and out client sound volume. -typedef struct +typedef struct voxword_s { - float initial_percent; - float percent; // how far to adjust client's volume down by. - float starttime; // GetHostTime() when we started adjusting volume - float fadeouttime; // # of seconds to get to faded out state - float holdtime; // # of seconds to hold - float fadeintime; // # of seconds to restore -} soundfade_t; - -typedef struct -{ - float percent; -} musicfade_t; + sfx_t *sfx; + uint16_t volume; // volume percent + uint16_t pitch; // pitch shift percent (keep large for extra chipmunk fun) + uint16_t timecompress; // percent of skipped data (speeds up playback without pitch shift) + uint16_t start : 7; // percent at which playback starts + uint16_t end : 7; // percent at which playback ends + uint16_t in_cache : 1; // if set to 1, it was loaded prior and shouldn't be freed +} voxword_t; typedef struct snd_format_s { @@ -94,16 +82,6 @@ typedef struct const char *backendName; } dma_t; -#include "vox.h" - -typedef struct -{ - double sample; - wavdata_t *pData; - double forcedEndSample; - qboolean finished; -} mixer_t; - typedef struct rawchan_s { int entnum; @@ -120,29 +98,29 @@ typedef struct rawchan_s typedef struct channel_s { - char name[16]; // keep sentence name - sfx_t *sfx; // sfx number + char name[16]; // keep sentence name + sfx_t *sfx; // sfx number - int leftvol; // 0-255 left volume - int rightvol; // 0-255 right volume + int leftvol; // 0-255 left volume + int rightvol; // 0-255 right volume - int entnum; // entity soundsource - int entchannel; // sound channel (CHAN_STREAM, CHAN_VOICE, etc.) - vec3_t origin; // only use if fixed_origin is set - float dist_mult; // distance multiplier (attenuation/clipK) - int master_vol; // 0-255 master volume - int basePitch; // base pitch percent (100% is normal pitch playback) - float pitch; // real-time pitch after any modulation or shift by dynamic data - qboolean use_loop; // don't loop default and local sounds - qboolean staticsound; // use origin instead of fetching entnum's origin - qboolean localsound; // it's a local menu sound (not looped, not paused) - mixer_t pMixer; - - // sentence mixer - qboolean isSentence; // bit indicating sentence - int wordIndex; - mixer_t *currentWord; // NULL if sentence is finished + int entnum; // entity soundsource + int entchannel; // sound channel (CHAN_STREAM, CHAN_VOICE, etc.) + vec3_t origin; // only use if fixed_origin is set + float dist_mult; // distance multiplier (attenuation/clipK) + int master_vol; // 0-255 master volume + byte use_loop : 1; // don't loop default and local sounds + byte staticsound : 1; // use origin instead of fetching entnum's origin + byte localsound : 1; // it's a local menu sound (not looped, not paused) + byte is_sentence : 1; // bit indicating vox sentence + byte sentence_finished : 1; // if set, finished playing sentence + byte finished : 1; // if set, finished playing single word + byte word_index; + short basePitch; // base pitch percent (100% is normal pitch playback) voxword_t words[CVOXWORDMAX]; + double sample; + double forced_end; + wavdata_t *data; } channel_t; typedef struct @@ -153,23 +131,10 @@ typedef struct vec3_t up; int entnum; - int waterlevel; - float frametime; // used for sound fade - qboolean active; - qboolean inmenu; // listener in-menu ? - qboolean paused; qboolean streaming; // playing AVI-file qboolean stream_paused; // pause only background track } listener_t; -typedef struct -{ - string current; // a currently playing track - string loopName; // may be empty - stream_t *stream; - int source; // may be game, menu, etc -} bg_track_t; - typedef int sound_t; //==================================================================== @@ -198,7 +163,6 @@ extern convar_t s_samplecount; extern convar_t s_warn_late_precache; extern convar_t snd_mute_losefocus; -void S_InitScaletable( void ); wavdata_t *S_LoadSound( sfx_t *sfx ); float S_GetMasterVolume( void ); float S_GetMusicVolume( void ); @@ -211,16 +175,24 @@ void S_FreeChannel( channel_t *ch ); // // s_mix.c // -int S_MixDataToDevice( channel_t *pChannel, int sampleCount, int outputRate, int outputOffset, int timeCompress ); -void MIX_ClearAllPaintBuffers( int SampleCount, qboolean clearFilters ); -void MIX_InitAllPaintbuffers( void ); -void MIX_FreeAllPaintbuffers( void ); -void MIX_PaintChannels( int endtime ); +void S_ClearBuffers( int num_samples ); +void S_PaintChannels( int endtime ); // s_load.c -qboolean S_TestSoundChar( const char *pch, char c ); -char *S_SkipSoundChar( const char *pch ); -sfx_t *S_FindName( const char *name, int *pfInCache ); +static inline qboolean S_TestSoundChar( const char *pch, char c ) +{ + if( COM_StringEmptyOrNULL( pch )) + return false; + + return pch[0] == c || pch[1] == c; +} + +static inline const char *S_SkipSoundChar( const char *pch ) +{ + return pch[0] == '!' ? pch + 1 : pch; +} + +sfx_t *S_FindName( const char *name, qboolean *pfInCache ); sound_t S_RegisterSound( const char *name ); void S_FreeSound( sfx_t *sfx ); void S_InitSounds( void ); @@ -228,9 +200,8 @@ void S_InitSounds( void ); // s_dsp.c void SX_Init( void ); void SX_Free( void ); -void CheckNewDspPresets( void ); -void DSP_Process( portable_samplepair_t *pbfront, int sampleCount ); -void DSP_ClearState( void ); +void SX_RoomFX( portable_samplepair_t *paint, int num_samples ); +void SX_ClearState( void ); qboolean S_Init( void ); void S_Shutdown( void ); @@ -254,28 +225,41 @@ void S_FreeSounds( void ); // // s_mouth.c // -void SND_InitMouth( int entnum, int entchannel ); void SND_ForceInitMouth( int entnum ); -void SND_MoveMouth8( channel_t *ch, wavdata_t *pSource, int count ); -void SND_MoveMouth16( channel_t *ch, wavdata_t *pSource, int count ); -void SND_MoveMouthRaw( rawchan_t *ch, portable_samplepair_t *pData, int count ); -void SND_CloseMouth( channel_t *ch ); void SND_ForceCloseMouth( int entnum ); +void SND_MoveMouth8( mouth_t *mouth, int pos, const wavdata_t *sc, int count, qboolean use_loop ); +void SND_MoveMouth16( mouth_t *mouth, int pos, const wavdata_t *sc, int count, qboolean use_loop ); +void SND_MoveMouthRaw( mouth_t *mouth, const portable_samplepair_t *buf, int count ); + +static inline void SND_InitMouth( int entnum, int entchannel ) +{ + if(( entchannel == CHAN_VOICE || entchannel == CHAN_STREAM ) && entnum > 0 ) + { + SND_ForceInitMouth( entnum ); + } +} + +static inline void SND_CloseMouth( const channel_t *ch ) +{ + if( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_STREAM ) + { + SND_ForceCloseMouth( ch->entnum ); + } +} + // // s_stream.c // void S_StreamBackgroundTrack( void ); void S_PrintBackgroundTrackState( void ); -void S_FadeMusicVolume( float fadePercent ); +void S_MusicFade( float fade_percent ); // // s_utils.c // -int S_ZeroCrossingAfter( wavdata_t *pWaveData, int sample ); -int S_ZeroCrossingBefore( wavdata_t *pWaveData, int sample ); -int S_ConvertLoopedPosition( wavdata_t *pSource, int samplePosition, qboolean use_loop ); -int S_GetOutputData( wavdata_t *pSource, void **pData, int samplePosition, int sampleCount, qboolean use_loop ); +int S_AdjustLoopedSamplePosition( const wavdata_t *source, int current_sample, qboolean enable_looping ); +int S_RetrieveAudioSamples( const wavdata_t *source, const void **output_buffer, int start_position, int num_samples, qboolean enable_looping ); // // s_vox.c @@ -285,6 +269,7 @@ void VOX_Shutdown( void ); void VOX_SetChanVol( channel_t *ch ); void VOX_LoadSound( channel_t *pchan, const char *psz ); float VOX_ModifyPitch( channel_t *ch, float pitch ); -int VOX_MixDataToDevice( channel_t *pChannel, int sampleCount, int outputRate, int outputOffset ); +void VOX_LoadWord( channel_t *pchan ); +void VOX_FreeWord( channel_t *pchan ); #endif//SOUND_H diff --git a/engine/client/soundlib/snd_main.c b/engine/client/soundlib/snd_main.c index 2f7dd58c..23e2ff35 100644 --- a/engine/client/soundlib/snd_main.c +++ b/engine/client/soundlib/snd_main.c @@ -32,7 +32,7 @@ static MALLOC_LIKE( FS_FreeSound, 1 ) wavdata_t *SoundPack( void ) wavdata_t *pack = Mem_Malloc( host.soundpool, sizeof( *pack ) + sound.size ); pack->size = sound.size; - pack->loopStart = sound.loopstart; + pack->loop_start = sound.loopstart; pack->samples = sound.samples; pack->type = sound.type; pack->flags = sound.flags; diff --git a/engine/client/vox.h b/engine/client/vox.h deleted file mode 100644 index f6319a5c..00000000 --- a/engine/client/vox.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -vox.h - sentences vox private header -Copyright (C) 2010 Uncle Mike - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -*/ - -#ifndef VOX_H -#define VOX_H - -#define CVOXWORDMAX 64 -#define SENTENCE_INDEX -99999 // unique sentence index - -typedef struct voxword_s -{ - int volume; // increase percent, ie: 125 = 125% increase - int pitch; // pitch shift up percent - int start; // offset start of wave percent - int end; // offset end of wave percent - int fKeepCached; // 1 if this word was already in cache before sentence referenced it - int timecompress; // % of wave to skip during playback (causes no pitch shift) - sfx_t *sfx; // name and cache pointer -} voxword_t; - -struct channel_s; -void VOX_LoadWord( struct channel_s *pchan ); -void VOX_FreeWord( struct channel_s *pchan ); - -#endif diff --git a/engine/common/common.h b/engine/common/common.h index ed896507..8efa0dd3 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -537,7 +537,7 @@ typedef enum sndFlags_e typedef struct wavdata_s { size_t size; // for bounds checking - uint loopStart; // offset at this point sound will be looping while playing more than only once + uint loop_start; // offset at this point sound will be looping while playing more than only once uint samples; // total samplecount in wav uint type; // compression type uint flags; // misc sound flags diff --git a/engine/common/soundlib/snd_utils.c b/engine/common/soundlib/snd_utils.c index 6d039e5b..52d1df84 100644 --- a/engine/common/soundlib/snd_utils.c +++ b/engine/common/soundlib/snd_utils.c @@ -379,7 +379,7 @@ static qboolean Sound_ResampleInternal( wavdata_t *sc, int outrate, int outwidth sc->samples = outcount; if( FBitSet( sc->flags, SOUND_LOOPED )) - sc->loopStart = sc->loopStart / stepscale; + sc->loop_start = sc->loop_start / stepscale; sound.tempbuffer = (byte *)Mem_Realloc( host.soundpool, sound.tempbuffer, sc->size );