From f0da5370cf549bb3cad48189af9728c3e52c3f65 Mon Sep 17 00:00:00 2001 From: g-cont Date: Sat, 13 Aug 2016 00:00:00 +0300 Subject: [PATCH] 13 Aug 2016 --- change.log | 15 + common/render_api.h | 3 + engine/client/cl_events.c | 22 +- engine/client/cl_frame.c | 41 +- engine/client/cl_game.c | 50 +- engine/client/cl_main.c | 13 +- engine/client/cl_parse.c | 3 + engine/client/cl_pmove.c | 11 +- engine/client/cl_tent.c | 3 + engine/client/cl_view.c | 5 +- engine/client/client.h | 7 + engine/client/gl_backend.c | 2 +- engine/client/gl_beams.c | 2 +- engine/client/gl_image.c | 4 +- engine/client/gl_local.h | 1 - engine/client/gl_rmain.c | 4 +- engine/client/gl_rsurf.c | 2 + engine/client/gl_studio.c | 23 +- engine/client/gl_vidnt.c | 6 +- engine/client/gl_warp.c | 4 +- engine/client/s_dsp.c | 5992 ++++-------------------------- engine/client/s_main.c | 7 +- engine/common/build.c | 4 +- engine/common/cmd.c | 4 +- engine/common/common.h | 4 + engine/common/filesystem.c | 36 +- engine/common/gamma.c | 22 +- engine/common/imagelib/img_bmp.c | 8 + engine/common/imagelib/img_dds.c | 11 +- engine/common/imagelib/img_tga.c | 9 + engine/common/imagelib/img_wad.c | 12 + engine/common/model.c | 25 +- engine/common/net_encode.c | 6 +- engine/common/netchan.h | 2 +- engine/server/server.h | 1 + engine/server/sv_game.c | 2 +- engine/server/sv_init.c | 22 +- engine/server/sv_main.c | 17 +- engine/studio.h | 4 +- 39 files changed, 1052 insertions(+), 5357 deletions(-) diff --git a/change.log b/change.log index b50f25b3..6bf11b7e 100644 --- a/change.log +++ b/change.log @@ -1,5 +1,20 @@ build 3366 +Render: get support for custom DXT encoding (custom renderers only) +Render: remove image program stuff (just not used) +Engine: adding support for new wad filetypes (like DDS images) and wad imagetypes (normalmap, glossmap etc) +Render: implement tiling on the studiomodels +Client: do revision of predicting implementation, fix errors, more clean code +Client: implement prediction error to avoid ugly blinking view on moving platforms when predicting is enabled +Render: fixup the DDS loading code (invalid calc for mip-sizes) +Client: fixup parser of detailtextures when texturename contain symbol '{' +Render: added experimental hint for Nvidia drivers for force select Nvidia videocard when engine is running +Engine: rewrote condition to calculate level CRC (singleplayer or multiplayer) +Engine: added cvar r_wadtextures like in HL. First load textures from the wad, then from BSP +Server: fix bug with cl_updaterate variable (always get default values if not changed by user) +Server: recalc the ping time correctly +Server: fix bug with too long player name +GameUI: fix bug with 4-bit bmps buttons (image cutter) build 3224 diff --git a/common/render_api.h b/common/render_api.h index a8573094..2e2fe7c1 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -65,6 +65,7 @@ GNU General Public License for more details. #define PARM_MAX_IMAGE_UNITS 29 #define PARM_CLIENT_ACTIVE 30 #define PARM_REBUILD_GAMMA 31 // if true lightmaps rebuilding for gamma change +#define PARM_DEDICATED_SERVER 32 enum { @@ -257,6 +258,8 @@ typedef struct render_interface_s qboolean (*R_DrawCubemapView)( const float *origin, const float *angles, int size ); // alloc or destroy studiomodel custom data void (*Mod_ProcessUserData)( struct model_s *mod, qboolean create, const byte *buffer ); + // alloc or destroy entity custom data + void (*R_ProcessEntData)( qboolean allocate ); } render_interface_t; #endif//RENDER_API_H \ No newline at end of file diff --git a/engine/client/cl_events.c b/engine/client/cl_events.c index bc047330..af697990 100644 --- a/engine/client/cl_events.c +++ b/engine/client/cl_events.c @@ -395,6 +395,11 @@ void CL_ParseEvent( sizebuf_t *msg ) args.angles[PITCH] = -state->angles[PITCH] * 3; args.angles[YAW] = state->angles[YAW]; args.angles[ROLL] = 0; // no roll + + if( VectorIsNull( args.origin )) + VectorCopy( state->origin, args.origin ); + if( VectorIsNull( args.velocity )) + VectorCopy( state->velocity, args.velocity ); } } else if( state ) @@ -443,6 +448,13 @@ void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, floa MsgDev( D_ERROR, "CL_PlaybackEvent: invalid eventindex %i\n", eventindex ); return; } + + if( flags & FEV_SERVER ) + { + MsgDev( D_WARN, "CL_PlaybackEvent: event with FEV_SERVER flag!\n" ); + return; + } + // check event for precached if( !CL_EventIndex( cl.event_precache[eventindex] )) { @@ -459,15 +471,17 @@ void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, floa args.flags = 0; args.entindex = invokerIndex; -// TODO: restore checks when predicting will be done -// if( !angles || VectorIsNull( angles )) + if( !angles || VectorIsNull( angles )) VectorCopy( cl.refdef.cl_viewangles, args.angles ); + else VectorCopy( angles, args.angles ); -// if( !origin || VectorIsNull( origin )) + if( !origin || VectorIsNull( origin )) VectorCopy( cl.frame.client.origin, args.origin ); + else VectorCopy( origin, args.origin ); VectorCopy( cl.frame.client.velocity, args.velocity ); - args.ducking = cl.frame.client.bInDuck; + args.ducking = (cl.frame.playerstate[cl.playernum].usehull == 1); +// args.ducking = cl.frame.client.bInDuck; args.fparam1 = fparam1; args.fparam2 = fparam2; diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 4fe8c6af..2d916f62 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -148,7 +148,7 @@ int CL_InterpolateModel( cl_entity_t *e ) if( t - t2 < 0.0f ) return 0; - if( t2 == 0.0f || VectorIsNull( ph1->origin ) && !VectorIsNull( ph0->origin )) + if( t2 == 0.0f || ( VectorIsNull( ph1->origin ) && !VectorIsNull( ph0->origin ))) { VectorCopy( ph0->origin, e->origin ); VectorCopy( ph0->angles, e->angles ); @@ -513,7 +513,12 @@ void CL_WeaponAnim( int iAnim, int body ) { cl_entity_t *view = &clgame.viewent; - view->curstate.modelindex = cl.frame.client.viewmodel; + cl.weaponstarttime = 0; + cl.weaponsequence = iAnim; + + if( Host_IsLocalClient() || cl_predict->value || !cl_lw->value ) + view->curstate.modelindex = cl.frame.client.viewmodel; + else view->curstate.modelindex = cl.predicted_viewmodel; // anim is changed. update latchedvars if( iAnim != view->curstate.sequence ) @@ -652,8 +657,16 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t qboolean newent = (old) ? false : true; qboolean result = true; - ent = CL_EDICT_NUM( newnum ); state = &cls.packet_entities[cls.next_client_entities % cls.num_client_entities]; + + if(( newnum < 0 ) || ( newnum >= clgame.maxEntities )) + { + if( !unchanged ) + MSG_ReadDeltaEntity( msg, old, state, newnum, CL_IsPlayerIndex( newnum ), cl.mtime[0] ); + return; + } + + ent = CL_EDICT_NUM( newnum ); ent->index = newnum; if( newent ) old = &ent->baseline; @@ -663,7 +676,21 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t if( !result ) { - if( newent ) Host_Error( "Cl_DeltaEntity: tried to release new entity\n" ); + if( newent ) + { + MsgDev( D_WARN, "Cl_DeltaEntity: tried to release new entity\n" ); + + // perform remove, entity was created and removed between packets + if( state->number == -1 ) + { + MsgDev( D_NOTE, "Entity %i was removed from server\n", newnum ); + ent->curstate.messagenum = 0; + ent->baseline.number = 0; + } + else MsgDev( D_NOTE, "Entity %i was removed from delta-message\n", newnum ); + + return; + } CL_KillDeadBeams( ent ); // release dead beams #if 0 @@ -788,13 +815,16 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) if( subtracted == 0 ) { - Host_Error( "CL_DeltaPacketEntities: update too old, connection dropped.\n" ); + MsgDev( D_NOTE, "CL_DeltaPacketEntities: update too old (flush)\n" ); + Con_NPrintf( 2, "^3Warning:^1 update too old\n^7\n" ); + CL_FlushEntityPacket( msg ); return; } if( subtracted >= CL_UPDATE_MASK ) { // we can't use this, it is too old + MsgDev( D_NOTE, "CL_ParsePacketEntities: delta frame is too old: overflow (flush)\n"); Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" ); CL_FlushEntityPacket( msg ); return; @@ -804,6 +834,7 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) if(( cls.next_client_entities - oldframe->first_entity ) > ( cls.num_client_entities - 128 )) { + MsgDev( D_NOTE, "CL_ParsePacketEntities: delta frame is too old (flush)\n"); Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" ); CL_FlushEntityPacket( msg ); return; diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 29169c6c..f2dfeeb6 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -30,6 +30,7 @@ GNU General Public License for more details. #include "vgui_draw.h" #include "sound.h" // SND_STOP_LOOPING +#define MAX_LINELENGTH 80 #define MAX_TEXTCHANNELS 8 // must be power of two (GoldSrc uses 4 channels) #define TEXT_MSGNAME "TextMessage%i" @@ -573,7 +574,7 @@ void CL_DrawCenterPrint( void ) char *pText; int i, j, x, y; int width, lineLength; - byte *colorDefault, line[80]; + byte *colorDefault, line[MAX_LINELENGTH]; int charWidth, charHeight; if( !clgame.centerPrint.time ) @@ -596,7 +597,7 @@ void CL_DrawCenterPrint( void ) lineLength = 0; width = 0; - while( *pText && *pText != '\n' ) + while( *pText && *pText != '\n' && lineLength < MAX_LINELENGTH ) { byte c = *pText; line[lineLength] = c; @@ -606,6 +607,9 @@ void CL_DrawCenterPrint( void ) pText++; } + if( lineLength == MAX_LINELENGTH ) + lineLength--; + pText++; // Skip LineFeed line[lineLength] = 0; @@ -1084,6 +1088,8 @@ void CL_InitEdicts( void ) { ASSERT( clgame.entities == NULL ); + if( !clgame.mempool ) return; // Host_Error without client + CL_UPDATE_BACKUP = ( cl.maxclients == 1 ) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP; cls.num_client_entities = CL_UPDATE_BACKUP * 64; cls.packet_entities = Z_Realloc( cls.packet_entities, sizeof( entity_state_t ) * cls.num_client_entities ); @@ -1097,10 +1103,22 @@ void CL_InitEdicts( void ) clgame.maxRemapInfos = clgame.maxEntities + 1; clgame.remap_info = (remap_info_t **)Mem_Alloc( clgame.mempool, sizeof( remap_info_t* ) * clgame.maxRemapInfos ); } + + if( clgame.drawFuncs.R_ProcessEntData != NULL ) + { + // let the client.dll free custom data + clgame.drawFuncs.R_ProcessEntData( true ); + } } void CL_FreeEdicts( void ) { + if( clgame.drawFuncs.R_ProcessEntData != NULL ) + { + // let the client.dll free custom data + clgame.drawFuncs.R_ProcessEntData( false ); + } + if( clgame.entities ) Mem_Free( clgame.entities ); clgame.entities = NULL; @@ -2098,7 +2116,7 @@ static void pfnHookEvent( const char *filename, pfnEventHook pfn ) if( !Q_stricmp( name, ev->name ) && ev->func != NULL ) { - MsgDev( D_WARN, "CL_HookEvent: %s already hooked!\n" ); + MsgDev( D_WARN, "CL_HookEvent: %s already hooked!\n", name ); return; } } @@ -2808,7 +2826,11 @@ TODO: implement */ int pfnDrawString( int x, int y, const char *str, int r, int g, int b ) { - return 0; + // draw the string until we hit the null character or a newline character + for( ; *str != 0 && *str != '\n'; str++ ) + x += pfnDrawCharacter( x, y, (byte)*str, r, g, b ); + + return x; } /* @@ -2820,7 +2842,14 @@ TODO: implement */ int pfnDrawStringReverse( int x, int y, const char *str, int r, int g, int b ) { - return 0; + char *szIt; + + // find the end of the string + for( szIt = (char *)str; *szIt != 0; szIt++ ) + x -= clgame.scrInfo.charWidths[(byte)*szIt]; + pfnDrawString( x, y, str, r, g, b ); + + return x; } /* @@ -2951,7 +2980,7 @@ pfnGetAppID */ int pfnGetAppID( void ) { - return 220; // standard Valve value + return 130; // borrowed from SDLash3D } /* @@ -4074,7 +4103,7 @@ qboolean CL_LoadProgs( const char *name ) } Cvar_Get( "cl_nopred", "1", CVAR_ARCHIVE|CVAR_USERINFO, "disable client movement predicting" ); - Cvar_Get( "cl_lw", "0", CVAR_ARCHIVE|CVAR_USERINFO, "enable client weapon predicting" ); + cl_lw = Cvar_Get( "cl_lw", "0", CVAR_ARCHIVE|CVAR_USERINFO, "enable client weapon predicting" ); Cvar_Get( "cl_lc", "0", CVAR_ARCHIVE|CVAR_USERINFO, "enable lag compensation" ); Cvar_FullSet( "host_clientloaded", "1", CVAR_INIT ); @@ -4086,13 +4115,12 @@ qboolean CL_LoadProgs( const char *name ) CL_InitParticles (); CL_InitViewBeams (); CL_InitTempEnts (); - CL_InitEdicts (); // initailize local player and world - CL_InitClientMove(); // initialize pm_shared if( !R_InitRenderAPI()) // Xash3D extension - { MsgDev( D_WARN, "CL_LoadProgs: couldn't get render API\n" ); - } + + CL_InitEdicts (); // initailize local player and world + CL_InitClientMove(); // initialize pm_shared // initialize game clgame.dllFuncs.pfnInit(); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index e7ce6c82..94d7a986 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -45,6 +45,7 @@ convar_t *cl_solid_players; convar_t *cl_draw_beams; convar_t *cl_cmdrate; convar_t *cl_interp; +convar_t *cl_lw; // // userinfo @@ -311,14 +312,20 @@ void CL_CreateCmd( void ) VectorCopy( cl.frame.client.origin, cl.data.origin ); VectorCopy( cl.refdef.cl_viewangles, cl.data.viewangles ); cl.data.iWeaponBits = cl.frame.client.weapons; - cl.data.fov = cl.frame.client.fov; + + if( cl.scr_fov < 1.0f || cl.scr_fov > 179.0f ) + cl.scr_fov = 90.0f; // reset to default + cl.data.fov = cl.scr_fov; clgame.dllFuncs.pfnUpdateClientData( &cl.data, cl.time ); // grab changes VectorCopy( cl.data.viewangles, cl.refdef.cl_viewangles ); cl.frame.client.weapons = cl.data.iWeaponBits; - cl.frame.client.fov = cl.data.fov; + cl.scr_fov = cl.data.fov; + + if( cl.scr_fov < 1.0f || cl.scr_fov > 179.0f ) + cl.scr_fov = 90.0f; // reset to default // allways dump the first ten messages, // because it may contain leftover inputs @@ -430,6 +437,7 @@ void CL_WritePacket( void ) Cvar_SetFloat( "cl_cmdrate", MIN_CMD_RATE ); } #endif + Q_memset( data, 0, MAX_CMD_BUFFER ); BF_Init( &buf, "ClientData", data, sizeof( data )); // Determine number of backup commands to send along @@ -814,6 +822,7 @@ void CL_ClearState( void ) Cvar_FullSet( "cl_background", "0", CVAR_READ_ONLY ); cl.refdef.movevars = &clgame.movevars; cl.maxclients = 1; // allow to drawing player in menu + cl.scr_fov = 90.0f; Cvar_SetFloat( "scr_download", 0.0f ); Cvar_SetFloat( "scr_loading", 0.0f ); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index edfb685d..9f75b376 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -285,6 +285,9 @@ void CL_ParseSoundPacket( sizebuf_t *msg, qboolean is_ambient ) } else handle = cl.sound_index[sound]; // see precached sound + if( !cl.audio_prepped ) + return; // too early + if( is_ambient ) { S_AmbientSound( pos, entnum, handle, volume, attn, pitch, flags ); diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 1e5f2ecc..a08df177 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -550,9 +550,10 @@ static const char *pfnTraceTexture( int ground, float *vstart, float *vend ) static void pfnPlaySound( int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch ) { - sound_t snd = S_RegisterSound( sample ); + if( !clgame.pmove->runfuncs ) + return; - S_StartSound( NULL, clgame.pmove->player_index + 1, channel, snd, volume, attenuation, pitch, fFlags ); + S_StartSound( NULL, clgame.pmove->player_index + 1, channel, S_RegisterSound( sample ), volume, attenuation, pitch, fFlags ); } static void pfnPlaybackEventFull( int flags, int clientindex, word eventindex, float delay, float *origin, @@ -1007,5 +1008,11 @@ void CL_PredictMovement( void ) VectorCopy( to->client.velocity, cl.predicted_velocity ); VectorCopy( to->client.view_ofs, cl.predicted_viewofs ); VectorCopy( to->client.punchangle, cl.predicted_punchangle ); + + cl.predicted_viewmodel = to->client.viewmodel; + cl.scr_fov = to->client.fov; + + if( cl.scr_fov < 1.0f || cl.scr_fov > 179.0f ) + cl.scr_fov = 90.0f; } } \ No newline at end of file diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index c2ca4d17..2721545c 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -442,6 +442,9 @@ void CL_FizzEffect( cl_entity_t *pent, int modelIndex, int density ) if( !pent || Mod_GetType( modelIndex ) == mod_bad ) return; + if( pent->curstate.modelindex <= 0 ) + return; + count = density + 1; density = count * 3 + 6; diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index 476629be..cef90e3a 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -81,8 +81,11 @@ void V_SetupRefDef( void ) cl.refdef.viewport[0] = (scr_width->integer - cl.refdef.viewport[2]) / 2; cl.refdef.viewport[1] = (scr_height->integer - sb_lines - cl.refdef.viewport[3]) / 2; + if( cl.scr_fov < 1.0f || cl.scr_fov > 179.0f ) + cl.scr_fov = 90.0f; + // calc FOV - cl.refdef.fov_x = cl.data.fov; // this is a final fov value + cl.refdef.fov_x = cl.scr_fov; // this is a final fov value cl.refdef.fov_y = V_CalcFov( &cl.refdef.fov_x, cl.refdef.viewport[2], cl.refdef.viewport[3] ); // adjust FOV for widescreen diff --git a/engine/client/client.h b/engine/client/client.h index 746a7dc1..617e9aad 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -163,6 +163,12 @@ typedef struct cl_entity_t *world; model_t *worldmodel; // pointer to world + + // weapon predict stuff + float scr_fov; + int predicted_viewmodel; + float weaponstarttime; + int weaponsequence; } client_t; /* @@ -528,6 +534,7 @@ extern convar_t *cl_lightstyle_lerping; extern convar_t *cl_draw_particles; extern convar_t *cl_levelshot_name; extern convar_t *cl_draw_beams; +extern convar_t *cl_lw; // local weapons extern convar_t *scr_centertime; extern convar_t *scr_viewsize; extern convar_t *scr_download; diff --git a/engine/client/gl_backend.c b/engine/client/gl_backend.c index 95948f37..ca6c96fe 100644 --- a/engine/client/gl_backend.c +++ b/engine/client/gl_backend.c @@ -649,7 +649,7 @@ void R_ShowTextures( void ) if( showHelp ) { - CL_CenterPrint( "use '<-' and '->' keys for view all the textures", 0.25f ); + CL_CenterPrint( "use '<-' and '->' keys to view all the textures", 0.25f ); showHelp = false; } diff --git a/engine/client/gl_beams.c b/engine/client/gl_beams.c index bc19e284..50864cb9 100644 --- a/engine/client/gl_beams.c +++ b/engine/client/gl_beams.c @@ -2166,7 +2166,7 @@ void CL_ReadLineFile_f( void ) if( token[0] != '-' ) { - MsgDev( D_ERROR, "%s is corrupted\n" ); + MsgDev( D_ERROR, "%s is corrupted\n", filename ); break; } diff --git a/engine/client/gl_image.c b/engine/client/gl_image.c index 46041441..08ed48e0 100644 --- a/engine/client/gl_image.c +++ b/engine/client/gl_image.c @@ -210,7 +210,7 @@ void GL_TexFilter( gltexture_t *tex, qboolean update ) if( tex->flags & ( TF_BORDER|TF_ALPHA_BORDER ) && !GL_Support( GL_CLAMP_TEXBORDER_EXT )) { // border is not support, use clamp instead - tex->flags &= ~(TF_BORDER||TF_ALPHA_BORDER); + tex->flags &= ~(TF_BORDER|TF_ALPHA_BORDER); tex->flags |= TF_CLAMP; } @@ -1777,7 +1777,7 @@ void GL_FreeImage( const char *name ) if( Q_strlen( name ) >= sizeof( r_textures->name )) { - MsgDev( D_ERROR, "GL_FreeImage: too long name %s\n", name, sizeof( r_textures->name )); + MsgDev( D_ERROR, "GL_FreeImage: too long name %s\n", name ); return; } diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 9283df74..1deed5b4 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -672,7 +672,6 @@ extern convar_t *r_fastsky; extern convar_t *vid_displayfrequency; extern convar_t *vid_fullscreen; extern convar_t *vid_gamma; -extern convar_t *vid_texgamma; extern convar_t *vid_mode; #endif//GL_LOCAL_H \ No newline at end of file diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index ac49d123..f107ac8f 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -1248,7 +1248,7 @@ void R_BeginFrame( qboolean clearScene ) else { glConfig.softwareGammaUpdate = true; - BuildGammaTable( vid_gamma->value, vid_texgamma->value ); + BuildGammaTable( vid_gamma->value, GAMMA ); GL_RebuildLightmaps(); glConfig.softwareGammaUpdate = false; } @@ -1466,6 +1466,8 @@ static int GL_RenderGetParm( int parm, int arg ) return (cls.state == ca_active); case PARM_REBUILD_GAMMA: return glConfig.softwareGammaUpdate; + case PARM_DEDICATED_SERVER: + return (host.type == HOST_DEDICATED); } return 0; } diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index 0772d670..b0466608 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -716,6 +716,8 @@ void DrawGLPoly( glpoly_t *p, float xScale, float yScale ) cl_entity_t *e = RI.currententity; int i, hasScale = false; + if( !p ) return; + // special hack for non-lightmapped surfaces if( p->flags & SURF_DRAWTILED ) GL_ResetFogColor(); diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index 52a77b94..f49b13ca 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -138,7 +138,7 @@ R_StudioInit */ void R_StudioInit( void ) { - float pixelAspect; + float pixelAspect, fov_x = 90.0f, fov_y; r_studio_lambert = Cvar_Get( "r_studio_lambert", "2", CVAR_ARCHIVE, "bonelighting lambert value" ); r_studio_lerping = Cvar_Get( "r_studio_lerping", "1", CVAR_ARCHIVE, "enables studio animation lerping" ); @@ -156,7 +156,8 @@ void R_StudioInit( void ) pixelAspect *= (320.0f / 240.0f); else pixelAspect *= (640.0f / 480.0f); - aliasXscale = (float)scr_width->integer / RI.refdef.fov_y; + fov_y = V_CalcFov( &fov_x, scr_width->integer, scr_height->integer ); + aliasXscale = (float)scr_width->integer / fov_y; // stub aliasYscale = aliasXscale * pixelAspect; Matrix3x4_LoadIdentity( g_aliastransform ); @@ -736,7 +737,7 @@ StudioCalcBoneAdj void R_StudioCalcBoneAdj( float dadt, float *adj, const byte *pcontroller1, const byte *pcontroller2, byte mouthopen ) { mstudiobonecontroller_t *pbonecontroller; - float value; + float value = 0.0f; int i, j; pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pStudioHeader + m_pStudioHeader->bonecontrollerindex); @@ -2948,7 +2949,7 @@ R_StudioDrawPlayer static int R_StudioDrawPlayer( int flags, entity_state_t *pplayer ) { int m_nPlayerIndex; - float gaitframe, gaityaw; + float gaitframe = 0.0f, gaityaw = 0.0f; vec3_t dir, prevgaitorigin; alight_t lighting; @@ -3301,10 +3302,17 @@ void R_RunViewmodelEvents( void ) if( !Mod_Extradata( clgame.viewent.model )) return; + if( cl_lw->value && cl.frame.client.viewmodel != cl.predicted_viewmodel ) + return; + RI.currententity = &clgame.viewent; RI.currentmodel = RI.currententity->model; if( !RI.currentmodel ) return; + if( !cl.weaponstarttime ) cl.weaponstarttime = cl.time; + RI.currententity->curstate.animtime = cl.weaponstarttime; + RI.currententity->curstate.sequence = cl.weaponsequence; + pStudioDraw->StudioDrawModel( STUDIO_EVENTS ); RI.currententity = NULL; @@ -3331,6 +3339,9 @@ void R_DrawViewModel( void ) if( !Mod_Extradata( clgame.viewent.model )) return; + if( cl_lw->value && cl.frame.client.viewmodel != cl.predicted_viewmodel ) + return; + RI.currententity = &clgame.viewent; RI.currentmodel = RI.currententity->model; if( !RI.currentmodel ) return; @@ -3344,6 +3355,10 @@ void R_DrawViewModel( void ) if( r_lefthand->integer == 1 || g_iBackFaceCull ) GL_FrontFace( !glState.frontFace ); + if( !cl.weaponstarttime ) cl.weaponstarttime = cl.time; + RI.currententity->curstate.animtime = cl.weaponstarttime; + RI.currententity->curstate.sequence = cl.weaponsequence; + pStudioDraw->StudioDrawModel( STUDIO_RENDER ); // restore depth range diff --git a/engine/client/gl_vidnt.c b/engine/client/gl_vidnt.c index 1d2c5f20..34c6cc7e 100644 --- a/engine/client/gl_vidnt.c +++ b/engine/client/gl_vidnt.c @@ -90,7 +90,6 @@ convar_t *r_fastsky; convar_t *vid_displayfrequency; convar_t *vid_fullscreen; convar_t *vid_gamma; -convar_t *vid_texgamma; convar_t *vid_mode; byte *r_temppool; @@ -882,7 +881,7 @@ void VID_StartupGamma( void ) if( gl_ignorehwgamma->integer ) { glConfig.deviceSupportsGamma = false; // even if supported! - BuildGammaTable( vid_gamma->value, vid_texgamma->value ); + BuildGammaTable( vid_gamma->value, GAMMA ); MsgDev( D_NOTE, "VID_StartupGamma: software gamma initialized\n" ); return; } @@ -1599,7 +1598,7 @@ void R_RenderInfo_f( void ) } Msg( "\n" ); - Msg( "MODE: %i, %i x %i %s\n", vid_mode->integer, r_width->integer, r_height->integer ); + Msg( "MODE: %i, %i x %i %s\n", vid_mode->integer, r_width->integer, r_height->integer, vidmode[vid_mode->integer].desc ); Msg( "GAMMA: %s\n", (glConfig.deviceSupportsGamma) ? "hardware" : "software" ); Msg( "\n" ); Msg( "PICMIP: %i\n", gl_picmip->integer ); @@ -1680,7 +1679,6 @@ void GL_InitCommands( void ) gl_swapInterval->modified = true; vid_gamma = Cvar_Get( "gamma", "1.0", CVAR_ARCHIVE, "gamma amount" ); - vid_texgamma = Cvar_Get( "texgamma", "2.2", CVAR_GLCONFIG, "texgamma amount (default Half-Life artwork gamma)" ); vid_mode = Cvar_Get( "vid_mode", VID_AUTOMODE, CVAR_RENDERINFO, "display resolution mode" ); vid_fullscreen = Cvar_Get( "fullscreen", "0", CVAR_RENDERINFO, "set in 1 to enable fullscreen mode" ); vid_displayfrequency = Cvar_Get ( "vid_displayfrequency", "0", CVAR_RENDERINFO, "fullscreen refresh rate" ); diff --git a/engine/client/gl_warp.c b/engine/client/gl_warp.c index e86d2ad4..a8ddd4f8 100644 --- a/engine/client/gl_warp.c +++ b/engine/client/gl_warp.c @@ -127,6 +127,8 @@ void DrawSkyPolygon( int nump, vec3_t vecs ) j = vec_to_st[axis][2]; dv = (j > 0) ? vecs[j-1] : -vecs[-j-1]; + if( dv == 0.0f ) continue; + j = vec_to_st[axis][0]; s = (j < 0) ? -vecs[-j-1] / dv : vecs[j-1] / dv; @@ -502,7 +504,7 @@ void R_InitSky( mip_t *mt, texture_t *tx ) } // make sure what sky image is valid - if( !r_sky || !r_sky->palette || r_sky->type != PF_INDEXED_32 ) + if( !r_sky || !r_sky->palette || r_sky->type != PF_INDEXED_32 || r_sky->height == 0 ) { MsgDev( D_ERROR, "R_InitSky: unable to load sky texture %s\n", tx->name ); FS_FreeImage( r_sky ); diff --git a/engine/client/s_dsp.c b/engine/client/s_dsp.c index 484f5ccf..b616b82e 100644 --- a/engine/client/s_dsp.c +++ b/engine/client/s_dsp.c @@ -14,5445 +14,899 @@ GNU General Public License for more details. */ #include "common.h" +#include "client.h" #include "sound.h" -#define SIGN( d ) (( d ) < 0 ? -1 : 1 ) -#define ABS( a ) abs( a ) -#define MSEC_TO_SAMPS( a ) ((( a ) * SOUND_DMA_SPEED) / 1000 ) // convert milliseconds to # samples in equivalent time -#define SEC_TO_SAMPS( a ) (( a ) * SOUND_DMA_SPEED) // conver seconds to # samples in equivalent time -#define CLIP_DSP( x ) ( x ) -#define SOUND_MS_PER_FT 1 // sound travels approx 1 foot per millisecond -#define ROOM_MAX_SIZE 1000 // max size in feet of room simulation for dsp +#define MAX_DELAY 0.4f +#define MAX_ROOM_TYPES ARRAYSIZE( rgsxpre ) -// Performance notes: +#define MONODLY 0 +#define MAX_MONO_DELAY 0.4f -// DSP processing should take no more than 3ms total time per frame to remain on par with hl1 -// Assume a min frame rate of 24fps = 42ms per frame -// at 24fps, to maintain 44.1khz output rate, we must process about 1840 mono samples per frame. -// So we must process 1840 samples in 3ms. +#define REVERBPOS 1 +#define MAX_REVERB_DELAY 0.1f -// on a 1Ghz CPU (mid-low end CPU) 3ms provides roughly 3,000,000 cycles. -// Thus we have 3e6 / 1840 = 1630 cycles per sample. +#define STEREODLY 3 +#define MAX_STEREO_DELAY 0.1f -#define PBITS 12 // parameter bits -#define PMAX ((1 << PBITS)-1) // parameter max size +#define MAXDLY (STEREODLY + 1) +#define MAXLP 10 +#define MAXPRESETS ARRAYSIZE( rgsxpre ) -// crossfade from y2 to y1 at point r (0 < r < PMAX ) -#define XFADE( y1, y2, r ) (((y1) * (r)) >> PBITS) + (((y2) * (PMAX - (r))) >> PBITS); -#define XFADEF( y1, y2, r ) (((y1) * (r)) / (float)(PMAX)) + (((y2) * (PMAX - (r))) / (float)(PMAX)); - -///////////////////// -// dsp helpers -///////////////////// - -// dot two integer vectors of length M+1 -// M is filter order, h is filter vector, w is filter state vector -_inline int dot ( int M, int *h, int *w ) +typedef struct sx_preset_s { - int i, y; + float room_lp; // lowpass + float room_mod; // modulation - for( y = 0, i = 0; i <= M; i++ ) - y += ( h[i] * w[i] ) >> PBITS; - return y; -} + // reverb + float room_size; + float room_refl; + float room_rvblp; -// delay array w[] by D samples -// w[0] = input, w[D] = output -// practical for filters, but not for large values of D -_inline void delay( int D, int *w ) + // delay + float room_delay; + float room_feedback; + float room_dlylp; + float room_left; +} sx_preset_t; + +typedef struct dly_s { - int i; + size_t cdelaysamplesmax; // delay line array size - for( i = D; i >= 1; i-- ) // reverse order updating - w[i] = w[i-1]; -} + // delay line pointers + size_t idelayinput; + size_t idelayoutput; -// circular wrap of pointer p, relative to array w -// D delay line size in samples w[0...D] -// w delay line buffer pointer, dimension D+1 -// p circular pointer -_inline void wrap( int D, int *w, int **p ) -{ - if( *p > w + D ) *p -= D + 1; // when *p = w + D + 1, it wraps around to *p = w - if( *p < w ) *p += D + 1; // when *p = w - 1, it wraps around to *p = w + D -} + // crossfade + size_t idelayoutputxf; // output pointer + int xfade; // value -// simple averaging filter for performance - a[] is 0, b[] is 1, L is # of samples to average -_inline int avg_filter( int M, int *a, int L, int *b, int *w, int x ) -{ - int i, y = 0; + int delaysamples; // delay setting + int delayfeedback; // feedback setting - w[0] = x; + // lowpass + int lp; // is lowpass enabled + int lp0, lp1, lp2; // lowpass buffer - // output adder - switch( L ) - { - default: - case 12: y += w[12]; - case 11: y += w[11]; - case 10: y += w[10]; - case 9: y += w[9]; - case 8: y += w[8]; - case 7: y += w[7]; - case 6: y += w[6]; - case 5: y += w[5]; - case 4: y += w[4]; - case 3: y += w[3]; - case 2: y += w[2]; - case 1: y += w[1]; - case 0: y += w[0]; - } - - for( i = L; i >= 1; i-- ) // reverse update internal state - w[i] = w[i-1]; + // modulation + int mod; + int modcur; - switch( L ) - { - default: - case 12: return y / 13; - case 11: return y / 12; - case 10: return y / 11; - case 9: return y / 10; - case 8: return y / 9; - case 7: return y >> 3; - case 6: return y / 7; - case 5: return y / 6; - case 4: return y / 5; - case 3: return y >> 2; - case 2: return y / 3; - case 1: return y >> 1; - case 0: return y; - } -} - -// IIR filter, cannonical form -// returns single sample y for current input value x -// x is input sample -// w = internal state vector, dimension max(M,L) + 1 -// L, M numerator and denominator filter orders -// a,b are M+1 dimensional arrays of filter params -// -// for M = 4: -// -// 1 w0(n) b0 -// x(n)--->(+)--(*)-----.------(*)->(+)---> y(n) -// ^ | ^ -// | [Delay d] | -// | | | -// | -a1 |W1 b1 | -// ----(*)---.------(*)---- -// ^ | ^ -// | [Delay d] | -// | | | -// | -a2 |W2 b2 | -// ----(*)---.------(*)---- -// ^ | ^ -// | [Delay d] | -// | | | -// | -a3 |W3 b3 | -// ----(*)---.------(*)---- -// ^ | ^ -// | [Delay d] | -// | | | -// | -a4 |W4 b4 | -// ----(*)---.------(*)---- -// -// for each input sample x, do: -// w0 = x - a1*w1 - a2*w2 - ... aMwM -// y = b0*w0 + b1*w1 + ...bL*wL -// wi = wi-1, i = K, K-1, ..., 1 - -_inline int iir_filter( int M, int *a, int L, int *b, int *w, int x ) -{ - int K, i, y, x0; - - if( M == 0 ) - return avg_filter( M, a, L, b, w, x ); - - y = 0; - x0 = x; - - K = max ( M, L ); - - // for (i = 1; i <= M; i++) // input adder - // w[0] -= ( a[i] * w[i] ) >> PBITS; - - // M is clamped between 1 and FLT_M - // change this switch statement if FLT_M changes! - - switch( M ) - { - case 12: x0 -= ( a[12] * w[12] ) >> PBITS; - case 11: x0 -= ( a[11] * w[11] ) >> PBITS; - case 10: x0 -= ( a[10] * w[10] ) >> PBITS; - case 9: x0 -= ( a[9] * w[9] ) >> PBITS; - case 8: x0 -= ( a[8] * w[8] ) >> PBITS; - case 7: x0 -= ( a[7] * w[7] ) >> PBITS; - case 6: x0 -= ( a[6] * w[6] ) >> PBITS; - case 5: x0 -= ( a[5] * w[5] ) >> PBITS; - case 4: x0 -= ( a[4] * w[4] ) >> PBITS; - case 3: x0 -= ( a[3] * w[3] ) >> PBITS; - case 2: x0 -= ( a[2] * w[2] ) >> PBITS; - default: - case 1: x0 -= ( a[1] * w[1] ) >> PBITS; - } - - w[0] = x0; - - // for( i = 0; i <= L; i++ ) // output adder - // y += ( b[i] * w[i] ) >> PBITS; - - switch( L ) - { - case 12: y += ( b[12] * w[12] ) >> PBITS; - case 11: y += ( b[11] * w[11] ) >> PBITS; - case 10: y += ( b[10] * w[10] ) >> PBITS; - case 9: y += ( b[9] * w[9] ) >> PBITS; - case 8: y += ( b[8] * w[8] ) >> PBITS; - case 7: y += ( b[7] * w[7] ) >> PBITS; - case 6: y += ( b[6] * w[6] ) >> PBITS; - case 5: y += ( b[5] * w[5] ) >> PBITS; - case 4: y += ( b[4] * w[4] ) >> PBITS; - case 3: y += ( b[3] * w[3] ) >> PBITS; - case 2: y += ( b[2] * w[2] ) >> PBITS; - default: - case 1: y += ( b[1] * w[1] ) >> PBITS; - case 0: y += ( b[0] * w[0] ) >> PBITS; - } - - for( i = K; i >= 1; i-- ) // reverse update internal state - w[i] = w[i-1]; - - return y; // current output sample -} - -// IIR filter, cannonical form, using dot product and delay implementation -// (may be easier to optimize this routine.) -_inline int iir_filter2( int M, int *a, int L, int *b, int *w, int x ) -{ - int K, y; - - K = max( M, L ); // K = max (M, L) - w[0] = 0; // needed for dot (M, a, w) - - w[0] = x - dot( M, a, w ); // input adder - y = dot( L, b, w ); // output adder - - delay( K, w ); // update delay line - - return y; // current output sample -} - - -// fir filter - no feedback = high stability but also may be more expensive computationally -_inline int fir_filter( int M, int *h, int *w, int x ) -{ - int i, y; - - w[0] = x; - - for( y = 0, i = 0; i <= M; i++ ) - y += h[i] * w[i]; - - for( i = M; i >= -1; i-- ) - w[i] = w[i-1]; - - return y; -} - -// fir filter, using dot product and delay implementation -_inline int fir_filter2( int M, int *h, int *w, int x ) -{ - int y; - - w[0] = x; - y = dot( M, h, w ); - delay( M, w ); - - return y; -} - - -// tap - i-th tap of circular delay line buffer -// D delay line size in samples -// w delay line buffer pointer, of dimension D+1 -// p circular pointer -// t = 0...D -int tap( int D, int *w, int *p, int t ) -{ - return w[(p - w + t) % (D + 1)]; -} - -// tapi - interpolated tap output of a delay line -// interpolates sample between adjacent samples in delay line for 'frac' part of delay -// D delay line size in samples -// w delay line buffer pointer, of dimension D+1 -// p circular pointer -// t - delay tap integer value 0...D. (complete delay is t.frac ) -// frac - varying 16 bit fractional delay value 0...32767 (normalized to 0.0 - 1.0) -_inline int tapi( int D, int *w, int *p, int t, int frac ) -{ - int i, j; - int si, sj; - - i = t; // tap value, interpolate between adjacent samples si and sj - j = (i + 1) % (D+1); // if i = D, then j = 0; otherwise, j = i + 1 - - si = tap( D, w, p, i ); // si(n) = x(n - i) - sj = tap( D, w, p, j ); // sj(n) = x(n - j) - - return si + (((frac) * (sj - si) ) >> 16); -} - -// circular delay line, D-fold delay -// D delay line size in samples w[0..D] -// w delay line buffer pointer, dimension D+1 -// p circular pointer -_inline void cdelay( int D, int *w, int **p ) -{ - (*p)--; // decrement pointer and wrap modulo (D+1) - wrap ( D, w, p ); // when *p = w-1, it wraps around to *p = w+D -} - -// plain reverberator with circular delay line -// D delay line size in samples -// t tap from this location - <= D -// w delay line buffer pointer of dimension D+1 -// p circular pointer, must be init to &w[0] before first call -// a feedback value, 0-PMAX (normalized to 0.0-1.0) -// b gain -// x input sample - -// w0(n) b -// x(n)--->(+)--------.-----(*)-> y(n) -// ^ | -// | [Delay d] -// | | -// | a |Wd(n) -// ----(*)---. - -_inline int dly_plain( int D, int t, int *w, int **p, int a, int b, int x ) -{ - int y, sD; - - sD = tap( D, w, *p, t ); // Tth tap delay output - y = x + (( a * sD ) >> PBITS); // filter output - **p = y; // delay input - cdelay( D, w, p ); // update delay line - - return (( y * b ) >> PBITS ); -} - -// straight delay line -// -// D delay line size in samples -// t tap from this location - <= D -// w delay line buffer pointer of dimension D+1 -// p circular pointer, must be init to &w[0] before first call -// x input sample -// -// x(n)--->[Delay d]---> y(n) -// -_inline int dly_linear ( int D, int t, int *w, int **p, int x ) -{ - int y; - - y = tap( D, w, *p, t ); // Tth tap delay output - **p = x; // delay input - cdelay( D, w, p ); // update delay line - - return y; -} - -// lowpass reverberator, replace feedback multiplier 'a' in -// plain reverberator with a low pass filter -// D delay line size in samples -// t tap from this location - <= D -// w delay line buffer pointer of dimension D+1 -// p circular pointer, must be init to &w[0] before first call -// a feedback gain -// b output gain -// M filter order -// bf filter numerator, 0-PMAX (normalized to 0.0-1.0), M+1 dimensional -// af filter denominator, 0-PMAX (normalized to 0.0-1.0), M+1 dimensional -// vf filter state, M+1 dimensional -// x input sample -// w0(n) b -// x(n)--->(+)--------------.----(*)--> y(n) -// ^ | -// | [Delay d] -// | | -// | a |Wd(n) -// --(*)--[Filter])- - -int dly_lowpass( int D, int t, int *w, int **p, int a, int b, int M, int *af, int L, int *bf, int *vf, int x ) -{ - int y, sD; - - sD = tap( D, w, *p, t ); // delay output is filter input - y = x + ((iir_filter ( M, af, L, bf, vf, sD ) * a) >> PBITS); // filter output with gain - **p = y; // delay input - cdelay( D, w, p ); // update delay line - - return (( y * b ) >> PBITS ); // output with gain -} - -// allpass reverberator with circular delay line -// D delay line size in samples -// t tap from this location - <= D -// w delay line buffer pointer of dimension D+1 -// p circular pointer, must be init to &w[0] before first call -// a feedback value, 0-PMAX (normalized to 0.0-1.0) -// b gain - -// w0(n) -a b -// x(n)--->(+)--------.-----(*)-->(+)--(*)-> y(n) -// ^ | ^ -// | [Delay d] | -// | | | -// | a |Wd(n) | -// ----(*)---.------------- -// -// for each input sample x, do: -// w0 = x + a*Wd -// y = -a*w0 + Wd -// delay (d, W) - w is the delay buffer array -// -// or, using circular delay, for each input sample x do: -// -// Sd = tap (D,w,p,D) -// S0 = x + a*Sd -// y = -a*S0 + Sd -// *p = S0 -// cdelay(D, w, &p) - -_inline int dly_allpass( int D, int t, int *w, int **p, int a, int b, int x ) -{ - int y, s0, sD; - - sD = tap( D, w, *p, t ); // Dth tap delay output - s0 = x + (( a * sD ) >> PBITS); - - y = (( -a * s0 ) >> PBITS ) + sD; // filter output - **p = s0; // delay input - cdelay( D, w, p ); // update delay line - - return (( y * b ) >> PBITS ); -} - - -/////////////////////////////////////////////////////////////////////////////////// -// fixed point math for real-time wave table traversing, pitch shifting, resampling -/////////////////////////////////////////////////////////////////////////////////// -#define FIX20_BITS 20 // 20 bits of fractional part -#define FIX20_SCALE (1 << FIX20_BITS) -#define FIX20_INTMAX ((1 << (32 - FIX20_BITS))-1) // maximum step integer -#define FLOAT_TO_FIX20(a) ((int)((a) * (float)FIX20_SCALE)) // convert float to fixed point -#define INT_TO_FIX20(a) (((int)(a)) << FIX20_BITS) // convert int to fixed point -#define FIX20_TO_FLOAT(a) ((float)(a) / (float)FIX20_SCALE) // convert fix20 to float -#define FIX20_INTPART(a) (((int)(a)) >> FIX20_BITS) // get integer part of fixed point -#define FIX20_FRACPART(a) ((a) - (((a) >> FIX20_BITS) << FIX20_BITS)) // get fractional part of fixed point -#define FIX20_FRACTION(a,b) (FIX(a)/(b)) // convert int a to fixed point, divide by b - -typedef int fix20int; - -///////////////////////////////// -// DSP processor parameter block -///////////////////////////////// - -// NOTE: these prototypes must match the XXX_Params ( prc_t *pprc ) and XXX_GetNext ( XXX_t *p, int x ) functions - -typedef void * (*prc_Param_t)( void *pprc ); // individual processor allocation functions -typedef int (*prc_GetNext_t)( void *pdata, int x ); // get next function for processor -typedef int (*prc_GetNextN_t)( void *pdata, portable_samplepair_t *pbuffer, int SampleCount, int op); // batch version of getnext -typedef void (*prc_Free_t)( void *pdata ); // free function for processor -typedef void (*prc_Mod_t)(void *pdata, float v); // modulation function for processor - -#define OP_LEFT 0 // batch process left channel in place -#define OP_RIGHT 1 // batch process right channel in place -#define OP_LEFT_DUPLICATE 2 // batch process left channel in place, duplicate to right channel - -#define PRC_NULL 0 // pass through - must be 0 -#define PRC_DLY 1 // simple feedback reverb -#define PRC_RVA 2 // parallel reverbs -#define PRC_FLT 3 // lowpass or highpass filter -#define PRC_CRS 4 // chorus -#define PRC_PTC 5 // pitch shifter -#define PRC_ENV 6 // adsr envelope -#define PRC_LFO 7 // lfo -#define PRC_EFO 8 // envelope follower -#define PRC_MDY 9 // mod delay -#define PRC_DFR 10 // diffusor - n series allpass delays -#define PRC_AMP 11 // amplifier with distortion - -#define QUA_LO 0 // quality of filter or reverb. Must be 0,1,2,3. -#define QUA_MED 1 -#define QUA_HI 2 -#define QUA_VHI 3 -#define QUA_MAX QUA_VHI - -#define CPRCPARAMS 16 // up to 16 floating point params for each processor type - -// processor definition - one for each running instance of a dsp processor -typedef struct -{ - int type; // PRC type - - float prm[CPRCPARAMS]; // dsp processor parameters - array of floats - - prc_Param_t pfnParam; // allocation function - takes ptr to prc, returns ptr to specialized data struct for proc type - prc_GetNext_t pfnGetNext; // get next function - prc_GetNextN_t pfnGetNextN; // batch version of get next - prc_Free_t pfnFree; // free function - prc_Mod_t pfnMod; // modulation function - - void *pdata; // processor state data - ie: pdly, pflt etc. -} prc_t; - -// processor parameter ranges - for validating parameters during allocation of new processor -typedef struct prm_rng_s -{ - int iprm; // parameter index - float lo; // min value of parameter - float hi; // max value of parameter -} prm_rng_t; - -void PRC_CheckParams( prc_t *pprc, prm_rng_t *prng ); - -/////////// -// Filters -/////////// - -#define CFLTS 64 // max number of filters simultaneously active -#define FLT_M 12 // max order of any filter - -#define FLT_LP 0 // lowpass filter -#define FLT_HP 1 // highpass filter -#define FTR_MAX FLT_HP - -// flt parameters - -typedef struct -{ - qboolean fused; // true if slot in use - - int b[FLT_M+1]; // filter numerator parameters (convert 0.0-1.0 to 0-PMAX representation) - int a[FLT_M+1]; // filter denominator parameters (convert 0.0-1.0 to 0-PMAX representation) - int w[FLT_M+1]; // filter state - samples (dimension of max (M, L)) - int L; // filter order numerator (dimension of a[M+1]) - int M; // filter order denominator (dimension of b[L+1]) -} flt_t; - -// flt flts -flt_t flts[CFLTS]; - -void FLT_Init( flt_t *pf ) { if( pf ) Q_memset( pf, 0, sizeof( flt_t )); } -void FLT_InitAll( void ) { int i; for( i = 0; i < CFLTS; i++ ) FLT_Init( &flts[i] ); } -void FLT_Free( flt_t *pf ) { if( pf ) Q_memset( pf, 0, sizeof( flt_t )); } -void FLT_FreeAll( void ) { int i; for( i = 0; i < CFLTS; i++ ) FLT_Free( &flts[i] ); } - - -// find a free filter from the filter pool -// initialize filter numerator, denominator b[0..M], a[0..L] -flt_t * FLT_Alloc( int M, int L, int *a, int *b ) -{ - int i, j; - flt_t *pf = NULL; - - for( i = 0; i < CFLTS; i++ ) - { - if( !flts[i].fused ) - { - pf = &flts[i]; - - // transfer filter params into filter struct - pf->M = M; - pf->L = L; - for( j = 0; j <= M; j++ ) - pf->a[j] = a[j]; - - for( j = 0; j <= L; j++ ) - pf->b[j] = b[j]; - - pf->fused = true; - break; - } - } - - ASSERT( pf ); // make sure we're not trying to alloc more than CFLTS flts - - return pf; -} - -// convert filter params cutoff and type into -// iir transfer function params M, L, a[], b[] -// iir filter, 1st order, transfer function is H(z) = b0 + b1 Z^-1 / a0 + a1 Z^-1 -// or H(z) = b0 - b1 Z^-1 / a0 + a1 Z^-1 for lowpass -// design cutoff filter at 3db (.5 gain) p579 -void FLT_Design_3db_IIR( float cutoff, float ftype, int *pM, int *pL, int *a, int *b ) -{ - // ftype: FLT_LP, FLT_HP, FLT_BP - - double Wc = M_PI2 * cutoff / SOUND_DMA_SPEED; // radians per sample - double Oc; - double fa; - double fb; - - // calculations: - // Wc = 2pi * fc/44100 convert to radians - // Oc = tan (Wc/2) * Gc / sqt ( 1 - Gc^2) get analog version, low pass - // Oc = tan (Wc/2) * (sqt (1 - Gc^2)) / Gc analog version, high pass - // Gc = 10 ^ (-Ac/20) gain at cutoff. Ac = 3db, so Gc^2 = 0.5 - // a = ( 1 - Oc ) / ( 1 + Oc ) - // b = ( 1 - a ) / 2 - - Oc = tan( Wc / 2.0 ); - - fa = ( 1.0 - Oc ) / ( 1.0 + Oc ); - - fb = ( 1.0 - fa ) / 2.0; - - if( ftype == FLT_HP ) - fb = ( 1.0 + fa ) / 2.0; - - a[0] = 0; // a0 always ignored - a[1] = (int)( -fa * PMAX ); // quantize params down to 0-PMAX >> PBITS - b[0] = (int)( fb * PMAX ); - b[1] = b[0]; - - if( ftype == FLT_HP ) - b[1] = -b[1]; - - *pM = *pL = 1; -} - - -// convolution of x[n] with h[n], resulting in y[n] -// h, x, y filter, input and output arrays (double precision) -// M = filter order, L = input length -// h is M+1 dimensional -// x is L dimensional -// y is L+M dimensional -void conv( int M, double *h, int L, double *x, double *y ) -{ - int n, m; - - for( n = 0; n < L+M; n++ ) - { - for( y[n] = 0, m = max(0, n-L+1); m <= min(n, M); m++ ) - { - y[n] += h[m] * x[n-m]; - } - } -} - -// cas2can - convert cascaded, second order section parameter arrays to -// canonical numerator/denominator arrays. Canonical implementations -// have half as many multiplies as cascaded implementations. - -// K is number of cascaded sections -// A is Kx3 matrix of sos params A[K] = A[0]..A[K-1] -// a is (2K + 1) -dimensional output of canonical params - -#define KMAX 32 // max # of sos sections - 8 is the most we should ever see at runtime - -void cas2can( int K, double A[KMAX+1][3], int *aout ) -{ - int i, j; - double d[2*KMAX + 1]; - double a[2*KMAX + 1]; - - ASSERT( K <= KMAX ); - - Q_memset( d, 0, sizeof( double ) * ( 2 * KMAX + 1 )); - Q_memset( a, 0, sizeof( double ) * ( 2 * KMAX + 1 )); - - a[0] = 1; - - for( i = 0; i < K; i++ ) - { - conv( 2, A[i], 2 * i + 1, a, d ); - - for( j = 0; j < 2 * i + 3; j++ ) - a[j] = d[j]; - } - - for( i = 0; i < (2*K + 1); i++ ) - aout[i] = a[i] * PMAX; -} - - -// chebyshev IIR design, type 2, Lowpass or Highpass - -#define lnf( e ) ( 2.303 * log10( e )) -#define acosh( e ) ( lnf( (e) + sqrt(( e ) * ( e ) - 1) )) -#define asinh( e ) ( lnf( (e) + sqrt(( e ) * ( e ) + 1) )) - - -// returns a[], b[] which are Kx3 matrices of cascaded second-order sections -// these matrices may be passed directly to the iir_cas() routine for evaluation -// Nmax - maximum order of filter -// cutoff, ftype, qwidth - filter cutoff in hz, filter type FLT_LOWPASS/HIGHPASS, qwidth in hz -// pM - denominator order -// pL - numerator order -// a - array of canonical filter params -// b - array of canonical filter params -void FLT_Design_Cheb( int Nmax, float cutoff, float ftype, float qwidth, int *pM, int *pL, int *a, int *b ) -{ -// p769 - converted from MATLAB - - double s = (ftype == FLT_LP ? 1 : -1 ); // 1 for LP, -1 for HP - double fs = SOUND_DMA_SPEED; // sampling frequency - double fpass = cutoff; // cutoff frequency - double fstop = fpass + max (2000, qwidth); // stop frequency - double Apass = 0.5; // max attenuation of pass band UNDONE: use Quality to select this - double Astop = 10; // max amplitude of stop band UNDONE: use Quality to select this - - double Wpass, Wstop, epass, estop, Nex, aa; - double W3, f3, W0, G, Wi2, W02, a1, a2, th, Wi, D, b1; - int i, K, r, N; - double A[KMAX+1][3]; // denominator output matrices, second order sections - double B[KMAX+1][3]; // numerator output matrices, second order sections - - Wpass = tan( M_PI * fpass / fs ); - Wpass = pow( Wpass, s ); - Wstop = tan( M_PI * fstop / fs ); - Wstop = pow( Wstop, s ); - - epass = sqrt( pow( (float)10.0f, (float)Apass/10.0f ) - 1 ); - estop = sqrt( pow( (float)10.0f, (float)Astop/10.0f ) - 1 ); - - // calculate filter order N - - Nex = acosh( estop/epass ) / acosh ( Wstop/Wpass ); - N = min ( ceil(Nex), Nmax ); // don't exceed Nmax for filter order - r = ( (int)N & 1); // r == 1 if N is odd - K = (N - r ) / 2; - - aa = asinh ( estop ) / N; - W3 = Wstop / cosh( acosh( estop ) / N ); - f3 = (fs / M_PI) * atan( pow( W3, s )); - - W0 = sinh( aa ) / Wstop; - W02 = W0 * W0; - - // 1st order section for N odd - if( r == 1 ) - { - G = 1 / (1 + W0); - A[0][0] = 1; A[0][1] = s * (2*G-1); A[0][2] = 0; - B[0][0] = G; B[0][1] = G * s; B[0][2] = 0; - } - else - { - A[0][0] = 1; A[0][1] = 0; A[0][2] = 0; - B[0][0] = 1; B[0][1] = 0; B[0][2] = 0; - } - - for( i = 1; i <= K ; i++ ) - { - th = M_PI * (N - 1 + 2 * i) / (2 * N); - Wi = sin( th ) / Wstop; - Wi2 = Wi * Wi; - - D = 1 - 2 * W0 * cos( th ) + W02 + Wi2; - G = ( 1 + Wi2 ) / D; - - b1 = 2 * ( 1 - Wi2 ) / ( 1 + Wi2 ); - a1 = 2 * ( 1 - W02 - Wi2) / D; - a2 = ( 1 + 2 * W0 * cos( th ) + W02 + Wi2) / D; - - A[i][0] = 1; - A[i][1] = s * a1; - A[i][2] = a2; - - B[i][0] = G; - B[i][1] = G* s* b1; - B[i][2] = G; - } - - // convert cascade parameters to canonical parameters - - cas2can( K, A, a ); - *pM = 2*K + 1; - - cas2can( K, B, b ); - *pL = 2*K + 1; -} - -// filter parameter order - -typedef enum -{ - flt_iftype, - flt_icutoff, - flt_iqwidth, - flt_iquality, - - flt_cparam // # of params -} flt_e; - -// filter parameter ranges - -prm_rng_t flt_rng[] = -{ -{ flt_cparam, 0, 0 }, // first entry is # of parameters -{ flt_iftype, 0, FTR_MAX }, // filter type FLT_LP, FLT_HP, FLT_BP (UNDONE: FLT_BP currently ignored) -{ flt_icutoff, 10, 22050 }, // cutoff frequency in hz at -3db gain -{ flt_iqwidth, 100, 11025 }, // width of BP, or steepness of LP/HP (ie: fcutoff + qwidth = -60db gain point) -{ flt_iquality, 0, QUA_MAX }, // QUA_LO, _MED, _HI 0,1,2,3 -}; - - -// convert prc float params to iir filter params, alloc filter and return ptr to it -// filter quality set by prc quality - 0,1,2 -flt_t * FLT_Params ( prc_t *pprc ) -{ - float qual = pprc->prm[flt_iquality]; - float cutoff = pprc->prm[flt_icutoff]; - float ftype = pprc->prm[flt_iftype]; - float qwidth = pprc->prm[flt_iqwidth]; - - int L = 0; // numerator order - int M = 0; // denominator order - int b[FLT_M+1]; // numerator params 0..PMAX - int a[FLT_M+1]; // denominator params 0..PMAX - - // low pass and highpass filter design - - if( (int)qual == QUA_LO ) - qual = QUA_MED; // disable lowest quality filter - check perf on lowend KDB - - switch ( (int)qual ) - { - case QUA_LO: - // lowpass averaging filter: perf KDB - ASSERT( ftype == FLT_LP ); - ASSERT( cutoff <= SOUND_DMA_SPEED ); - M = 0; - - // L is # of samples to average - - L = 0; - if( cutoff <= SOUND_DMA_SPEED / 4 ) L = 1; // 11k - if( cutoff <= SOUND_DMA_SPEED / 8 ) L = 2; // 5.5k - if( cutoff <= SOUND_DMA_SPEED / 16 ) L = 4; // 2.75k - if( cutoff <= SOUND_DMA_SPEED / 32 ) L = 8; // 1.35k - if( cutoff <= SOUND_DMA_SPEED / 64 ) L = 12; // 750hz - - break; - case QUA_MED: - // 1st order IIR filter, 3db cutoff at fc - FLT_Design_3db_IIR( cutoff, ftype, &M, &L, a, b ); - - M = bound( 1, M, FLT_M ); - L = bound( 1, L, FLT_M ); - break; - case QUA_HI: - // type 2 chebyshev N = 4 IIR - FLT_Design_Cheb( 4, cutoff, ftype, qwidth, &M, &L, a, b ); - - M = bound( 1, M, FLT_M ); - L = bound( 1, L, FLT_M ); - break; - case QUA_VHI: - // type 2 chebyshev N = 7 IIR - FLT_Design_Cheb( 8, cutoff, ftype, qwidth, &M, &L, a, b ); - - M = bound( 1, M, FLT_M ); - L = bound( 1, L, FLT_M ); - break; - } - - return FLT_Alloc( M, L, a, b ); -} - -_inline void * FLT_VParams( void *p ) -{ - PRC_CheckParams(( prc_t *)p, flt_rng ); - return (void *)FLT_Params ((prc_t *)p); -} - -_inline void FLT_Mod( void *p, float v ) -{ -} - -// get next filter value for filter pf and input x -_inline int FLT_GetNext( flt_t *pf, int x ) -{ - return iir_filter( pf->M, pf->a, pf->L, pf->b, pf->w, x ); -} - -// batch version for performance -_inline void FLT_GetNextN( flt_t *pflt, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = FLT_GetNext( pflt, pb->left ); - pb++; - } - break; - case OP_RIGHT: - while( count-- ) - { - pb->right = FLT_GetNext( pflt, pb->right ); - pb++; - } - break; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = FLT_GetNext( pflt, pb->left ); - pb++; - } - break; - } -} - -/////////////////////////////////////////////////////////////////////////// -// Positional updaters for pitch shift etc -/////////////////////////////////////////////////////////////////////////// - -// looping position within a wav, with integer and fractional parts -// used for pitch shifting, upsampling/downsampling -// 20 bits of fraction, 8+ bits of integer -typedef struct -{ - - fix20int step; // wave table whole and fractional step value - fix20int cstep; // current cummulative step value - int pos; // current position within wav table - - int D; // max dimension of array w[0...D] ie: # of samples = D+1 -} pos_t; - -// circular wrap of pointer p, relative to array w -// D max buffer index w[0...D] (count of samples in buffer is D+1) -// i circular index -_inline void POS_Wrap( int D, int *i ) -{ - if( *i > D ) *i -= D + 1; // when *pi = D + 1, it wraps around to *pi = 0 - if( *i < 0 ) *i += D + 1; // when *pi = - 1, it wraps around to *pi = D -} - -// set initial update value - fstep can have no more than 8 bits of integer and 20 bits of fract -// D is array max dimension w[0...D] (ie: size D+1) -// w is ptr to array -// p is ptr to pos_t to initialize -_inline void POS_Init( pos_t *p, int D, float fstep ) -{ - float step = fstep; - - // make sure int part of step is capped at fix20_intmax - - if( (int)step > FIX20_INTMAX ) - step = (step - (int)step) + FIX20_INTMAX; - - p->step = FLOAT_TO_FIX20( step ); // convert fstep to fixed point - p->cstep = 0; - p->pos = 0; // current update value - p->D = D; // always init to end value, in case we're stepping backwards -} - -// change step value - this is an instantaneous change, not smoothed. -_inline void POS_ChangeVal( pos_t *p, float fstepnew ) -{ - p->step = FLOAT_TO_FIX20( fstepnew ); // convert fstep to fixed point -} - -// return current integer position, then update internal position value -_inline int POS_GetNext ( pos_t *p ) -{ - // float f = FIX20_TO_FLOAT( p->cstep ); - // int i1 = FIX20_INTPART( p->cstep ); - // float f1 = FIX20_TO_FLOAT( FIX20_FRACPART( p->cstep )); - // float f2 = FIX20_TO_FLOAT( p->step ); - - p->cstep += p->step; // update accumulated fraction step value (fixed point) - p->pos += FIX20_INTPART( p->cstep ); // update pos with integer part of accumulated step - p->cstep = FIX20_FRACPART( p->cstep ); // throw away the integer part of accumulated step - - // wrap pos around either end of buffer if needed - POS_Wrap( p->D, &( p->pos )); - - // make sure returned position is within array bounds - ASSERT( p->pos <= p->D ); - - return p->pos; -} - -// oneshot position within wav -typedef struct -{ - pos_t p; // pos_t - qboolean fhitend; // flag indicating we hit end of oneshot wav -} pos_one_t; - -// set initial update value - fstep can have no more than 8 bits of integer and 20 bits of fract -// one shot position - play only once, don't wrap, when hit end of buffer, return last position -_inline void POS_ONE_Init( pos_one_t *p1, int D, float fstep ) -{ - POS_Init( &p1->p, D, fstep ) ; - - p1->fhitend = false; -} - -// return current integer position, then update internal position value -_inline int POS_ONE_GetNext( pos_one_t *p1 ) -{ - int pos; - pos_t *p0; - - pos = p1->p.pos; // return current position - - if( p1->fhitend ) - return pos; - - p0 = &(p1->p); - p0->cstep += p0->step; // update accumulated fraction step value (fixed point) - p0->pos += FIX20_INTPART( p0->cstep ); // update pos with integer part of accumulated step - //p0->cstep = SIGN(p0->cstep) * FIX20_FRACPART( p0->cstep ); - p0->cstep = FIX20_FRACPART( p0->cstep ); // throw away the integer part of accumulated step - - // if we wrapped, stop updating, always return last position - // if step value is 0, return hit end - - if( !p0->step || p0->pos < 0 || p0->pos >= p0->D ) - p1->fhitend = true; - else pos = p0->pos; - - // make sure returned value is within array bounds - ASSERT( pos <= p0->D ); - - return pos; -} - -///////////////////// -// Reverbs and delays -///////////////////// -#define CDLYS 128 // max delay lines active. Also used for lfos. - -#define DLY_PLAIN 0 // single feedback loop -#define DLY_ALLPASS 1 // feedback and feedforward loop - flat frequency response (diffusor) -#define DLY_LOWPASS 2 // lowpass filter in feedback loop -#define DLY_LINEAR 3 // linear delay, no feedback, unity gain -#define DLY_MAX DLY_LINEAR - -// delay line -typedef struct -{ - qboolean fused; // true if dly is in use - int type; // delay type - int D; // delay size, in samples - int t; // current tap, <= D - int D0; // original delay size (only relevant if calling DLY_ChangeVal) - int *p; // circular buffer pointer - int *w; // array of samples - int a; // feedback value 0..PMAX,normalized to 0-1.0 - int b; // gain value 0..PMAX, normalized to 0-1.0 - flt_t *pflt; // pointer to filter, if type DLY_LOWPASS - HANDLE h; // memory handle for sample array + // delay line + int *lpdelayline; } dly_t; -dly_t dlys[CDLYS]; // delay lines - -void DLY_Init( dly_t *pdly ) { if( pdly ) Q_memset( pdly, 0, sizeof( dly_t )); } -void DLY_InitAll( void ) { int i; for( i = 0; i < CDLYS; i++ ) DLY_Init( &dlys[i] ); } -void DLY_Free( dly_t *pdly ) +const sx_preset_t rgsxpre[] = { - // free memory buffer - if( pdly ) - { - FLT_Free( pdly->pflt ); +// -------reverb-------- -------delay-------- +// lp mod size refl rvblp delay feedback dlylp left +{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.0 }, // 0 off +{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.065, 0.1, 0.0, 0.01 }, // 1 generic +{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.02, 0.75, 0.0, 0.01 }, // 2 metalic +{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.03, 0.78, 0.0, 0.02 }, // 3 +{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.06, 0.77, 0.0, 0.03 }, // 4 +{ 0.0, 0.0, 0.05, 0.85, 1.0, 0.008, 0.96, 2.0, 0.01 }, // 5 tunnel +{ 0.0, 0.0, 0.05, 0.88, 1.0, 0.01, 0.98, 2.0, 0.02 }, // 6 +{ 0.0, 0.0, 0.05, 0.92, 1.0, 0.015, 0.995, 2.0, 0.04 }, // 7 +{ 0.0, 0.0, 0.05, 0.84, 1.0, 0.0, 0.0, 2.0, 0.012 }, // 8 chamber +{ 0.0, 0.0, 0.05, 0.9, 1.0, 0.0, 0.0, 2.0, 0.008 }, // 9 +{ 0.0, 0.0, 0.05, 0.95, 1.0, 0.0, 0.0, 2.0, 0.004 }, // 10 +{ 0.0, 0.0, 0.05, 0.7, 0.0, 0.0, 0.0, 2.0, 0.012 }, // 11 brite +{ 0.0, 0.0, 0.055, 0.78, 0.0, 0.0, 0.0, 2.0, 0.008 }, // 12 +{ 0.0, 0.0, 0.05, 0.86, 0.0, 0.0, 0.0, 2.0, 0.002 }, // 13 +{ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.01 }, // 14 water +{ 1.0, 0.0, 0.0, 0.0, 1.0, 0.06, 0.85, 2.0, 0.02 }, // 15 +{ 1.0, 0.0, 0.0, 0.0, 1.0, 0.2, 0.6, 2.0, 0.05 }, // 16 +{ 0.0, 0.0, 0.05, 0.8, 1.0, 0.0, 0.48, 2.0, 0.016 }, // 17 concrete +{ 0.0, 0.0, 0.06, 0.9, 1.0, 0.0, 0.52, 2.0, 0.01 }, // 18 +{ 0.0, 0.0, 0.07, 0.94, 1.0, 0.3, 0.6, 2.0, 0.008 }, // 19 +{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.3, 0.42, 2.0, 0.0 }, // 20 outside +{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.35, 0.48, 2.0, 0.0 }, // 21 +{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.38, 0.6, 2.0, 0.0 }, // 22 +{ 0.0, 0.0, 0.05, 0.9, 1.0, 0.2, 0.28, 0.0, 0.0 }, // 23 cavern +{ 0.0, 0.0, 0.07, 0.9, 1.0, 0.3, 0.4, 0.0, 0.0 }, // 24 +{ 0.0, 0.0, 0.09, 0.9, 1.0, 0.35, 0.5, 0.0, 0.0 }, // 25 +{ 0.0, 1.0, 0.01, 0.9, 0.0, 0.0, 0.0, 2.0, 0.05 }, // 26 weirdo +{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.009, 0.999, 2.0, 0.04 }, // 27 +{ 0.0, 0.0, 0.001, 0.999, 0.0, 0.2, 0.8, 2.0, 0.05 } // 28 +}; - if( pdly->w ) - { - GlobalUnlock( pdly->h ); - GlobalFree( pdly->h ); - } - - // free dly slot - Q_memset( pdly, 0, sizeof( dly_t )); +// cvars +convar_t *dsp_off; // disable dsp +convar_t *roomwater_type; // water room_type +convar_t *room_type; // current room type + +// underwater/special fx modulations +convar_t *sxmod_mod; +convar_t *sxmod_lowpass; + +// stereo delay(no feedback) +convar_t *sxste_delay; // straight left delay + +// mono reverb +convar_t *sxrvb_lp; // lowpass +convar_t *sxrvb_feedback; // reverb decay. Higher -- longer +convar_t *sxrvb_size; // room size. Higher -- larger + +// mono delay +convar_t *sxdly_lp; // lowpass +convar_t *sxdly_feedback; // cycles +convar_t *sxdly_delay; // current delay in seconds + +convar_t *dsp_room; // for compability +int idsp_room; +int room_typeprev; + +// routines +int sxamodl, sxamodr; // amplitude modulation values +int sxamodlt, sxamodrt; // modulation targets +int sxhires, sxhiresprev; +int sxmod1, sxmod2; +int sxmod1cur, sxmod2cur; + +portable_samplepair_t *paintto = NULL; + +dly_t rgsxdly[MAXDLY]; // stereo is last +int rgsxlp[MAXLP]; + +void SX_Profiling_f( void ); + +/* +============ +SX_ReloadRoomFX + +============ +*/ +void SX_ReloadRoomFX( void ) +{ + if( !dsp_room ) return; // not initialized + + sxste_delay->modified = true; + sxrvb_feedback->modified = true; + sxdly_delay->modified = true; + room_type->modified = true; +} + +/* +============ +SX_Init() + +Starts sound crackling system +============ +*/ +void SX_Init( void ) +{ + Q_memset( rgsxdly, 0, sizeof( rgsxdly ) ); + Q_memset( rgsxlp, 0, sizeof( rgsxlp ) ); + + sxamodr = sxamodl = sxamodrt = sxamodlt = 255; + + sxhires = sxhiresprev = 0; + + sxmod1cur = sxmod1 = 350 * ( SOUND_DMA_SPEED / SOUND_11k ); + sxmod2cur = sxmod2 = 450 * ( SOUND_DMA_SPEED / SOUND_11k ); + + dsp_off = Cvar_Get( "dsp_off", "0", 0, "disable DSP processing" ); + roomwater_type = Cvar_Get( "waterroom_type", "14", 0, "water room type" ); + room_type = Cvar_Get( "room_type", "0", 0, "current room type preset" ); + + sxmod_lowpass = Cvar_Get( "room_lp", "0", 0, "for water fx, lowpass for entire room" ); + sxmod_mod = Cvar_Get( "room_mod", "0", 0, "stereo amptitude modulation for room" ); + + sxrvb_size = Cvar_Get( "room_size", "0", 0, "reverb: initial reflection size" ); + sxrvb_feedback = Cvar_Get( "room_refl", "0", 0, "reverb: decay time" ); + sxrvb_lp = Cvar_Get( "room_rvblp", "1", 0, "reverb: low pass filtering level" ); + + sxdly_delay = Cvar_Get( "room_delay", "0.8", 0, "mono delay: delay time" ); + sxdly_feedback = Cvar_Get( "room_feedback", "0.2", 0, "mono delay: decay time" ); + sxdly_lp = Cvar_Get( "room_dlylp", "1", 0, "mono delay: low pass filtering level" ); + + sxste_delay = Cvar_Get( "room_left", "0", 0, "left channel delay time" ); + + Cmd_AddCommand( "dsp_profile", SX_Profiling_f, "dsp stress-test, first argument is room_type" ); + + // for compability + dsp_room = room_type; + SX_ReloadRoomFX(); +} + +/* +=========== +DLY_Free + +Free memory allocated for DSP +=========== +*/ +void DLY_Free( int idelay ) +{ + Assert( idelay >= 0 && idelay < MAXDLY ); + + if( rgsxdly[idelay].lpdelayline ) + { + Z_Free( rgsxdly[idelay].lpdelayline ); + rgsxdly[idelay].lpdelayline = NULL; } } +/* +========== +SX_Shutdown -void DLY_FreeAll( void ) { int i; for( i = 0; i < CDLYS; i++ ) DLY_Free( &dlys[i] ); } - -// set up 'b' gain parameter of feedback delay to -// compensate for gain caused by feedback. -void DLY_SetNormalizingGain( dly_t *pdly ) +Stop DSP processor +========== +*/ +void SX_Free( void ) { - // compute normalized gain, set as output gain - - // calculate gain of delay line with feedback, and use it to - // reduce output. ie: force delay line with feedback to unity gain - - // for constant input x with feedback fb: - - // out = x + x*fb + x * fb^2 + x * fb^3... - // gain = out/x - // so gain = 1 + fb + fb^2 + fb^3... - // which, by the miracle of geometric series, equates to 1/1-fb - // thus, gain = 1/(1-fb) - - float fgain = 0; - float gain; - int b; - - // if b is 0, set b to PMAX (1) - b = pdly->b ? pdly->b : PMAX; - - // fgain = b * (1.0 / (1.0 - (float)pdly->a / (float)PMAX)) / (float)PMAX; - fgain = (1.0 / (1.0 - (float)pdly->a / (float)PMAX )); - - // compensating gain - multiply rva output by gain then >> PBITS - gain = (int)((1.0 / fgain) * PMAX); - - gain = gain * 4; // compensate for fact that gain calculation is for +/- 32767 amplitude wavs - // ie: ok to allow a bit more gain because most wavs are not at theoretical peak amplitude at all times - - gain = min( gain, PMAX ); // cap at PMAX - gain = ((float)b/(float)PMAX) * gain; // scale final gain by pdly->b. - - pdly->b = (int)gain; -} - -// allocate a new delay line -// D number of samples to delay -// a feedback value (0-PMAX normalized to 0.0-1.0) -// b gain value (0-PMAX normalized to 0.0-1.0) -// if DLY_LOWPASS: -// L - numerator order of filter -// M - denominator order of filter -// fb - numerator params, M+1 -// fa - denominator params, L+1 - -dly_t * DLY_AllocLP( int D, int a, int b, int type, int M, int L, int *fa, int *fb ) -{ - HANDLE h; - int cb; - int *w; int i; - dly_t *pdly = NULL; - // find open slot - for( i = 0; i < CDLYS; i++ ) + for( i = 0; i <= 3; i++ ) + DLY_Free( i ); + + Cmd_RemoveCommand( "dsp_profile" ); +} + + +/* +=========== +DLY_Init + +Initialize dly +=========== +*/ +int DLY_Init( int idelay, float delay ) +{ + dly_t *cur; + + // 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 * SOUND_DMA_SPEED) << sxhires) + 1; + cur->lpdelayline = (int *)Z_Malloc( cur->cdelaysamplesmax * sizeof( int )); + cur->xfade = 0; + + // init modulation + cur->mod = cur->modcur = 0; + + // init lowpass + cur->lp = 1; + cur->lp0 = cur->lp1 = cur->lp2 = 0; + + cur->idelayinput = 0; + cur->idelayoutput = cur->cdelaysamplesmax - cur->delaysamples; // NOTE: delaysamples must be set!!! + + + return 1; +} + +/* +============ +DLY_MovePointer + +Checks overflow and moves pointer +============ +*/ +_inline void DLY_MovePointer( dly_t *dly ) +{ + if( ++dly->idelayinput >= dly->cdelaysamplesmax ) + dly->idelayinput = 0; + + if( ++dly->idelayoutput >= dly->cdelaysamplesmax ) + dly->idelayoutput = 0; +} + +/* +============= +DLY_CheckNewStereoDelayVal + +Update stereo processor settings if we are in new room +============= +*/ +void DLY_CheckNewStereoDelayVal( void ) +{ + dly_t *const dly = &rgsxdly[STEREODLY]; + float delay = sxste_delay->value; + + if( !sxste_delay->modified ) + return; + + if( delay == 0 ) { - if( !dlys[i].fused ) - { - pdly = &dlys[i]; - DLY_Init( pdly ); - break; - } - } - - if( i == CDLYS ) - { - MsgDev( D_WARN, "DSP: failed to allocate delay line.\n" ); - return NULL; // all delay lines in use - } - - cb = (D + 1) * sizeof( int ); // assume all samples are signed integers - - if( type == DLY_LOWPASS ) - { - // alloc lowpass fir_filter - pdly->pflt = FLT_Alloc( M, L, fa, fb ); - if( !pdly->pflt ) - { - MsgDev( D_WARN, "DSP: failed to allocate filter for delay line.\n" ); - return NULL; - } - } - - // alloc delay memory - h = GlobalAlloc( GMEM_MOVEABLE|GMEM_SHARE, cb ); - if( !h ) - { - MsgDev( D_ERROR, "Sound DSP: Out of memory.\n" ); - FLT_Free( pdly->pflt ); - return NULL; - } - - // lock delay memory - w = (int *)GlobalLock( h ); - - if( !w ) - { - MsgDev( D_ERROR, "Sound DSP: Failed to lock.\n" ); - GlobalFree( h ); - FLT_Free( pdly->pflt ); - return NULL; - } - - // clear delay array - Q_memset( w, 0, cb ); - - // init values - pdly->type = type; - pdly->D = D; - pdly->t = D; // set delay tap to full delay - pdly->D0 = D; - pdly->p = w; // init circular pointer to head of buffer - pdly->w = w; - pdly->h = h; - pdly->a = min( a, PMAX ); // do not allow 100% feedback - pdly->b = b; - pdly->fused = true; - - if( type == DLY_LINEAR ) - { - // linear delay has no feedback and unity gain - pdly->a = 0; - pdly->b = PMAX; + DLY_Free( STEREODLY ); } else { - // adjust b to compensate for feedback gain - DLY_SetNormalizingGain( pdly ); + int samples; + + delay = min( delay, MAX_STEREO_DELAY ); + samples = (int)(delay * SOUND_DMA_SPEED) << sxhires; + + // re-init dly + if( !dly->lpdelayline ) + { + dly->delaysamples = samples; + DLY_Init( STEREODLY, MAX_STEREO_DELAY ); + } + + if( dly->delaysamples != samples ) + { + dly->xfade = 128; + dly->idelayoutputxf = dly->idelayinput - samples; + if( dly->idelayoutputxf < 0 ) + dly->idelayoutputxf += dly->cdelaysamplesmax; + } + + dly->modcur = dly->mod = 0; + + if( dly->delaysamples == 0 ) + DLY_Free( STEREODLY ); } - return pdly; + sxste_delay->modified = false; } -// allocate lowpass or allpass delay -dly_t * DLY_Alloc( int D, int a, int b, int type ) +/* +============= +DLY_DoStereoDelay + +Do stereo processing +============= +*/ +void DLY_DoStereoDelay( int count ) { - return DLY_AllocLP( D, a, b, type, 0, 0, 0, 0 ); -} + int delay, samplexf; + dly_t *const dly = &rgsxdly[STEREODLY]; + portable_samplepair_t *paint = paintto; + if( !dly->lpdelayline ) + return; // inactive -// Allocate new delay, convert from float params in prc preset to internal parameters -// Uses filter params in prc if delay is type lowpass - -// delay parameter order -typedef enum -{ - dly_idtype, // NOTE: first 8 params must match those in mdy_e - dly_idelay, - dly_ifeedback, - dly_igain, - dly_iftype, - dly_icutoff, - dly_iqwidth, - dly_iquality, - dly_cparam -} dly_e; - - -// delay parameter ranges -prm_rng_t dly_rng[] = -{ -{ dly_cparam, 0, 0 }, // first entry is # of parameters - -// delay params -{ dly_idtype, 0, DLY_MAX }, // delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS -{ dly_idelay, 0.0, 1000.0 }, // delay in milliseconds -{ dly_ifeedback, 0.0, 0.99 }, // feedback 0-1.0 -{ dly_igain, 0.0, 1.0 }, // final gain of output stage, 0-1.0 - -// filter params if dly type DLY_LOWPASS -{ dly_iftype, 0, FTR_MAX }, -{ dly_icutoff, 10.0, 22050.0 }, -{ dly_iqwidth, 100.0, 11025.0 }, -{ dly_iquality, 0, QUA_MAX }, -}; - -dly_t * DLY_Params( prc_t *pprc ) -{ - dly_t *pdly = NULL; - int D, a, b; - - float delay = pprc->prm[dly_idelay]; - float feedback = pprc->prm[dly_ifeedback]; - float gain = pprc->prm[dly_igain]; - int type = pprc->prm[dly_idtype]; - - float ftype = pprc->prm[dly_iftype]; - float cutoff = pprc->prm[dly_icutoff]; - float qwidth = pprc->prm[dly_iqwidth]; - float qual = pprc->prm[dly_iquality]; - - D = MSEC_TO_SAMPS( delay ); // delay samples - a = feedback * PMAX; // feedback - b = gain * PMAX; // gain - - switch( type ) + for( ; count; count--, paint++ ) { - case DLY_PLAIN: - case DLY_ALLPASS: - case DLY_LINEAR: - pdly = DLY_Alloc( D, a, b, type ); - break; - case DLY_LOWPASS: - { - // set up dummy lowpass filter to convert params - prc_t prcf; - flt_t *pflt; + if( dly->mod && --dly->modcur < 0 ) + dly->modcur = dly->mod; - // 0,1,2 - high, medium, low (low quality implies faster execution time) - prcf.prm[flt_iquality] = qual; - prcf.prm[flt_icutoff] = cutoff; - prcf.prm[flt_iftype] = ftype; - prcf.prm[flt_iqwidth] = qwidth; - - pflt = (flt_t *)FLT_Params( &prcf ); - - if( !pflt ) + delay = dly->lpdelayline[dly->idelayoutput]; + + // process only if crossfading, active left value or delayline + if( delay || paint->left || dly->xfade ) + { + // set up new crossfade, if not crossfading, not modulating, but going to + if( !dly->xfade && !dly->modcur && dly->mod ) { - MsgDev( D_WARN, "DSP: failed to allocate filter.\n" ); - return NULL; + dly->idelayoutputxf = dly->idelayoutput + ((Com_RandomLong( 0, 255 ) * dly->delaysamples ) >> 9 ); + + dly->xfade = 128; } - pdly = DLY_AllocLP( D, a, b, type, pflt->M, pflt->L, pflt->a, pflt->b ); + dly->idelayoutputxf %= dly->cdelaysamplesmax; - FLT_Free( pflt ); - break; - } - } - return pdly; -} - -_inline void *DLY_VParams( void *p ) -{ - PRC_CheckParams(( prc_t *)p, dly_rng ); - return (void *) DLY_Params((prc_t *)p); -} - -// get next value from delay line, move x into delay line -int DLY_GetNext( dly_t *pdly, int x ) -{ - switch( pdly->type ) - { - default: - case DLY_PLAIN: - return dly_plain( pdly->D, pdly->t, pdly->w, &pdly->p, pdly->a, pdly->b, x ); - case DLY_ALLPASS: - return dly_allpass( pdly->D, pdly->t, pdly->w, &pdly->p, pdly->a, pdly->b, x ); - case DLY_LOWPASS: - return dly_lowpass( pdly->D, pdly->t, pdly->w, &(pdly->p), pdly->a, pdly->b, pdly->pflt->M, pdly->pflt->a, pdly->pflt->L, pdly->pflt->b, pdly->pflt->w, x ); - case DLY_LINEAR: - return dly_linear( pdly->D, pdly->t, pdly->w, &pdly->p, x ); - } -} - -// batch version for performance -void DLY_GetNextN( dly_t *pdly, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = DLY_GetNext( pdly, pb->left ); - pb++; - } - break; - case OP_RIGHT: - while( count-- ) - { - pb->right = DLY_GetNext( pdly, pb->right ); - pb++; - } - break; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = DLY_GetNext( pdly, pb->left ); - pb++; - } - break; - } -} - -// get tap on t'th sample in delay - don't update buffer pointers, this is done via DLY_GetNext -_inline int DLY_GetTap( dly_t *pdly, int t ) -{ - return tap( pdly->D, pdly->w, pdly->p, t ); -} - - -// make instantaneous change to new delay value D. -// t tap value must be <= original D (ie: we don't do any reallocation here) -void DLY_ChangeVal( dly_t *pdly, int t ) -{ - // never set delay > original delay - pdly->t = min( t, pdly->D0 ); -} - -// ignored - use MDY_ for modulatable delay -_inline void DLY_Mod( void *p, float v ) -{ -} - -/////////////////// -// Parallel reverbs -/////////////////// - -// Reverb A -// M parallel reverbs, mixed to mono output - -#define CRVAS 64 // max number of parallel series reverbs active -#define CRVA_DLYS 12 // max number of delays making up reverb_a - -typedef struct -{ - qboolean fused; - int m; // number of parallel plain or lowpass delays - int fparallel; // true if filters in parallel with delays, otherwise single output filter - flt_t *pflt; - - dly_t *pdlys[CRVA_DLYS]; // array of pointers to delays -} rva_t; - -rva_t rvas[CRVAS]; - -void RVA_Init( rva_t *prva ) { if( prva ) Q_memset( prva, 0, sizeof( rva_t )); } -void RVA_InitAll( void ) { int i; for( i = 0; i < CRVAS; i++ ) RVA_Init( &rvas[i] ); } - -// free parallel series reverb -void RVA_Free( rva_t *prva ) -{ - if( prva ) - { - int i; - - // free all delays - for( i = 0; i < CRVA_DLYS; i++) - DLY_Free ( prva->pdlys[i] ); - - FLT_Free( prva->pflt ); - Q_memset( prva, 0, sizeof (rva_t) ); - } -} - - -void RVA_FreeAll( void ) { int i; for( i = 0; i < CRVAS; i++ ) RVA_Free( &rvas[i] ); } - -// create parallel reverb - m parallel reverbs summed -// D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples) -// a array of reverb feedback parms for parallel reverbs (CRVB_P_DLYS) -// b array of CRVB_P_DLYS - mix params for parallel reverbs -// m - number of parallel delays -// pflt - filter template, to be used by all parallel delays -// fparallel - true if filter operates in parallel with delays, otherwise filter output only -rva_t *RVA_Alloc( int *D, int *a, int *b, int m, flt_t *pflt, int fparallel ) -{ - int i; - rva_t *prva; - flt_t *pflt2 = NULL; - - // find open slot - for( i = 0; i < CRVAS; i++ ) - { - if( !rvas[i].fused ) - break; - } - - // return null if no free slots - if( i == CRVAS ) - { - MsgDev( D_WARN, "DSP: failed to allocate reverb.\n" ); - return NULL; - } - - prva = &rvas[i]; - - // if series filter specified, alloc - if( pflt && !fparallel ) - { - // use filter data as template for a filter on output - pflt2 = FLT_Alloc( pflt->M, pflt->L, pflt->a, pflt->b ); - - if( !pflt2 ) - { - MsgDev( D_WARN, "DSP: failed to allocate flt for reverb.\n" ); - return NULL; - } - } - - // alloc parallel reverbs - if( pflt && fparallel ) - { - - // use this filter data as a template to alloc a filter for each parallel delay - for( i = 0; i < m; i++ ) - prva->pdlys[i] = DLY_AllocLP( D[i], a[i], b[i], DLY_LOWPASS, pflt->M, pflt->L, pflt->a, pflt->b ); - } - else - { - // no filter specified, use plain delays in parallel sections - for( i = 0; i < m; i++ ) - prva->pdlys[i] = DLY_Alloc( D[i], a[i], b[i], DLY_PLAIN ); - } - - - // if we failed to alloc any reverb, free all, return NULL - for( i = 0; i < m; i++ ) - { - if( !prva->pdlys[i] ) - { - FLT_Free( pflt2 ); - RVA_Free( prva ); - MsgDev( D_WARN, "DSP: failed to allocate delay for reverb.\n" ); - return NULL; - } - } - - prva->fused = true; - prva->m = m; - prva->fparallel = fparallel; - prva->pflt = pflt2; - - return prva; -} - - -// parallel reverberator -// -// for each input sample x do: -// x0 = plain(D0,w0,&p0,a0,x) -// x1 = plain(D1,w1,&p1,a1,x) -// x2 = plain(D2,w2,&p2,a2,x) -// x3 = plain(D3,w3,&p3,a3,x) -// y = b0*x0 + b1*x1 + b2*x2 + b3*x3 -// -// rgdly - array of 6 delays: -// D - Delay values (typical - 29, 37, 44, 50, 27, 31) -// w - array of delayed values -// p - array of pointers to circular delay line pointers -// a - array of 6 feedback values (typical - all equal, like 0.75 * PMAX) -// b - array of 6 gain values for plain reverb outputs (1, .9, .8, .7) -// xin - input value -// if fparallel, filters are built into delays, -// otherwise, filter output - -_inline int RVA_GetNext( rva_t *prva, int x ) -{ - int m = prva->m; - int i, y, sum; - - sum = 0; - - for( i = 0; i < m; i++ ) - sum += DLY_GetNext( prva->pdlys[i], x ); - - // m is clamped between RVA_BASEM & CRVA_DLYS - - if( m ) y = sum/m; - else y = x; -#if 0 - // PERFORMANCE: - // UNDONE: build as array - int mm; - - switch( m ) - { - case 12: mm = (PMAX/12); break; - case 11: mm = (PMAX/11); break; - case 10: mm = (PMAX/10); break; - case 9: mm = (PMAX/9); break; - case 8: mm = (PMAX/8); break; - case 7: mm = (PMAX/7); break; - case 6: mm = (PMAX/6); break; - case 5: mm = (PMAX/5); break; - case 4: mm = (PMAX/4); break; - case 3: mm = (PMAX/3); break; - case 2: mm = (PMAX/2); break; - default: - case 1: mm = (PMAX/1); break; - } - - y = (sum * mm) >> PBITS; - -#endif // 0 - - // run series filter if present - if( prva->pflt && !prva->fparallel ) - y = FLT_GetNext( prva->pflt, y ); - - return y; -} - -// batch version for performance -_inline void RVA_GetNextN( rva_t *prva, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = RVA_GetNext( prva, pb->left ); - pb++; - } - break; - case OP_RIGHT: - while( count-- ) - { - pb->right = RVA_GetNext( prva, pb->right ); - pb++; - } - break; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = RVA_GetNext( prva, pb->left ); - pb++; - } - break; - } -} - -#define RVA_BASEM 3 // base number of parallel delays - -// nominal delay and feedback values - -//float rvadlys[] = { 29, 37, 44, 50, 62, 75, 96, 118, 127, 143, 164, 175 }; -float rvadlys[] = { 18, 23, 28, 36, 47, 21, 26, 33, 40, 49, 45, 38 }; -float rvafbs[] = { 0.7, 0.7, 0.7, 0.8, 0.8, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9 }; - -// reverb parameter order -typedef enum -{ - // parameter order - rva_isize, - rva_idensity, - rva_idecay, - rva_iftype, - rva_icutoff, - rva_iqwidth, - rva_ifparallel, - rva_cparam // # of params -} rva_e; - -// filter parameter ranges -prm_rng_t rva_rng[] = -{ -{ rva_cparam, 0, 0 }, // first entry is # of parameters - -// reverb params -{ rva_isize, 0.0, 2.0 }, // 0-2.0 scales nominal delay parameters (starting at approx 20ms) -{ rva_idensity, 0.0, 2.0 }, // 0-2.0 density of reverbs (room shape) - controls # of parallel or series delays -{ rva_idecay, 0.0, 2.0 }, // 0-2.0 scales feedback parameters (starting at approx 0.15) - -// filter params for each parallel reverb (quality set to 0 for max execution speed) -{ rva_iftype, 0, FTR_MAX }, -{ rva_icutoff, 10, 22050 }, -{ rva_iqwidth, 100, 11025 }, -{ rva_ifparallel, 0, 1 } // if 1, then all filters operate in parallel with delays. otherwise filter output only -}; - -rva_t * RVA_Params( prc_t *pprc ) -{ - flt_t *pflt; - rva_t *prva; - float size = pprc->prm[rva_isize]; // 0-2.0 controls scale of delay parameters - float density = pprc->prm[rva_idensity]; // 0-2.0 density of reverbs (room shape) - controls # of parallel delays - float decay = pprc->prm[rva_idecay]; // 0-1.0 controls feedback parameters - - float ftype = pprc->prm[rva_iftype]; - float cutoff = pprc->prm[rva_icutoff]; - float qwidth = pprc->prm[rva_iqwidth]; - - float fparallel = pprc->prm[rva_ifparallel]; - - // D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples) - // a array of reverb feedback parms for parallel delays - // b array of CRVB_P_DLYS - mix params for parallel reverbs - // m - number of parallel delays - - int D[CRVA_DLYS]; - int a[CRVA_DLYS]; - int b[CRVA_DLYS]; - int m = RVA_BASEM; - int i; - - m = density * CRVA_DLYS / 2; - - // limit # delays 3-12 - m = bound( RVA_BASEM, m, CRVA_DLYS ); - - // average time sound takes to travel from most distant wall - // (cap at 1000 ft room) - for( i = 0; i < m; i++ ) - { - // delays of parallel reverb - D[i] = MSEC_TO_SAMPS( rvadlys[i] * size ); - - // feedback and gain of parallel reverb - a[i] = (int)min( 0.9 * PMAX, rvafbs[i] * (float)PMAX * decay ); - b[i] = PMAX; - } - - // add filter - pflt = NULL; - - if( cutoff ) - { - // set up dummy lowpass filter to convert params - prc_t prcf; - - prcf.prm[flt_iquality] = QUA_LO; // force filter to low quality for faster execution time - prcf.prm[flt_icutoff] = cutoff; - prcf.prm[flt_iftype] = ftype; - prcf.prm[flt_iqwidth] = qwidth; - - pflt = (flt_t *)FLT_Params( &prcf ); - } - - prva = RVA_Alloc( D, a, b, m, pflt, fparallel ); - FLT_Free( pflt ); - - return prva; -} - -_inline void *RVA_VParams( void *p ) -{ - PRC_CheckParams((prc_t *)p, rva_rng ); - return (void *)RVA_Params((prc_t *)p ); -} - -_inline void RVA_Mod( void *p, float v ) -{ -} - - -//////////// -// Diffusor -/////////// - -// (N series allpass reverbs) -#define CDFRS 64 // max number of series reverbs active -#define CDFR_DLYS 16 // max number of delays making up diffusor - -typedef struct -{ - qboolean fused; - int n; // series allpass delays - int w[CDFR_DLYS]; // internal state array for series allpass filters - dly_t *pdlys[CDFR_DLYS]; // array of pointers to delays -} dfr_t; - -dfr_t dfrs[CDFRS]; - -void DFR_Init( dfr_t *pdfr ) { if( pdfr ) Q_memset( pdfr, 0, sizeof( dfr_t )); } -void DFR_InitAll( void ) { int i; for( i = 0; i < CDFRS; i++ ) DFR_Init ( &dfrs[i] ); } - -// free parallel series reverb -void DFR_Free( dfr_t *pdfr ) -{ - if( pdfr ) - { - int i; - - // free all delays - for( i = 0; i < CDFR_DLYS; i++ ) - DLY_Free( pdfr->pdlys[i] ); - - Q_memset( pdfr, 0, sizeof( dfr_t )); - } -} - - -void DFR_FreeAll( void ) { int i; for( i = 0; i < CDFRS; i++ ) DFR_Free( &dfrs[i] ); } - -// create n series allpass reverbs -// D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples) -// a array of reverb feedback parms for series delays -// b array of gain params for parallel reverbs -// n - number of series delays - -dfr_t *DFR_Alloc( int *D, int *a, int *b, int n ) -{ - int i; - dfr_t *pdfr; - - // find open slot - for( i = 0; i < CDFRS; i++ ) - { - if( !dfrs[i].fused ) - break; - } - - // return null if no free slots - if( i == CDFRS ) - { - MsgDev( D_WARN, "DSP: failed to allocate diffusor.\n" ); - return NULL; - } - - pdfr = &dfrs[i]; - - DFR_Init( pdfr ); - - // alloc reverbs - for( i = 0; i < n; i++ ) - pdfr->pdlys[i] = DLY_Alloc( D[i], a[i], b[i], DLY_ALLPASS ); - - // if we failed to alloc any reverb, free all, return NULL - for( i = 0; i < n; i++ ) - { - if( !pdfr->pdlys[i] ) - { - DFR_Free( pdfr ); - MsgDev( D_WARN, "DSP: failed to allocate delay for diffusor.\n" ); - return NULL; - } - } - - pdfr->fused = true; - pdfr->n = n; - - return pdfr; -} - -// series reverberator -_inline int DFR_GetNext( dfr_t *pdfr, int x ) -{ - int i, y; - int n = pdfr->n; - - y = x; - for( i = 0; i < n; i++ ) - y = DLY_GetNext( pdfr->pdlys[i], y ); - return y; - -#if 0 - // alternate method, using internal state - causes PREDELAY = sum of delay times - - int *v = pdfr->w; // intermediate results - - v[0] = x; - - // reverse evaluate series delays - // w[0] w[1] w[2] w[n-1] w[n] - // x---->D[0]--->D[1]--->D[2]...-->D[n-1]--->out - // - - for( i = n; i > 0; i-- ) - v[i] = DLY_GetNext( pdfr->pdlys[i-1], v[i-1] ); - - return v[n]; -#endif -} - -// batch version for performance -_inline void DFR_GetNextN( dfr_t *pdfr, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = DFR_GetNext( pdfr, pb->left ); - pb++; - } - break; - case OP_RIGHT: - while( count-- ) - { - pb->right = DFR_GetNext( pdfr, pb->right ); - pb++; - } - break; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = DFR_GetNext( pdfr, pb->left ); - pb++; - } - break; - } -} - -#define DFR_BASEN 2 // base number of series allpass delays - -// nominal diffusor delay and feedback values -//float dfrdlys[] = { 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95 }; -float dfrdlys[] = { 13, 19, 26, 21, 32, 36, 38, 16, 24, 28, 41, 35, 10, 46, 50, 27 }; -float dfrfbs[] = { 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15 }; - - -// diffusor parameter order - -typedef enum -{ - // parameter order - dfr_isize, - dfr_idensity, - dfr_idecay, - dfr_cparam // # of params - -} dfr_e; - -// diffusor parameter ranges - -prm_rng_t dfr_rng[] = -{ -{ dfr_cparam, 0, 0 }, // first entry is # of parameters -{ dfr_isize, 0.0, 1.0 }, // 0-1.0 scales all delays -{ dfr_idensity, 0.0, 1.0 }, // 0-1.0 controls # of series delays -{ dfr_idecay, 0.0, 1.0 }, // 0-1.0 scales all feedback parameters -}; - -dfr_t *DFR_Params( prc_t *pprc ) -{ - dfr_t *pdfr; - int i, s; - float size = pprc->prm[dfr_isize]; // 0-1.0 scales all delays - float density = pprc->prm[dfr_idensity]; // 0-1.0 controls # of series delays - float diffusion = pprc->prm[dfr_idecay]; // 0-1.0 scales all feedback parameters - - // D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples) - // a array of reverb feedback parms for series delays (CRVB_S_DLYS) - // b gain of each reverb section - // n - number of series delays - - int D[CDFR_DLYS]; - int a[CDFR_DLYS]; - int b[CDFR_DLYS]; - int n = DFR_BASEN; - - // increase # of series diffusors with increased density - n += density * 2; - - // limit m, n to half max number of delays - n = min( CDFR_DLYS / 2, n ); - - // compute delays for diffusors - for( i = 0; i < n; i++ ) - { - s = (int)( dfrdlys[i] * size ); - - // delay of diffusor - D[i] = MSEC_TO_SAMPS( s ); - - // feedback and gain of diffusor - a[i] = min( 0.9 * PMAX, dfrfbs[i] * PMAX * diffusion ); - b[i] = PMAX; - } - - pdfr = DFR_Alloc( D, a, b, n ); - - return pdfr; -} - -_inline void *DFR_VParams( void *p ) -{ - PRC_CheckParams((prc_t *)p, dfr_rng ); - return (void *)DFR_Params((prc_t *)p ); -} - -_inline void DFR_Mod( void *p, float v ) -{ -} - -////////////////////// -// LFO wav definitions -////////////////////// - -#define CLFOSAMPS 512 // samples per wav table - single cycle only -#define LFOBITS 14 // bits of peak amplitude of lfo wav -#define LFOAMP ((1<pdly ); - - Q_memset( plw, 0, sizeof( lfowav_t )); -} - -// deallocate all lfo wave tables. Called only when sound engine exits. -void LFOWAV_FreeAll( void ) -{ - int i; - - for( i = 0; i < CLFOWAV; i++ ) - LFOWAV_Free( &lfowavs[i] ); -} - -// fill lfo array w with count samples of lfo type 'type' -// all lfo wavs except fade out, rnd, and log_out should start with 0 output -void LFOWAV_Fill( int *w, int count, int type ) -{ - int i,x; - - switch( type ) - { - default: - case LFO_SIN: // sine wav, all values 0 <= x <= LFOAMP, initial value = 0 - for( i = 0; i < count; i++ ) - { - x = ( int )(( float)(LFOAMP) * sin( (M_PI2 * (float)i / (float)count ) + ( M_PI_F * 1.5 ))); - w[i] = (x + LFOAMP)/2; - } - break; - case LFO_TRI: // triangle wav, all values 0 <= x <= LFOAMP, initial value = 0 - for( i = 0; i < count; i++ ) - { - w[i] = ( int ) ( (float)(2 * LFOAMP * i ) / (float)(count) ); - - if( i > count / 2 ) - w[i] = ( int )( (float) (2 * LFOAMP) - (float)( 2 * LFOAMP * i ) / (float)( count )); - } - break; - case LFO_SQR: // square wave, 50% duty cycle, all values 0 <= x <= LFOAMP, initial value = 0 - for( i = 0; i < count; i++ ) - w[i] = i > count / 2 ? 0 : LFOAMP; - break; - case LFO_SAW: // forward saw wav, aall values 0 <= x <= LFOAMP, initial value = 0 - for( i = 0; i < count; i++ ) - w[i] = ( int )( (float)(LFOAMP) * (float)i / (float)( count )); - break; - case LFO_RND: // random wav, all values 0 <= x <= LFOAMP - for( i = 0; i < count; i++ ) - w[i] = ( int )( Com_RandomLong( 0, LFOAMP )); - break; - case LFO_LOG_IN: // logarithmic fade in, all values 0 <= x <= LFOAMP, initial value = 0 - for( i = 0; i < count; i++ ) - w[i] = ( int ) ( (float)(LFOAMP) * pow( (float)i / (float)count, 2 )); - break; - case LFO_LOG_OUT: // logarithmic fade out, all values 0 <= x <= LFOAMP, initial value = LFOAMP - for( i = 0; i < count; i++ ) - w[i] = ( int ) ( (float)(LFOAMP) * pow( 1.0 - ((float)i / (float)count), 2 )); - break; - case LFO_LIN_IN: // linear fade in, all values 0 <= x <= LFOAMP, initial value = 0 - for( i = 0; i < count; i++ ) - w[i] = ( int )( (float)(LFOAMP) * (float)i / (float)(count) ); - break; - case LFO_LIN_OUT: // linear fade out, all values 0 <= x <= LFOAMP, initial value = LFOAMP - for( i = 0; i < count; i++ ) - w[i] = LFOAMP - ( int )( (float)(LFOAMP) * (float)i / (float)(count) ); - break; - } -} - -// allocate all lfo wave tables. Called only when sound engine loads. -void LFOWAV_InitAll( void ) -{ - int i; - dly_t *pdly; - - Q_memset( lfowavs, 0, sizeof( lfowavs )); - - // alloc space for each lfo wav type - for( i = 0; i < CLFOWAV; i++ ) - { - pdly = DLY_Alloc( CLFOSAMPS, 0, 0 , DLY_PLAIN ); - - lfowavs[i].pdly = pdly; - lfowavs[i].type = i; - - LFOWAV_Fill( pdly->w, CLFOSAMPS, i ); - } - - // if any dlys fail to alloc, free all - for( i = 0; i < CLFOWAV; i++ ) - { - if( !lfowavs[i].pdly ) - LFOWAV_FreeAll(); - } -} - - -//////////////////////////////////////// -// LFO iterators - one shot and looping -//////////////////////////////////////// - -#define CLFO 16 // max active lfos (this steals from active delays) - -typedef struct -{ - qboolean fused; // true if slot take - dly_t *pdly; // delay points to lfo wav within lfowav_t (don't free this) - float f; // playback frequency in hz - pos_t pos; // current position within wav table, looping - pos_one_t pos1; // current position within wav table, one shot - int foneshot; // true - one shot only, don't repeat -} lfo_t; - -lfo_t lfos[CLFO]; - -void LFO_Init( lfo_t *plfo ) { if( plfo ) Q_memset( plfo, 0, sizeof( lfo_t )); } -void LFO_InitAll( void ) { int i; for( i = 0; i < CLFO; i++ ) LFO_Init( &lfos[i] ); } -void LFO_Free( lfo_t *plfo ) { if( plfo ) Q_memset( plfo, 0, sizeof( lfo_t )); } -void LFO_FreeAll( void ) { int i; for( i = 0; i < CLFO; i++ ) LFO_Free( &lfos[i] ); } - - -// get step value given desired playback frequency -_inline float LFO_HzToStep( float freqHz ) -{ - float lfoHz; - - // calculate integer and fractional step values, - // assume an update rate of SOUND_DMA_SPEED samples/sec - - // 1 cycle/CLFOSAMPS * SOUND_DMA_SPEED samps/sec = cycles/sec = current lfo rate - // - // lforate * X = freqHz so X = freqHz/lforate = update rate - lfoHz = (float)(SOUND_DMA_SPEED) / (float)(CLFOSAMPS); - - return freqHz / lfoHz; -} - -// return pointer to new lfo - -lfo_t *LFO_Alloc( int wtype, float freqHz, qboolean foneshot ) -{ - int i, type = min( CLFOWAV - 1, wtype ); - float lfostep; - - for( i = 0; i < CLFO; i++ ) - { - if( !lfos[i].fused ) - { - lfo_t *plfo = &lfos[i]; - - LFO_Init( plfo ); - - plfo->fused = true; - plfo->pdly = lfowavs[type].pdly; // pdly in lfo points to wav table data in lfowavs - plfo->f = freqHz; - plfo->foneshot = foneshot; - - lfostep = LFO_HzToStep( freqHz ); - - // init positional pointer (ie: fixed point updater for controlling pitch of lfo) - if( !foneshot ) POS_Init(&(plfo->pos), plfo->pdly->D, lfostep ); - else POS_ONE_Init(&(plfo->pos1), plfo->pdly->D,lfostep ); - - return plfo; - } - } - - MsgDev( D_WARN, "DSP: failed to allocate LFO.\n" ); - return NULL; -} - -// get next lfo value -// Value returned is 0..LFOAMP. can be normalized by shifting right by LFOBITS -// To play back at correct passed in frequency, routien should be -// called once for every output sample (ie: at SOUND_DMA_SPEED) -// x is dummy param -_inline int LFO_GetNext( lfo_t *plfo, int x ) -{ - int i; - - // get current position - if( !plfo->foneshot ) i = POS_GetNext( &plfo->pos ); - else i = POS_ONE_GetNext( &plfo->pos1 ); - - // return current sample - return plfo->pdly->w[i]; -} - -// batch version for performance -_inline void LFO_GetNextN( lfo_t *plfo, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = LFO_GetNext( plfo, pb->left ); - pb++; - } - break; - case OP_RIGHT: - while( count-- ) - { - pb->right = LFO_GetNext( plfo, pb->right ); - pb++; - } - break; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = LFO_GetNext( plfo, pb->left ); - pb++; - } - break; - } -} - -// uses lfowav, rate, foneshot -typedef enum -{ - // parameter order - lfo_iwav, - lfo_irate, - lfo_ifoneshot, - lfo_cparam // # of params - -} lfo_e; - -// parameter ranges - -prm_rng_t lfo_rng[] = -{ -{ lfo_cparam, 0, 0 }, // first entry is # of parameters -{ lfo_iwav, 0.0, LFO_MAX }, // lfo type to use (LFO_SIN, LFO_RND...) -{ lfo_irate, 0.0, 16000.0 }, // modulation rate in hz. for MDY, 1/rate = 'glide' time in seconds -{ lfo_ifoneshot, 0.0, 1.0 }, // 1.0 if lfo is oneshot -}; - -lfo_t * LFO_Params( prc_t *pprc ) -{ - lfo_t *plfo; - qboolean foneshot = pprc->prm[lfo_ifoneshot] > 0 ? true : false; - - plfo = LFO_Alloc( pprc->prm[lfo_iwav], pprc->prm[lfo_irate], foneshot ); - - return plfo; -} - -void LFO_ChangeVal( lfo_t *plfo, float fhz ) -{ - float fstep = LFO_HzToStep( fhz ); - - // change lfo playback rate to new frequency fhz - if( plfo->foneshot ) POS_ChangeVal( &plfo->pos, fstep ); - else POS_ChangeVal( &plfo->pos1.p, fstep ); -} - -_inline void *LFO_VParams( void *p ) -{ - PRC_CheckParams((prc_t *)p, lfo_rng ); - return (void *)LFO_Params((prc_t *)p); -} - -// v is +/- 0-1.0 -// v changes current lfo frequency up/down by +/- v% -_inline void LFO_Mod( lfo_t *plfo, float v ) -{ - float fhz; - float fhznew; - - fhz = plfo->f; - fhznew = fhz * (1.0 + v); - - LFO_ChangeVal( plfo, fhznew ); - - return; -} - - -///////////////////////////////////////////////////////////////////////////// -// Ramp - used for varying smoothly between int parameters ie: modulation delays -///////////////////////////////////////////////////////////////////////////// -typedef struct -{ - int initval; // initial ramp value - int target; // final ramp value - int sign; // increasing (1) or decreasing (-1) ramp - int yprev; // previous output value - qboolean fhitend; // true if hit end of ramp - pos_one_t ps; // current ramp output -} rmp_t; - -// ramp smoothly between initial value and target value in approx 'ramptime' seconds. -// (initial value may be greater or less than target value) -// never changes output by more than +1 or -1 (which can cause the ramp to take longer to complete than ramptime) -// called once per sample while ramping -// ramptime - duration of ramp in seconds -// initval - initial ramp value -// targetval - target ramp value -void RMP_Init( rmp_t *prmp, float ramptime, int initval, int targetval ) -{ - int rise; - int run; - - if( prmp ) Q_memset( prmp, 0, sizeof( rmp_t )); - - run = (int)( ramptime * SOUND_DMA_SPEED ); // 'samples' in ramp - rise = (targetval - initval); // height of ramp - - // init fixed point iterator to iterate along the height of the ramp 'rise' - // always iterates from 0..'rise', increasing in value - - POS_ONE_Init( &prmp->ps, ABS( rise ), ABS((float) rise) / ((float) run)); - - prmp->yprev = initval; - prmp->initval = initval; - prmp->target = targetval; - prmp->sign = SIGN( rise ); - -} - -// continues from current position to new target position -void RMP_SetNext( rmp_t *prmp, float ramptime, int targetval ) -{ - RMP_Init( prmp, ramptime, prmp->yprev, targetval ); -} - -_inline qboolean RMP_HitEnd( rmp_t *prmp ) -{ - return prmp->fhitend; -} - -_inline void RMP_SetEnd( rmp_t *prmp ) -{ - prmp->fhitend = true; -} - -// get next ramp value & update ramp, never varies by more than +1 or -1 between calls -// when ramp hits target value, it thereafter always returns last value - -_inline int RMP_GetNext( rmp_t *prmp ) -{ - int y, d; - - // if we hit ramp end, return last value - if( prmp->fhitend ) - return prmp->yprev; - - // get next integer position in ramp height. - d = POS_ONE_GetNext( &prmp->ps ); - - if( prmp->ps.fhitend ) - prmp->fhitend = true; - - // increase or decrease from initval, depending on ramp sign - if( prmp->sign > 0 ) - y = prmp->initval + d; - else y = prmp->initval - d; - - // only update current height by a max of +1 or -1 - // this means that for short ramp times, we may not hit target - if( ABS( y - prmp->yprev ) >= 1 ) - prmp->yprev += prmp->sign; - - return prmp->yprev; -} - -// get current ramp value, don't update ramp -_inline int RMP_GetCurrent( rmp_t *prmp ) -{ - return prmp->yprev; -} - -//////////////////////////////////////// -// Time Compress/expand with pitch shift -//////////////////////////////////////// - -// realtime pitch shift - ie: pitch shift without change to playback rate - -#define CPTCS 64 - -typedef struct -{ - qboolean fused; - dly_t *pdly_in; // input buffer space - dly_t *pdly_out; // output buffer space - int *pin; // input buffer (pdly_in->w) - int *pout; // output buffer (pdly_out->w) - int cin; // # samples in input buffer - int cout; // # samples in output buffer - int cxfade; // # samples in crossfade segment - int ccut; // # samples to cut - int cduplicate; // # samples to duplicate (redundant - same as ccut) - int iin; // current index into input buffer (reading) - pos_one_t psn; // stepping index through output buffer - qboolean fdup; // true if duplicating, false if cutting - float fstep; // pitch shift & time compress/expand -} ptc_t; - -ptc_t ptcs[CPTCS]; - -void PTC_Init( ptc_t *pptc ) { if( pptc ) Q_memset( pptc, 0, sizeof( ptc_t )); }; -void PTC_Free( ptc_t *pptc ) -{ - if( pptc ) - { - DLY_Free( pptc->pdly_in ); - DLY_Free( pptc->pdly_out ); - - Q_memset( pptc, 0, sizeof( ptc_t )); - } -}; - -void PTC_InitAll() { int i; for( i = 0; i < CPTCS; i++ ) PTC_Init( &ptcs[i] ); }; -void PTC_FreeAll() { int i; for( i = 0; i < CPTCS; i++ ) PTC_Free( &ptcs[i] ); }; - -// Time compressor/expander with pitch shift (ie: pitch changes, playback rate does not) -// -// Algorithm: -// 1) Duplicate or discard chunks of sound to provide tslice * fstep seconds of sound. -// (The user-selectable size of the buffer to process is tslice milliseconds in length) -// 2) Resample this compressed/expanded buffer at fstep to produce a pitch shifted -// output with the same duration as the input (ie: #samples out = # samples in, an -// obvious requirement for realtime _inline processing). - -// timeslice is size in milliseconds of full buffer to process. -// timeslice * fstep is the size of the expanded/compressed buffer -// timexfade is length in milliseconds of crossfade region between duplicated or cut sections -// fstep is % expanded/compressed sound normalized to 0.01-2.0 (1% - 200%) - -// input buffer: - -// iin--> - -// [0... tslice ...D] input samples 0...D (D is NEWEST sample) -// [0... ...n][m... tseg ...D] region to be cut or duplicated m...D - -// [0... [p..txf1..n][m... tseg ...D] fade in region 1 txf1 p...n -// [0... ...n][m..[q..txf2..D] fade out region 2 txf2 q...D - - -// pitch up: duplicate into output buffer: tdup = tseg - -// [0... ...n][m... tdup ...D][m... tdup ...D] output buffer size with duplicate region -// [0... ...n][m..[p...xf1..n][m... tdup ...D] fade in p...n while fading out q...D -// [0... ...n][m..[q...xf2..D][m... tdup ...D] -// [0... ...n][m..[.XFADE...n][m... tdup ...D] final duplicated output buffer - resample at fstep - -// pitch down: cut into output buffer: tcut = tseg - -// [0... ...n][m... tcut ...D] input samples with cut region delineated m...D -// [0... ...n] output buffer size after cut -// [0... [q..txf2...D] fade in txf1 q...D while fade out txf2 p...n -// [0... [.XFADE ...D] final cut output buffer - resample at fstep - - -ptc_t * PTC_Alloc( float timeslice, float timexfade, float fstep ) -{ - int i; - ptc_t *pptc; - float tout; - int cin, cout; - float tslice = timeslice; - float txfade = timexfade; - float tcutdup; - - // find time compressor slot - for( i = 0; i < CPTCS; i++ ) - { - if( !ptcs[i].fused ) - break; - } - - if( i == CPTCS ) - { - MsgDev( D_WARN, "DSP: failed to allocate pitch shifter.\n" ); - return NULL; - } - - pptc = &ptcs[i]; - PTC_Init( pptc ); - - // get size of region to cut or duplicate - tcutdup = abs(( fstep - 1.0 ) * timeslice ); - - // to prevent buffer overruns: - - // make sure timeslice is greater than cut/dup time - tslice = max ( tslice, 1.1 * tcutdup); - - // make sure xfade time smaller than cut/dup time, and smaller than (timeslice-cutdup) time - txfade = min( txfade, 0.9 * tcutdup ); - txfade = min( txfade, 0.9 * ( tslice - tcutdup )); - - pptc->cxfade = MSEC_TO_SAMPS( txfade ); - pptc->ccut = MSEC_TO_SAMPS( tcutdup ); - pptc->cduplicate = MSEC_TO_SAMPS( tcutdup ); - - // alloc delay lines (buffers) - tout = tslice * fstep; - - cin = MSEC_TO_SAMPS( tslice ); - cout = MSEC_TO_SAMPS( tout ); - - pptc->pdly_in = DLY_Alloc( cin, 0, 1, DLY_LINEAR ); // alloc input buffer - pptc->pdly_out = DLY_Alloc( cout, 0, 1, DLY_LINEAR ); // alloc output buffer - - if( !pptc->pdly_in || !pptc->pdly_out ) - { - PTC_Free( pptc ); - MsgDev( D_WARN, "DSP: failed to allocate delay for pitch shifter.\n" ); - return NULL; - } - - // buffer pointers - pptc->pin = pptc->pdly_in->w; - pptc->pout = pptc->pdly_out->w; - - // input buffer index - pptc->iin = 0; - - // output buffer index - POS_ONE_Init( &pptc->psn, cout, fstep ); - - // if fstep > 1.0 we're pitching shifting up, so fdup = true - pptc->fdup = fstep > 1.0 ? true : false; - - pptc->cin = cin; - pptc->cout = cout; - - pptc->fstep = fstep; - pptc->fused = true; - - return pptc; -} - -// linear crossfader -// yfadein - instantaneous value fading in -// ydafeout -instantaneous value fading out -// nsamples - duration in #samples of fade -// isample - index in to fade 0...nsamples-1 -_inline int xfade( int yfadein, int yfadeout, int nsamples, int isample ) -{ - int yout; - int m = (isample << PBITS ) / nsamples; - - yout = ((yfadein * m) >> PBITS) + ((yfadeout * (PMAX - m)) >> PBITS); - - return yout; -} - -// w - pointer to start of input buffer samples -// v - pointer to start of output buffer samples -// cin - # of input buffer samples -// cout = # of output buffer samples -// cxfade = # of crossfade samples -// cduplicate = # of samples in duplicate/cut segment -void TimeExpand( int *w, int *v, int cin, int cout, int cxfade, int cduplicate ) -{ - int i, j; - int m; - int p; - int q; - int D; - - // input buffer - // xfade source duplicate - // [0...........][p.......n][m...........D] - - // output buffer - // xfade region duplicate - // [0.....................n][m..[q.......D][m...........D] - - // D - index of last sample in input buffer - // m - index of 1st sample in duplication region - // p - index of 1st sample of crossfade source - // q - index of 1st sample in crossfade region - - D = cin - 1; - m = cin - cduplicate; - p = m - cxfade; - q = cin - cxfade; - - // copy up to crossfade region - for( i = 0; i < q; i++ ) - v[i] = w[i]; - - // crossfade region - j = p; - - for( i = q; i <= D; i++ ) - v[i] = xfade( w[j++], w[i], cxfade, i-q ); // fade out p..n, fade in q..D - - // duplicate region - j = D+1; - - for( i = m; i <= D; i++ ) - v[j++] = w[i]; - -} - -// cut ccut samples from end of input buffer, crossfade end of cut section -// with end of remaining section - -// w - pointer to start of input buffer samples -// v - pointer to start of output buffer samples -// cin - # of input buffer samples -// cout = # of output buffer samples -// cxfade = # of crossfade samples -// ccut = # of samples in cut segment -void TimeCompress( int *w, int *v, int cin, int cout, int cxfade, int ccut ) -{ - int i, j; - int m; - int p; - int q; - int D; - - // input buffer - // xfade source - // [0.....................n][m..[p.......D] - - // xfade region cut - // [0...........][q.......n][m...........D] - - // output buffer - // xfade to source - // [0...........][p.......D] - - // D - index of last sample in input buffer - // m - index of 1st sample in cut region - // p - index of 1st sample of crossfade source - // q - index of 1st sample in crossfade region - - D = cin - 1; - m = cin - ccut; - p = cin - cxfade; - q = m - cxfade; - - // copy up to crossfade region - - for( i = 0; i < q; i++ ) - v[i] = w[i]; - - // crossfade region - j = p; - - for( i = q; i < m; i++ ) - v[i] = xfade( w[j++], w[i], cxfade, i-q ); // fade out p..n, fade in q..D - - // skip rest of input buffer -} - -// get next sample - -// put input sample into input (delay) buffer -// get output sample from output buffer, step by fstep % -// output buffer is time expanded or compressed version of previous input buffer -_inline int PTC_GetNext( ptc_t *pptc, int x ) -{ - int iout, xout; - qboolean fhitend = false; - - // write x into input buffer - ASSERT( pptc->iin < pptc->cin ); - - pptc->pin[pptc->iin] = x; - - pptc->iin++; - - // check for end of input buffer - if( pptc->iin >= pptc->cin ) - fhitend = true; - - // read sample from output buffer, resampling at fstep - iout = POS_ONE_GetNext( &pptc->psn ); - ASSERT( iout < pptc->cout ); - xout = pptc->pout[iout]; - - if( fhitend ) - { - // if hit end of input buffer (ie: input buffer is full) - // reset input buffer pointer - // reset output buffer pointer - // rebuild entire output buffer (TimeCompress/TimeExpand) - - pptc->iin = 0; - - POS_ONE_Init( &pptc->psn, pptc->cout, pptc->fstep ); - - if( pptc->fdup ) TimeExpand ( pptc->pin, pptc->pout, pptc->cin, pptc->cout, pptc->cxfade, pptc->cduplicate ); - else TimeCompress ( pptc->pin, pptc->pout, pptc->cin, pptc->cout, pptc->cxfade, pptc->ccut ); - } - - return xout; -} - -// batch version for performance -_inline void PTC_GetNextN( ptc_t *pptc, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = PTC_GetNext( pptc, pb->left ); - pb++; - } - break; - case OP_RIGHT: - while( count-- ) - { - pb->right = PTC_GetNext( pptc, pb->right ); - pb++; - } - break; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = PTC_GetNext( pptc, pb->left ); - pb++; - } - break; - } -} - -// change time compression to new value -// fstep is new value -// ramptime is how long change takes in seconds (ramps smoothly), 0 for no ramp - -void PTC_ChangeVal( ptc_t *pptc, float fstep, float ramptime ) -{ -// UNDONE: ignored -// UNDONE: just realloc time compressor with new fstep -} - -// uses pitch: -// 1.0 = playback normal rate -// 0.5 = cut 50% of sound (2x playback) -// 1.5 = add 50% sound (0.5x playback) - -typedef enum -{ - // parameter order - ptc_ipitch, - ptc_itimeslice, - ptc_ixfade, - ptc_cparam // # of params -} ptc_e; - -// diffusor parameter ranges -prm_rng_t ptc_rng[] = -{ -{ ptc_cparam, 0, 0 }, // first entry is # of parameters -{ ptc_ipitch, 0.1, 4.0 }, // 0-n.0 where 1.0 = 1 octave up and 0.5 is one octave down -{ ptc_itimeslice, 20.0, 300.0 }, // in milliseconds - size of sound chunk to analyze and cut/duplicate - 100ms nominal -{ ptc_ixfade, 1.0, 200.0 }, // in milliseconds - size of crossfade region between spliced chunks - 20ms nominal -}; - -ptc_t *PTC_Params( prc_t *pprc ) -{ - ptc_t *pptc; - - float pitch = pprc->prm[ptc_ipitch]; - float timeslice = pprc->prm[ptc_itimeslice]; - float txfade = pprc->prm[ptc_ixfade]; - - pptc = PTC_Alloc( timeslice, txfade, pitch ); - - return pptc; -} - -_inline void *PTC_VParams( void *p ) -{ - PRC_CheckParams((prc_t *)p, ptc_rng ); - return (void *)PTC_Params((prc_t *)p); -} - -// change to new pitch value -// v is +/- 0-1.0 -// v changes current pitch up/down by +/- v% -void PTC_Mod( ptc_t *pptc, float v ) -{ - float fstep; - float fstepnew; - - fstep = pptc->fstep; - fstepnew = fstep * (1.0 + v); - - PTC_ChangeVal( pptc, fstepnew, 0.01 ); -} - - -//////////////////// -// ADSR envelope -//////////////////// - -#define CENVS 64 // max # of envelopes active -#define CENVRMPS 4 // A, D, S, R - -#define ENV_LIN 0 // linear a,d,s,r -#define ENV_EXP 1 // exponential a,d,s,r -#define ENV_MAX ENV_EXP - -#define ENV_BITS 14 // bits of resolution of ramp - -typedef struct -{ - qboolean fused; - qboolean fhitend; // true if done - int ienv; // current ramp - rmp_t rmps[CENVRMPS]; // ramps -} env_t; - -env_t envs[CENVS]; - -void ENV_Init( env_t *penv ) { if( penv ) Q_memset( penv, 0, sizeof( env_t )); }; -void ENV_Free( env_t *penv ) { if( penv ) Q_memset( penv, 0, sizeof( env_t )); }; -void ENV_InitAll() { int i; for( i = 0; i < CENVS; i++ ) ENV_Init( &envs[i] ); }; -void ENV_FreeAll() { int i; for( i = 0; i < CENVS; i++ ) ENV_Free( &envs[i] ); }; - - -// allocate ADSR envelope -// all times are in seconds -// amp1 - attack amplitude multiplier 0-1.0 -// amp2 - sustain amplitude multiplier 0-1.0 -// amp3 - end of sustain amplitude multiplier 0-1.0 -env_t *ENV_Alloc( int type, float famp1, float famp2, float famp3, float attack, float decay, float sustain, float release ) -{ - int i; - env_t *penv; - - for( i = 0; i < CENVS; i++ ) - { - if( !envs[i].fused ) - { - int amp1 = famp1 * (1 << ENV_BITS); // ramp resolution - int amp2 = famp2 * (1 << ENV_BITS); - int amp3 = famp3 * (1 << ENV_BITS); - - penv = &envs[i]; - - ENV_Init( penv ); - - // UNDONE: ignoring type = ENV_EXP - use oneshot LFOS instead with sawtooth/exponential - - // set up ramps - RMP_Init( &penv->rmps[0], attack, 0, amp1 ); - RMP_Init( &penv->rmps[1], decay, amp1, amp2 ); - RMP_Init( &penv->rmps[2], sustain, amp2, amp3 ); - RMP_Init( &penv->rmps[3], release, amp3, 0 ); - - penv->ienv = 0; - penv->fused = true; - penv->fhitend = false; - - return penv; - } - } - - MsgDev( D_WARN, "DSP: failed to allocate envelope.\n" ); - return NULL; -} - -_inline int ENV_GetNext( env_t *penv, int x ) -{ - if( !penv->fhitend ) - { - int i, y; - - i = penv->ienv; - y = RMP_GetNext( &penv->rmps[i] ); - - // check for next ramp - if( penv->rmps[i].fhitend ) - i++; - - penv->ienv = i; - - // check for end of all ramps - if( i > 3 ) penv->fhitend = true; - - // multiply input signal by ramp - return (x * y) >> ENV_BITS; - } - return 0; -} - -// batch version for performance - -_inline void ENV_GetNextN( env_t *penv, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = ENV_GetNext( penv, pb->left ); - pb++; - } - break; - case OP_RIGHT: - while( count-- ) - { - pb->right = ENV_GetNext( penv, pb->right ); - pb++; - } - break; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = ENV_GetNext( penv, pb->left ); - pb++; - } - break; - } -} - -// uses lfowav, amp1, amp2, amp3, attack, decay, sustain, release -// lfowav is type, currently ignored - ie: LFO_LIN_IN, LFO_LOG_IN - -// parameter order -typedef enum -{ - env_itype, - env_iamp1, - env_iamp2, - env_iamp3, - env_iattack, - env_idecay, - env_isustain, - env_irelease, - env_cparam // # of params - -} env_e; - -// parameter ranges -prm_rng_t env_rng[] = -{ -{ env_cparam, 0, 0 }, // first entry is # of parameters -{ env_itype, 0.0, ENV_MAX }, // ENV_LINEAR, ENV_LOG - currently ignored -{ env_iamp1, 0.0, 1.0 }, // attack peak amplitude 0-1.0 -{ env_iamp2, 0.0, 1.0 }, // decay target amplitued 0-1.0 -{ env_iamp3, 0.0, 1.0 }, // sustain target amplitude 0-1.0 -{ env_iattack, 0.0, 20000.0 }, // attack time in milliseconds -{ env_idecay, 0.0, 20000.0 }, // envelope decay time in milliseconds -{ env_isustain, 0.0, 20000.0 }, // sustain time in milliseconds -{ env_irelease, 0.0, 20000.0 }, // release time in milliseconds -}; - -env_t *ENV_Params( prc_t *pprc ) -{ - env_t *penv; - - float type = pprc->prm[env_itype]; - float amp1 = pprc->prm[env_iamp1]; - float amp2 = pprc->prm[env_iamp2]; - float amp3 = pprc->prm[env_iamp3]; - float attack = pprc->prm[env_iattack] / 1000.0f; - float decay = pprc->prm[env_idecay] / 1000.0f; - float sustain = pprc->prm[env_isustain] / 1000.0f; - float release = pprc->prm[env_irelease] / 1000.0f; - - penv = ENV_Alloc( type, amp1, amp2, amp3, attack, decay, sustain, release ); - return penv; -} - -_inline void *ENV_VParams( void *p ) -{ - PRC_CheckParams((prc_t *)p, env_rng ); - return (void *)ENV_Params((prc_t *)p); -} - -_inline void ENV_Mod ( void *p, float v ) -{ -} - -//////////////////// -// envelope follower -//////////////////// -#define CEFOS 64 // max # of envelope followers active - -#define CEFOBITS 6 // size 2^6 = 64 -#define CEFOWINDOW (1 << (CEFOBITS)) // size of sample window - -typedef struct -{ - qboolean fused; - int avg; // accumulating average over sample window - int cavg; // count down - int xout; // current output value - -} efo_t; - -efo_t efos[CEFOS]; - -void EFO_Init( efo_t *pefo ) { if( pefo ) Q_memset( pefo, 0, sizeof( efo_t )); }; -void EFO_Free( efo_t *pefo ) { if( pefo ) Q_memset( pefo, 0, sizeof( efo_t )); }; -void EFO_InitAll() { int i; for( i = 0; i < CEFOS; i++ ) EFO_Init( &efos[i] ); }; -void EFO_FreeAll() { int i; for( i = 0; i < CEFOS; i++ ) EFO_Free( &efos[i] ); }; - -// allocate enveloper follower -efo_t *EFO_Alloc( void ) -{ - int i; - efo_t *pefo; - - for( i = 0; i < CEFOS; i++ ) - { - if( !efos[i].fused ) - { - pefo = &efos[i]; - - EFO_Init( pefo ); - - pefo->xout = 0; - pefo->cavg = CEFOWINDOW; - pefo->fused = true; - - return pefo; - } - } - - MsgDev( D_WARN, "DSP: failed to allocate envelope follower.\n" ); - return NULL; -} - - -_inline int EFO_GetNext( efo_t *pefo, int x ) -{ - int xa = ABS( x ); // rectify input wav - - // get running sum / 2 - pefo->avg += xa >> 1; // divide by 2 to prevent overflow - - pefo->cavg--; - - if( !pefo->cavg ) - { - // new output value - end of window - - // get average over window - pefo->xout = pefo->avg >> (CEFOBITS - 1); // divide by window size / 2 - pefo->cavg = CEFOWINDOW; - pefo->avg = 0; - } - - return pefo->xout; -} - -// batch version for performance -_inline void EFO_GetNextN( efo_t *pefo, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = EFO_GetNext( pefo, pb->left ); - pb++; - } - break; - case OP_RIGHT: - while( count-- ) - { - pb->right = EFO_GetNext( pefo, pb->right ); - pb++; - } - break; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = EFO_GetNext( pefo, pb->left ); - pb++; - } - break; - } -} - - -efo_t * EFO_Params( prc_t *pprc ) -{ - return EFO_Alloc(); -} - -_inline void *EFO_VParams( void *p ) -{ - // PRC_CheckParams(( prc_t *)p, efo_rng ); - efo has no params - return (void *)EFO_Params((prc_t *)p ); -} - -_inline void EFO_Mod( void *p, float v ) -{ -} - -////////////// -// mod delay -////////////// - -// modulate delay time anywhere from 0..D using MDY_ChangeVal. no output glitches (uses RMP) - -#define CMDYS 64 // max # of mod delays active (steals from delays) - -typedef struct -{ - qboolean fused; - qboolean fchanging; // true if modulating to new delay value - dly_t *pdly; // delay - int Dcur; // current delay value - float ramptime; // ramp 'glide' time - time in seconds to change between values - int mtime; // time in samples between delay changes. 0 implies no self-modulating - int mtimecur; // current time in samples until next delay change - float depth; // modulate delay from D to D - (D*depth) depth 0-1.0 - int xprev; // previous delay output, used to smooth transitions between delays - rmp_t rmp; // ramp -} mdy_t; - -mdy_t mdys[CMDYS]; - -void MDY_Init( mdy_t *pmdy ) { if( pmdy ) Q_memset( pmdy, 0, sizeof( mdy_t )); }; -void MDY_Free( mdy_t *pmdy ) { if( pmdy ) { DLY_Free( pmdy->pdly ); Q_memset( pmdy, 0, sizeof( mdy_t )); } }; -void MDY_InitAll() { int i; for( i = 0; i < CMDYS; i++ ) MDY_Init( &mdys[i] ); }; -void MDY_FreeAll() { int i; for( i = 0; i < CMDYS; i++ ) MDY_Free( &mdys[i] ); }; - - -// allocate mod delay, given previously allocated dly -// ramptime is time in seconds for delay to change from dcur to dnew -// modtime is time in seconds between modulations. 0 if no self-modulation -// depth is 0-1.0 multiplier, new delay values when modulating are Dnew = randomlong (D - D*depth, D) -mdy_t *MDY_Alloc( dly_t *pdly, float ramptime, float modtime, float depth ) -{ - int i; - mdy_t *pmdy; - - if( !pdly ) - return NULL; - - for( i = 0; i < CMDYS; i++ ) - { - if( !mdys[i].fused ) - { - pmdy = &mdys[i]; - - MDY_Init( pmdy ); - - pmdy->pdly = pdly; - - if( !pmdy->pdly ) + // modify delay, if crossfading + if( dly->xfade ) { - MsgDev( D_WARN, "DSP: failed to allocate delay for mod delay.\n" ); - return NULL; + samplexf = dly->lpdelayline[dly->idelayoutputxf] * (128 - dly->xfade) >> 7; + delay = samplexf + ((delay * dly->xfade) >> 7); + + if( ++dly->idelayoutputxf >= dly->cdelaysamplesmax ) + dly->idelayoutputxf = 0; + + if( --dly->xfade == 0 ) + dly->idelayoutput = dly->idelayoutputxf; } - pmdy->Dcur = pdly->D0; - pmdy->fused = true; - pmdy->ramptime = ramptime; - pmdy->mtime = SEC_TO_SAMPS( modtime ); - pmdy->mtimecur = pmdy->mtime; - pmdy->depth = depth; + // save left value to delay line + dly->lpdelayline[dly->idelayinput] = paint->left; - return pmdy; - } - } - - MsgDev( D_WARN, "DSP: failed to allocate mod delay.\n" ); - return NULL; -} - -// change to new delay tap value t samples, ramp linearly over ramptime seconds -void MDY_ChangeVal( mdy_t *pmdy, int t ) -{ - // if D > original delay value, cap at original value - - t = min( pmdy->pdly->D0, t ); - pmdy->fchanging = true; - - RMP_Init( &pmdy->rmp, pmdy->ramptime, pmdy->Dcur, t ); -} - -// get next value from modulating delay -int MDY_GetNext( mdy_t *pmdy, int x ) -{ - int xout; - int xcur; - - // get current delay output - xcur = DLY_GetNext( pmdy->pdly, x ); - - // return right away if not modulating (not changing and not self modulating) - if( !pmdy->fchanging && !pmdy->mtime ) - { - pmdy->xprev = xcur; - return xcur; - } - - xout = xcur; - - // if currently changing to new delay target, get next delay value - if( pmdy->fchanging ) - { - // get next ramp value, test for done - int r = RMP_GetNext( &pmdy->rmp ); - - if( RMP_HitEnd( &pmdy->rmp )) - pmdy->fchanging = false; - - // if new delay different from current delay, change delay - if( r != pmdy->Dcur ) - { - // ramp never changes by more than + or - 1 - - // change delay tap value to r - DLY_ChangeVal( pmdy->pdly, r ); - - pmdy->Dcur = r; - - // filter delay output within transitions. - // note: xprev = xcur = 0 if changing delay on 1st sample - xout = ( xcur + pmdy->xprev ) >> 1; - } - } - - // if self-modulating and timer has expired, get next change - if( pmdy->mtime && !pmdy->mtimecur-- ) - { - int D0 = pmdy->pdly->D0; - int Dnew; - float D1; - - pmdy->mtimecur = pmdy->mtime; - - // modulate between 0 and 100% of d0 - D1 = (float)D0 * (1.0 - pmdy->depth); - Dnew = Com_RandomLong( (int)D1, D0 ); - - MDY_ChangeVal( pmdy, Dnew ); - } - - pmdy->xprev = xcur; - - return xout; -} - -// batch version for performance -_inline void MDY_GetNextN( mdy_t *pmdy, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = MDY_GetNext( pmdy, pb->left ); - pb++; - } - return; - case OP_RIGHT: - while( count-- ) - { - pb->right = MDY_GetNext( pmdy, pb->right ); - pb++; - } - return; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = MDY_GetNext( pmdy, pb->left ); - pb++; - } - return; - } -} - -// parameter order -typedef enum -{ - mdy_idtype, // NOTE: first 8 params must match params in dly_e - mdy_idelay, - mdy_ifeedback, - mdy_igain, - mdy_iftype, - mdy_icutoff, - mdy_iqwidth, - mdy_iquality, - mdy_imodrate, - mdy_imoddepth, - mdy_imodglide, - mdy_cparam -} mdy_e; - - -// parameter ranges -prm_rng_t mdy_rng[] = -{ -{ mdy_cparam, 0, 0 }, // first entry is # of parameters - -// delay params -{ mdy_idtype, 0, DLY_MAX }, // delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS -{ mdy_idelay, 0.0, 1000.0 }, // delay in milliseconds -{ mdy_ifeedback, 0.0, 0.99 }, // feedback 0-1.0 -{ mdy_igain, 0.0, 1.0 }, // final gain of output stage, 0-1.0 - -// filter params if mdy type DLY_LOWPASS -{ mdy_iftype, 0, FTR_MAX }, -{ mdy_icutoff, 10.0, 22050.0 }, -{ mdy_iqwidth, 100.0, 11025.0 }, -{ mdy_iquality, 0, QUA_MAX }, -{ mdy_imodrate, 0.01, 200.0 }, // frequency at which delay values change to new random value. 0 is no self-modulation -{ mdy_imoddepth, 0.0, 1.0 }, // how much delay changes (decreases) from current value (0-1.0) -{ mdy_imodglide, 0.01, 100.0 }, // glide time between dcur and dnew in milliseconds -}; - -// convert user parameters to internal parameters, allocate and return -mdy_t *MDY_Params( prc_t *pprc ) -{ - mdy_t *pmdy; - dly_t *pdly; - - float ramptime = pprc->prm[mdy_imodglide] / 1000.0; // get ramp time in seconds - float modtime = 1.0 / pprc->prm[mdy_imodrate]; // time between modulations in seconds - float depth = pprc->prm[mdy_imoddepth]; // depth of modulations 0-1.0 - - // alloc plain, allpass or lowpass delay - pdly = DLY_Params( pprc ); - if( !pdly ) return NULL; - - pmdy = MDY_Alloc( pdly, ramptime, modtime, depth ); - - return pmdy; -} - -_inline void * MDY_VParams( void *p ) -{ - PRC_CheckParams(( prc_t *)p, mdy_rng ); - return (void *)MDY_Params ((prc_t *)p ); -} - -// v is +/- 0-1.0 -// change current delay value 0..D -void MDY_Mod( mdy_t *pmdy, float v ) -{ - int D = pmdy->Dcur; - float v2 = -(v + 1.0)/2.0; // v2 varies -1.0-0.0 - - // D varies 0..D - D = D + (int)((float)D * v2); - - // change delay - MDY_ChangeVal( pmdy, D ); -} - - -/////////////////////////////////////////// -// Chorus - lfo modulated delay -/////////////////////////////////////////// - - -#define CCRSS 64 // max number chorus' active - -typedef struct -{ - qboolean fused; - mdy_t *pmdy; // modulatable delay - lfo_t *plfo; // modulating lfo - int lfoprev; // previous modulator value from lfo - int mix; // mix of clean & chorus signal - 0..PMAX -} crs_t; - -crs_t crss[CCRSS]; - -void CRS_Init( crs_t *pcrs ) { if( pcrs ) Q_memset( pcrs, 0, sizeof( crs_t )); }; -void CRS_Free( crs_t *pcrs ) -{ - if( pcrs ) - { - MDY_Free( pcrs->pmdy ); - LFO_Free( pcrs->plfo ); - Q_memset( pcrs, 0, sizeof( crs_t )); - } -} - -void CRS_InitAll() { int i; for( i = 0; i < CCRSS; i++ ) CRS_Init( &crss[i] ); } -void CRS_FreeAll() { int i; for( i = 0; i < CCRSS; i++ ) CRS_Free( &crss[i] ); } - -// fstep is base pitch shift, ie: floating point step value, where 1.0 = +1 octave, 0.5 = -1 octave -// lfotype is LFO_SIN, LFO_RND, LFO_TRI etc (LFO_RND for chorus, LFO_SIN for flange) -// fHz is modulation frequency in Hz -// depth is modulation depth, 0-1.0 -// mix is mix of chorus and clean signal - -#define CRS_DELAYMAX 100 // max milliseconds of sweepable delay -#define CRS_RAMPTIME 5 // milliseconds to ramp between new delay values - -crs_t * CRS_Alloc( int lfotype, float fHz, float fdepth, float mix ) -{ - int i, D; - crs_t *pcrs; - dly_t *pdly; - mdy_t *pmdy; - lfo_t *plfo; - float ramptime; - - // find free chorus slot - for( i = 0; i < CCRSS; i++ ) - { - if( !crss[i].fused ) - break; - } - - if( i == CCRSS ) - { - MsgDev( D_WARN, "DSP: failed to allocate chorus.\n" ); - return NULL; - } - - pcrs = &crss[i]; - CRS_Init( pcrs ); - - D = fdepth * MSEC_TO_SAMPS( CRS_DELAYMAX ); // sweep from 0 - n milliseconds - - ramptime = (float)CRS_RAMPTIME / 1000.0f; // # milliseconds to ramp between new values - - pdly = DLY_Alloc( D, 0, 1, DLY_LINEAR ); - pmdy = MDY_Alloc( pdly, ramptime, 0.0, 0.0 ); - plfo = LFO_Alloc( lfotype, fHz, false ); - - if( !plfo || !pmdy ) - { - LFO_Free( plfo ); - MDY_Free( pmdy ); - MsgDev( D_WARN, "DSP: failed to allocate lfo or mdy for chorus.\n" ); - return NULL; - } - - pcrs->pmdy = pmdy; - pcrs->plfo = plfo; - pcrs->mix = (int)( PMAX * mix ); - pcrs->fused = true; - - return pcrs; -} - -// return next chorused sample (modulated delay) mixed with input sample -_inline int CRS_GetNext( crs_t *pcrs, int x ) -{ - int l, y; - - // get current mod delay value - y = MDY_GetNext( pcrs->pmdy, x ); - - // get next lfo value for modulation - // note: lfo must return 0 as first value - l = LFO_GetNext( pcrs->plfo, x ); - - // if modulator has changed, change mdy - if( l != pcrs->lfoprev ) - { - // calculate new tap (starts at D) - int D = pcrs->pmdy->pdly->D0; - int tap; - - // lfo should always output values 0 <= l <= LFOMAX - - if( l < 0 ) l = 0; - - tap = D - ((l * D) >> LFOBITS); - MDY_ChangeVal ( pcrs->pmdy, tap ); - pcrs->lfoprev = l; - } - - return ((y * pcrs->mix) >> PBITS) + x; -} - -// batch version for performance -_inline void CRS_GetNextN( crs_t *pcrs, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = CRS_GetNext( pcrs, pb->left ); - pb++; - } - break; - case OP_RIGHT: - while( count-- ) - { - pb->right = CRS_GetNext( pcrs, pb->right ); - pb++; - } - break; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = CRS_GetNext( pcrs, pb->left ); - pb++; - } - break; - } -} - -// parameter order -typedef enum -{ - crs_ilfotype, - crs_irate, - crs_idepth, - crs_imix, - crs_cparam -} crs_e; - - -// parameter ranges -prm_rng_t crs_rng[] = -{ -{ crs_cparam, 0, 0 }, // first entry is # of parameters -{ crs_ilfotype, 0, LFO_MAX }, // lfotype is LFO_SIN, LFO_RND, LFO_TRI etc (LFO_RND for chorus, LFO_SIN for flange) -{ crs_irate, 0.0, 1000.0 }, // rate is modulation frequency in Hz -{ crs_idepth, 0.0, 1.0 }, // depth is modulation depth, 0-1.0 -{ crs_imix, 0.0, 1.0 }, // mix is mix of chorus and clean signal -}; - -// uses pitch, lfowav, rate, depth -crs_t *CRS_Params( prc_t *pprc ) -{ - crs_t *pcrs; - - pcrs = CRS_Alloc( pprc->prm[crs_ilfotype], pprc->prm[crs_irate], pprc->prm[crs_idepth], pprc->prm[crs_imix] ); - - return pcrs; -} - -_inline void *CRS_VParams( void *p ) -{ - PRC_CheckParams((prc_t *)p, crs_rng ); - return (void *)CRS_Params((prc_t *)p ); -} - -_inline void CRS_Mod( void *p, float v ) -{ -} - -//////////////////////////////////////////////////// -// amplifier - modulatable gain, distortion -//////////////////////////////////////////////////// - -#define CAMPS 64 // max number amps active -#define AMPSLEW 10 // milliseconds of slew time between gain changes - -typedef struct -{ - qboolean fused; - float gain; // amplification 0-6.0 - float vthresh; // clip distortion threshold 0-1.0 - float distmix; // 0-1.0 mix of distortion with clean - float vfeed; // 0-1.0 feedback with distortion; - float gaintarget; // new gain - float gaindif; // incrementer -} amp_t; - -amp_t amps[CAMPS]; - -void AMP_Init( amp_t *pamp ) { if( pamp ) Q_memset( pamp, 0, sizeof( amp_t )); } -void AMP_Free( amp_t *pamp ) { if( pamp ) Q_memset( pamp, 0, sizeof( amp_t )); } -void AMP_InitAll() { int i; for( i = 0; i < CAMPS; i++ ) AMP_Init( &s[i] ); } -void AMP_FreeAll() { int i; for( i = 0; i < CAMPS; i++ ) AMP_Free( &s[i] ); } - -amp_t *AMP_Alloc( float gain, float vthresh, float distmix, float vfeed ) -{ - int i; - amp_t *pamp; - - // find free amp slot - for( i = 0; i < CAMPS; i++ ) - { - if ( !amps[i].fused ) - break; - } - - if( i == CAMPS ) - { - MsgDev( D_WARN, "DSP: failed to allocate amp.\n" ); - return NULL; - } - - pamp = &s[i]; - - AMP_Init ( pamp ); - - pamp->gain = gain; - pamp->vthresh = vthresh; - pamp->distmix = distmix; - pamp->vfeed = vfeed; - - return pamp; -} - -// return next amplified sample -_inline int AMP_GetNext( amp_t *pamp, int x ) -{ - float y = (float)x; - float yin; - float gain = pamp->gain; - - yin = y; - - // slew between gains - if( gain != pamp->gaintarget ) - { - float gaintarget = pamp->gaintarget; - float gaindif = pamp->gaindif; - - if( gain > gaintarget ) - { - gain -= gaindif; - if( gain <= gaintarget ) - pamp->gaintarget = gain; + // paint new delay value + paint->left = CLIP(delay); } else { - gain += gaindif; - if( gain >= gaintarget ) - pamp->gaintarget = gain; - } + // clear delay line + dly->lpdelayline[dly->idelayinput] = 0; + } - pamp->gain = gain; - } - - // if distortion is on, add distortion, feedback - if( pamp->vthresh < 1.0 ) - { - float fclip = pamp->vthresh * 32767.0; - - if( pamp->vfeed > 0.0 ) - { - // UNDONE: feedback - } - - // clip distort - y = ( y > fclip ? fclip : ( y < -fclip ? -fclip : y)); - - // mix distorted with clean (1.0 = full distortion) - if( pamp->distmix > 0.0 ) - y = y * pamp->distmix + yin * (1.0 - pamp->distmix); - } - - // amplify - y *= gain; - - return (int)y; -} - -// batch version for performance -_inline void AMP_GetNextN( amp_t *pamp, portable_samplepair_t *pbuffer, int SampleCount, int op ) -{ - int count = SampleCount; - portable_samplepair_t *pb = pbuffer; - - switch( op ) - { - default: - case OP_LEFT: - while( count-- ) - { - pb->left = AMP_GetNext( pamp, pb->left ); - pb++; - } - break; - case OP_RIGHT: - while( count-- ) - { - pb->right = AMP_GetNext( pamp, pb->right ); - pb++; - } - break; - case OP_LEFT_DUPLICATE: - while( count-- ) - { - pb->left = pb->right = AMP_GetNext( pamp, pb->left ); - pb++; - } - break; + DLY_MovePointer( dly ); } } -_inline void AMP_Mod( amp_t *pamp, float v ) +/* +============= +DLY_CheckNewDelayVal + +Update delay processor settings if we are in new room +============= +*/ +void DLY_CheckNewDelayVal( void ) { - float vmod = bound( v, 0.0, 1.0 ); - float samps = MSEC_TO_SAMPS( AMPSLEW ); // # samples to slew between amp values + float delay = sxdly_delay->value; + dly_t *const dly = &rgsxdly[MONODLY]; - // ramp to new amplification value - pamp->gaintarget = pamp->gain * vmod; - - pamp->gaindif = fabs( pamp->gain - pamp->gaintarget ) / samps; - - if( pamp->gaindif == 0.0f ) - pamp->gaindif = fabs( pamp->gain - pamp->gaintarget ) / 100; -} - - -// parameter order -typedef enum -{ - amp_gain, - amp_vthresh, - amp_distmix, - amp_vfeed, - amp_cparam -} amp_e; - - -// parameter ranges -prm_rng_t amp_rng[] = -{ -{ amp_cparam, 0, 0 }, // first entry is # of parameters -{ amp_gain, 0.0, 10.0 }, // amplification -{ amp_vthresh, 0.0, 1.0 }, // threshold for distortion (1.0 = no distortion) -{ amp_distmix, 0.0, 1.0 }, // mix of clean and distortion (1.0 = full distortion, 0.0 = full clean) -{ amp_vfeed, 0.0, 1.0 }, // distortion feedback -}; - -amp_t * AMP_Params( prc_t *pprc ) -{ - amp_t *pamp; - - pamp = AMP_Alloc( pprc->prm[amp_gain], pprc->prm[amp_vthresh], pprc->prm[amp_distmix], pprc->prm[amp_vfeed] ); - - return pamp; -} - -_inline void *AMP_VParams( void *p ) -{ - PRC_CheckParams((prc_t *)p, amp_rng ); - return (void *)AMP_Params((prc_t *)p ); -} - - -///////////////// -// NULL processor -///////////////// -typedef struct -{ - int type; -} nul_t; - -nul_t nuls[] = { 0 }; - -void NULL_Init( nul_t *pnul ) { } -void NULL_InitAll( ) { } -void NULL_Free( nul_t *pnul ) { } -void NULL_FreeAll( ) { } -nul_t *NULL_Alloc( ) { return &nuls[0]; } - -_inline int NULL_GetNext( void *p, int x ) { return x; } -_inline void NULL_GetNextN( nul_t *pnul, portable_samplepair_t *pbuffer, int SampleCount, int op ) { return; } -_inline void NULL_Mod( void *p, float v ) { return; } -_inline void * NULL_VParams( void *p ) { return (void *)(&nuls[0]); } - -////////////////////////// -// DSP processors presets -////////////////////////// - -// A dsp processor (prc) performs a single-sample function, such as pitch shift, delay, reverb, filter - -// note, this array must have CPRCPARMS entries -#define PRMZERO 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 -#define PFNZERO NULL,NULL,NULL,NULL,NULL // zero pointers for pfnparam...pdata within prc_t - -////////////////// -// NULL processor -///////////////// - -#define PRC_NULL1 { PRC_NULL, PRMZERO, PFNZERO } - -#define PRC0 PRC_NULL1 - -////////////// -// Amplifiers -////////////// - -// {amp_gain, 0.0, 10.0 }, // amplification -// {amp_vthresh, 0.0, 1.0 }, // threshold for distortion (1.0 = no distortion) -// {amp_distmix, 0.0, 1.0 }, // mix of clean and distortion (1.0 = full distortion, 0.0 = full clean) -// {amp_vfeed, 0.0, 1.0 }, // distortion feedback - -// prctype gain vthresh distmix vfeed -#define PRC_AMP1 {PRC_AMP, { 1.0, 1.0, 0.0, 0.0, }, PFNZERO } // modulatable unity gain amp -#define PRC_AMP2 {PRC_AMP, { 1.5, 0.75, 1.0, 0.0, }, PFNZERO } // amp with light distortion -#define PRC_AMP3 {PRC_AMP, { 2.0, 0.5, 1.0, 0.0, }, PFNZERO } // amp with medium distortion -#define PRC_AMP4 {PRC_AMP, { 4.0, 0.25, 1.0, 0.0, }, PFNZERO } // amp with heavy distortion -#define PRC_AMP5 {PRC_AMP, { 10.0, 0.10, 1.0, 0.0, }, PFNZERO } // mega distortion - -#define PRC_AMP6 {PRC_AMP, { 0.1, 1.0, 0.0, 0.0, }, PFNZERO } // fade out -#define PRC_AMP7 {PRC_AMP, { 0.2, 1.0, 0.0, 0.0, }, PFNZERO } // fade out -#define PRC_AMP8 {PRC_AMP, { 0.3, 1.0, 0.0, 0.0, }, PFNZERO } // fade out - -#define PRC_AMP9 {PRC_AMP, { 0.75, 1.0, 0.0, 0.0, }, PFNZERO } // duck out - - -/////////// -// Filters -/////////// - -// ftype: filter type FLT_LP, FLT_HP, FLT_BP (UNDONE: FLT_BP currently ignored) -// cutoff: cutoff frequency in hz at -3db gain -// qwidth: width of BP, or steepness of LP/HP (ie: fcutoff + qwidth = -60db gain point) -// quality: QUA_LO, _MED, _HI 0,1,2 - -// prctype ftype cutoff qwidth quality -#define PRC_FLT1 {PRC_FLT, { FLT_LP, 3000, 1000, QUA_MED, }, PFNZERO } -#define PRC_FLT2 {PRC_FLT, { FLT_LP, 2000, 2000, QUA_MED, }, PFNZERO } // lowpass for facing away -#define PRC_FLT3 {PRC_FLT, { FLT_LP, 1000, 1000, QUA_MED, }, PFNZERO } -#define PRC_FLT4 {PRC_FLT, { FLT_LP, 700, 700, QUA_LO, }, PFNZERO } // muffle filter - -#define PRC_FLT5 {PRC_FLT, { FLT_HP, 700, 200, QUA_MED, }, PFNZERO } // highpass (bandpass pair) -#define PRC_FLT6 {PRC_FLT, { FLT_HP, 2000, 1000, QUA_MED, }, PFNZERO } // lowpass (bandpass pair) - -////////// -// Delays -////////// - -// dtype: delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS -// delay: delay in milliseconds -// feedback: feedback 0-1.0 -// gain: final gain of output stage, 0-1.0 - -// prctype dtype delay feedbk gain ftype cutoff qwidth quality -#define PRC_DLY1 {PRC_DLY, { DLY_PLAIN, 500.0, 0.5, 0.6, 0.0, 0.0, 0.0, 0.0, }, PFNZERO } -#define PRC_DLY2 {PRC_DLY, { DLY_LOWPASS, 45.0, 0.8, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } -#define PRC_DLY3 {PRC_DLY, { DLY_LOWPASS, 300.0, 0.5, 0.6, FLT_LP, 2000, 2000, QUA_LO, }, PFNZERO } // outside S -#define PRC_DLY4 {PRC_DLY, { DLY_LOWPASS, 400.0, 0.5, 0.6, FLT_LP, 1500, 1500, QUA_LO, }, PFNZERO } // outside M -#define PRC_DLY5 {PRC_DLY, { DLY_LOWPASS, 750.0, 0.5, 0.6, FLT_LP, 1000, 1000, QUA_LO, }, PFNZERO } // outside L -#define PRC_DLY6 {PRC_DLY, { DLY_LOWPASS, 1000.0, 0.5, 0.6, FLT_LP, 800, 400, QUA_LO, }, PFNZERO } // outside VL -#define PRC_DLY7 {PRC_DLY, { DLY_LOWPASS, 45.0, 0.4, 0.5, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // tunnel S -#define PRC_DLY8 {PRC_DLY, { DLY_LOWPASS, 55.0, 0.4, 0.5, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // tunnel M -#define PRC_DLY9 {PRC_DLY, { DLY_LOWPASS, 65.0, 0.4, 0.5, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // tunnel L -#define PRC_DLY10 {PRC_DLY, { DLY_LOWPASS, 150.0, 0.5, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // cavern S -#define PRC_DLY11 {PRC_DLY, { DLY_LOWPASS, 200.0, 0.7, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // cavern M -#define PRC_DLY12 {PRC_DLY, { DLY_LOWPASS, 300.0, 0.7, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // cavern L -#define PRC_DLY13 {PRC_DLY, { DLY_LINEAR, 300.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,}, PFNZERO } // straight delay 300ms -#define PRC_DLY14 {PRC_DLY, { DLY_LINEAR, 80.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,}, PFNZERO } // straight delay 80ms - -/////////// -// Reverbs -/////////// - -// size: 0-2.0 scales nominal delay parameters (starting at approx 20ms) -// density: 0-2.0 density of reverbs (room shape) - controls # of parallel or series delays -// decay: 0-2.0 scales feedback parameters (starting at approx 0.15) - -// prctype size density decay ftype cutoff qwidth fparallel -#define PRC_RVA1 {PRC_RVA, {2.0, 0.5, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO } -#define PRC_RVA2 {PRC_RVA, {1.0, 0.2, 1.5, 0, 0, 0, 0}, PFNZERO } - -#define PRC_RVA3 {PRC_RVA, {0.8, 0.5, 1.5, FLT_LP, 2500, 2000, 0}, PFNZERO } // metallic S -#define PRC_RVA4 {PRC_RVA, {1.0, 0.5, 1.5, FLT_LP, 2500, 2000, 0}, PFNZERO } // metallic M -#define PRC_RVA5 {PRC_RVA, {1.2, 0.5, 1.5, FLT_LP, 2500, 2000, 0}, PFNZERO } // metallic L - -#define PRC_RVA6 {PRC_RVA, {0.8, 0.3, 1.5, FLT_LP, 4000, 2000, 0}, PFNZERO } // tunnel S -#define PRC_RVA7 {PRC_RVA, {0.9, 0.3, 1.5, FLT_LP, 4000, 2000, 0}, PFNZERO } // tunnel M -#define PRC_RVA8 {PRC_RVA, {1.0, 0.3, 1.5, FLT_LP, 4000, 2000, 0}, PFNZERO } // tunnel L - -#define PRC_RVA9 {PRC_RVA, {2.0, 1.5, 2.0, FLT_LP, 1500, 1500, 1}, PFNZERO } // cavern S -#define PRC_RVA10 {PRC_RVA, {2.0, 1.5, 2.0, FLT_LP, 1500, 1500, 1}, PFNZERO } // cavern M -#define PRC_RVA11 {PRC_RVA, {2.0, 1.5, 2.0, FLT_LP, 1500, 1500, 1}, PFNZERO } // cavern L - -#define PRC_RVA12 {PRC_RVA, {2.0, 0.5, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO } // chamber S -#define PRC_RVA13 {PRC_RVA, {2.0, 1.0, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO } // chamber M -#define PRC_RVA14 {PRC_RVA, {2.0, 2.0, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO } // chamber L - -#define PRC_RVA15 {PRC_RVA, {1.7, 1.0, 1.2, FLT_LP, 5000, 4000, 1}, PFNZERO } // brite S -#define PRC_RVA16 {PRC_RVA, {1.75, 1.0, 1.5, FLT_LP, 5000, 4000, 1}, PFNZERO } // brite M -#define PRC_RVA17 {PRC_RVA, {1.85, 1.0, 2.0, FLT_LP, 6000, 4000, 1}, PFNZERO } // brite L - -#define PRC_RVA18 {PRC_RVA, {1.0, 1.5, 1.0, FLT_LP, 1000, 1000, 0}, PFNZERO } // generic - -#define PRC_RVA19 {PRC_RVA, {1.9, 1.8, 1.25, FLT_LP, 4000, 2000, 1}, PFNZERO } // concrete S -#define PRC_RVA20 {PRC_RVA, {2.0, 1.8, 1.5, FLT_LP, 3500, 2000, 1}, PFNZERO } // concrete M -#define PRC_RVA21 {PRC_RVA, {2.0, 1.8, 1.75, FLT_LP, 3000, 2000, 1}, PFNZERO } // concrete L - -#define PRC_RVA22 {PRC_RVA, {1.8, 1.5, 1.5, FLT_LP, 1000, 1000, 0}, PFNZERO } // water S -#define PRC_RVA23 {PRC_RVA, {1.9, 1.75, 1.5, FLT_LP, 1000, 1000, 0}, PFNZERO } // water M -#define PRC_RVA24 {PRC_RVA, {2.0, 2.0, 1.5, FLT_LP, 1000, 1000, 0}, PFNZERO } // water L - - -///////////// -// Diffusors -///////////// - -// size: 0-1.0 scales all delays -// density: 0-1.0 controls # of series delays -// decay: 0-1.0 scales all feedback parameters - -// prctype size density decay -#define PRC_DFR1 {PRC_DFR, { 1.0, 0.5, 1.0 }, PFNZERO } -#define PRC_DFR2 {PRC_DFR, { 0.5, 0.3, 0.5 }, PFNZERO } // S -#define PRC_DFR3 {PRC_DFR, { 0.75, 0.5, 0.75 }, PFNZERO } // M -#define PRC_DFR4 {PRC_DFR, { 1.0, 0.5, 1.0 }, PFNZERO } // L -#define PRC_DFR5 {PRC_DFR, { 1.0, 1.0, 1.0 }, PFNZERO } // VL - -//////// -// LFOs -//////// - -// wavtype: lfo type to use (LFO_SIN, LFO_RND...) -// rate: modulation rate in hz. for MDY, 1/rate = 'glide' time in seconds -// foneshot: 1.0 if lfo is oneshot - -// prctype wavtype rate foneshot -#define PRC_LFO1 {PRC_LFO, { LFO_SIN, 440.0, 0.0, }, PFNZERO} -#define PRC_LFO2 {PRC_LFO, { LFO_SIN, 3000.0, 0.0, }, PFNZERO} // ear noise ring -#define PRC_LFO3 {PRC_LFO, { LFO_SIN, 4500.0, 0.0, }, PFNZERO} // ear noise ring -#define PRC_LFO4 {PRC_LFO, { LFO_SIN, 6000.0, 0.0, }, PFNZERO} // ear noise ring -#define PRC_LFO5 {PRC_LFO, { LFO_SAW, 100.0, 0.0, }, PFNZERO} // sub bass - -///////// -// Pitch -///////// - -// pitch: 0-n.0 where 1.0 = 1 octave up and 0.5 is one octave down -// timeslice: in milliseconds - size of sound chunk to analyze and cut/duplicate - 100ms nominal -// xfade: in milliseconds - size of crossfade region between spliced chunks - 20ms nominal - -// prctype pitch timeslice xfade -#define PRC_PTC1 {PRC_PTC, { 1.1, 100.0, 20.0 }, PFNZERO} // pitch up 10% -#define PRC_PTC2 {PRC_PTC, { 0.9, 100.0, 20.0 }, PFNZERO} // pitch down 10% -#define PRC_PTC3 {PRC_PTC, { 0.95, 100.0, 20.0 }, PFNZERO} // pitch down 5% -#define PRC_PTC4 {PRC_PTC, { 1.01, 100.0, 20.0 }, PFNZERO} // pitch up 1% -#define PRC_PTC5 {PRC_PTC, { 0.5, 100.0, 20.0 }, PFNZERO} // pitch down 50% - -///////////// -// Envelopes -///////////// - -// etype: ENV_LINEAR, ENV_LOG - currently ignored -// amp1: attack peak amplitude 0-1.0 -// amp2: decay target amplitued 0-1.0 -// amp3: sustain target amplitude 0-1.0 -// attack time in milliseconds -// envelope decay time in milliseconds -// sustain time in milliseconds -// release time in milliseconds - -// prctype etype amp1 amp2 amp3 attack decay sustain release -#define PRC_ENV1 {PRC_ENV, {ENV_LIN, 1.0, 0.5, 0.4, 500, 500, 3000, 6000 }, PFNZERO} - - -////////////// -// Mod delays -////////////// - -// dtype: delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS -// delay: delay in milliseconds -// feedback: feedback 0-1.0 -// gain: final gain of output stage, 0-1.0 - -// modrate: frequency at which delay values change to new random value. 0 is no self-modulation -// moddepth: how much delay changes (decreases) from current value (0-1.0) -// modglide: glide time between dcur and dnew in milliseconds - -// prctype dtype delay feedback gain ftype cutoff qwidth qual modrate moddepth modglide -#define PRC_MDY1 {PRC_MDY, {DLY_PLAIN, 500.0, 0.5, 1.0, 0, 0, 0, 0, 10, 0.8, 5,}, PFNZERO} -#define PRC_MDY2 {PRC_MDY, {DLY_PLAIN, 50.0, 0.8, 1.0, 0, 0, 0, 0, 5, 0.8, 5,}, PFNZERO} - -#define PRC_MDY3 {PRC_MDY, {DLY_PLAIN, 300.0, 0.2, 1.0, 0, 0, 0, 0, 30, 0.01, 15,}, PFNZERO } // weird 1 -#define PRC_MDY4 {PRC_MDY, {DLY_PLAIN, 400.0, 0.3, 1.0, 0, 0, 0, 0, 0.25, 0.01, 15,}, PFNZERO } // weird 2 -#define PRC_MDY5 {PRC_MDY, {DLY_PLAIN, 500.0, 0.4, 1.0, 0, 0, 0, 0, 0.25, 0.01, 15,}, PFNZERO } // weird 3 - -////////// -// Chorus -////////// - -// lfowav: lfotype is LFO_SIN, LFO_RND, LFO_TRI etc (LFO_RND for chorus, LFO_SIN for flange) -// rate: rate is modulation frequency in Hz -// depth: depth is modulation depth, 0-1.0 -// mix: mix is mix of chorus and clean signal - -// prctype lfowav rate depth mix -#define PRC_CRS1 {PRC_CRS, { LFO_SIN, 10, 1.0, 0.5, }, PFNZERO } - -///////////////////// -// Envelope follower -///////////////////// - -// takes no parameters -#define PRC_EFO1 {PRC_EFO, { PRMZERO }, PFNZERO } - -// init array of processors - first store pfnParam, pfnGetNext and pfnFree functions for type, -// then call the pfnParam function to initialize each processor - -// prcs - an array of prc structures, all with initialized params -// count - number of elements in the array -// returns false if failed to init one or more processors - -qboolean PRC_InitAll( prc_t *prcs, int count ) -{ - int i; - prc_Param_t pfnParam; // allocation function - takes ptr to prc, returns ptr to specialized data struct for proc type - prc_GetNext_t pfnGetNext; // get next function - prc_GetNextN_t pfnGetNextN; // get next function, batch version - prc_Free_t pfnFree; - prc_Mod_t pfnMod; - qboolean fok = true; - - // set up pointers to XXX_Free, XXX_GetNext and XXX_Params functions - - for( i = 0; i < count; i++ ) + if( sxdly_delay->modified ) { - switch (prcs[i].type) + if( delay == 0 ) { - case PRC_DLY: - pfnFree = &(prc_Free_t)DLY_Free; - pfnGetNext = &(prc_GetNext_t)DLY_GetNext; - pfnGetNextN = &(prc_GetNextN_t)DLY_GetNextN; - pfnParam = &DLY_VParams; - pfnMod = &(prc_Mod_t)DLY_Mod; - break; - case PRC_RVA: - pfnFree = &(prc_Free_t)RVA_Free; - pfnGetNext = &(prc_GetNext_t)RVA_GetNext; - pfnGetNextN = &(prc_GetNextN_t)RVA_GetNextN; - pfnParam = &RVA_VParams; - pfnMod = &(prc_Mod_t)RVA_Mod; - break; - case PRC_FLT: - pfnFree = &(prc_Free_t)FLT_Free; - pfnGetNext = &(prc_GetNext_t)FLT_GetNext; - pfnGetNextN = &(prc_GetNextN_t)FLT_GetNextN; - pfnParam = &FLT_VParams; - pfnMod = &(prc_Mod_t)FLT_Mod; - break; - case PRC_CRS: - pfnFree = &(prc_Free_t)CRS_Free; - pfnGetNext = &(prc_GetNext_t)CRS_GetNext; - pfnGetNextN = &(prc_GetNextN_t)CRS_GetNextN; - pfnParam = &CRS_VParams; - pfnMod = &(prc_Mod_t)CRS_Mod; - break; - case PRC_PTC: - pfnFree = &(prc_Free_t)PTC_Free; - pfnGetNext = &(prc_GetNext_t)PTC_GetNext; - pfnGetNextN = &(prc_GetNextN_t)PTC_GetNextN; - pfnParam = &PTC_VParams; - pfnMod = &(prc_Mod_t)PTC_Mod; - break; - case PRC_ENV: - pfnFree = &(prc_Free_t)ENV_Free; - pfnGetNext = &(prc_GetNext_t)ENV_GetNext; - pfnGetNextN = &(prc_GetNextN_t)ENV_GetNextN; - pfnParam = &ENV_VParams; - pfnMod = &(prc_Mod_t)ENV_Mod; - break; - case PRC_LFO: - pfnFree = &(prc_Free_t)LFO_Free; - pfnGetNext = &(prc_GetNext_t)LFO_GetNext; - pfnGetNextN = &(prc_GetNextN_t)LFO_GetNextN; - pfnParam = &LFO_VParams; - pfnMod = &(prc_Mod_t)LFO_Mod; - break; - case PRC_EFO: - pfnFree = &(prc_Free_t)EFO_Free; - pfnGetNext = &(prc_GetNext_t)EFO_GetNext; - pfnGetNextN = &(prc_GetNextN_t)EFO_GetNextN; - pfnParam = &EFO_VParams; - pfnMod = &(prc_Mod_t)EFO_Mod; - break; - case PRC_MDY: - pfnFree = &(prc_Free_t)MDY_Free; - pfnGetNext = &(prc_GetNext_t)MDY_GetNext; - pfnGetNextN = &(prc_GetNextN_t)MDY_GetNextN; - pfnParam = &MDY_VParams; - pfnMod = &(prc_Mod_t)MDY_Mod; - break; - case PRC_DFR: - pfnFree = &(prc_Free_t)DFR_Free; - pfnGetNext = &(prc_GetNext_t)DFR_GetNext; - pfnGetNextN = &(prc_GetNextN_t)DFR_GetNextN; - pfnParam = &DFR_VParams; - pfnMod = &(prc_Mod_t)DFR_Mod; - break; - case PRC_AMP: - pfnFree = &(prc_Free_t)AMP_Free; - pfnGetNext = &(prc_GetNext_t)AMP_GetNext; - pfnGetNextN = &(prc_GetNextN_t)AMP_GetNextN; - pfnParam = &_VParams; - pfnMod = &(prc_Mod_t)AMP_Mod; - break; - case PRC_NULL: - default: - pfnFree = &(prc_Free_t)NULL_Free; - pfnGetNext = &(prc_GetNext_t)NULL_GetNext; - pfnGetNextN = &(prc_GetNextN_t)NULL_GetNextN; - pfnParam = &NULL_VParams; - pfnMod = &(prc_Mod_t)NULL_Mod; - break; + DLY_Free( MONODLY ); } - - // set up function pointers - prcs[i].pfnParam = pfnParam; - prcs[i].pfnGetNext = pfnGetNext; - prcs[i].pfnGetNextN = pfnGetNextN; - prcs[i].pfnFree = pfnFree; - - // call param function, store pdata for the processor type - prcs[i].pdata = pfnParam((void *)( &prcs[i] )); - - if( !prcs[i].pdata ) - fok = false; - } - return fok; -} - -// free individual processor's data -void PRC_Free( prc_t *pprc ) -{ - if( pprc->pfnFree && pprc->pdata ) - pprc->pfnFree( pprc->pdata ); -} - -// free all processors for supplied array -// prcs - array of processors -// count - elements in array -void PRC_FreeAll( prc_t *prcs, int count ) -{ - int i; - - for( i = 0; i < count; i++ ) - PRC_Free( &prcs[i] ); -} - -// get next value for processor - (usually called directly by PSET_GetNext) -_inline int PRC_GetNext( prc_t *pprc, int x ) -{ - return pprc->pfnGetNext( pprc->pdata, x ); -} - -// automatic parameter range limiting -// force parameters between specified min/max in param_rng -void PRC_CheckParams( prc_t *pprc, prm_rng_t *prng ) -{ - // first entry in param_rng is # of parameters - int cprm = prng[0].iprm; - int i; - - for( i = 0; i < cprm; i++) - { - // if parameter is 0.0f, always allow it (this is 'off' for most params) - if( pprc->prm[i] != 0.0f && ( pprc->prm[i] > prng[i+1].hi || pprc->prm[i] < prng[i+1].lo )) + else { - MsgDev( D_WARN, "DSP: clamping out of range parameter.\n" ); - pprc->prm[i] = bound( prng[i+1].lo, pprc->prm[i], prng[i+1].hi ); - } - } -} + delay = min( delay, MAX_MONO_DELAY ); + dly->delaysamples = (int)(delay * SOUND_DMA_SPEED) << sxhires; -// DSP presets -// A dsp preset comprises one or more dsp processors in linear, parallel or feedback configuration -// preset configurations -// -#define PSET_SIMPLE 0 + // init dly + if( !dly->lpdelayline ) + DLY_Init( MONODLY, MAX_MONO_DELAY ); -// x(n)--->P(0)--->y(n) -#define PSET_LINEAR 1 - -// x(n)--->P(0)-->P(1)-->...P(m)--->y(n) -#define PSET_PARALLEL6 4 - -// x(n)-P(0)-->P(1)-->P(2)-->(+)-P(5)->y(n) -// | ^ -// | | -// -->P(3)-->P(4)----> - - -#define PSET_PARALLEL2 5 - -// x(n)--->P(0)-->(+)-->y(n) -// ^ -// | -// x(n)--->P(1)----- - -#define PSET_PARALLEL4 6 - -// x(n)--->P(0)-->P(1)-->(+)-->y(n) -// ^ -// | -// x(n)--->P(2)-->P(3)----- - -#define PSET_PARALLEL5 7 - -// x(n)--->P(0)-->P(1)-->(+)-->P(4)-->y(n) -// ^ -// | -// x(n)--->P(2)-->P(3)----- - -#define PSET_FEEDBACK 8 - -// x(n)-P(0)--(+)-->P(1)-->P(2)-->P(5)->y(n) -// ^ | -// | v -// -----P(4)<--P(3)-- - -#define PSET_FEEDBACK3 9 - -// x(n)---(+)-->P(0)--------->y(n) -// ^ | -// | v -// -----P(2)<--P(1)-- - -#define PSET_FEEDBACK4 10 - -// x(n)---(+)-->P(0)-------->P(3)--->y(n) -// ^ | -// | v -// ---P(2)<--P(1)-- - -#define PSET_MOD 11 - -// -// x(n)------>P(1)--P(2)--P(3)--->y(n) -// ^ -// x(n)------>P(0)....: - -#define PSET_MOD2 12 - -// -// x(n)-------P(1)-->y(n) -// ^ -// x(n)-->P(0)..: - - -#define PSET_MOD3 13 - -// -// x(n)-------P(1)-->P(2)-->y(n) -// ^ -// x(n)-->P(0)..: - - -#define CPSETS 64 // max number of presets simultaneously active - -#define CPSET_PRCS 6 // max # of processors per dsp preset -#define CPSET_STATES (CPSET_PRCS+3) // # of internal states - -// NOTE: do not reorder members of pset_t - psettemplates relies on it!!! -typedef struct -{ - int type; // preset configuration type - int cprcs; // number of processors for this preset - prc_t prcs[CPSET_PRCS]; // processor preset data - float gain; // preset gain 0.1->2.0 - int w[CPSET_STATES]; // internal states - int fused; -} pset_t; - -pset_t psets[CPSETS]; - -// array of dsp presets, each with up to 6 processors per preset - -#define WZERO {0,0,0,0,0,0,0,0,0}, 0 - -pset_t psettemplates[] = -{ -// presets 0-29 map to legacy room_type 0-29 - -// type # proc P0 P1 P2 P3 P4 P5 GAIN -{PSET_SIMPLE, 1, { PRC_NULL1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // OFF 0 -{PSET_SIMPLE, 1, { PRC_RVA18, PRC0, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // GENERIC 1 // general, low reflective, diffuse room -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA3, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // METALIC_S 2 // highly reflective, parallel surfaces -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA4, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // METALIC_M 3 -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA5, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // METALIC_L 4 -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA6, PRC0, PRC0, PRC0, PRC0 },2.0, WZERO }, // TUNNEL_S 5 // resonant reflective, long surfaces -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA7, PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // TUNNEL_M 6 -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA8, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // TUNNEL_L 7 -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA12,PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // CHAMBER_S 8 // diffuse, moderately reflective surfaces -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA13,PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // CHAMBER_M 9 -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA14,PRC0, PRC0, PRC0, PRC0 },1.9, WZERO }, // CHAMBER_L 10 -{PSET_SIMPLE, 1, { PRC_RVA15, PRC0, PRC0, PRC0, PRC0, PRC0 },1.5, WZERO }, // BRITE_S 11 // diffuse, highly reflective -{PSET_SIMPLE, 1, { PRC_RVA16, PRC0, PRC0, PRC0, PRC0, PRC0 },1.6, WZERO }, // BRITE_M 12 -{PSET_SIMPLE, 1, { PRC_RVA17, PRC0, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // BRITE_L 13 -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA22,PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WATER1 14 // underwater fx -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA23,PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WATER2 15 -{PSET_LINEAR, 3, { PRC_DFR1, PRC_RVA24,PRC_MDY5, PRC0, PRC0, PRC0 },1.8, WZERO }, // WATER3 16 -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA19,PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // CONCRTE_S 17 // bare, reflective, parallel surfaces -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA20,PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // CONCRTE_M 18 -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA21,PRC0, PRC0, PRC0, PRC0 },1.9, WZERO }, // CONCRTE_L 19 -{PSET_LINEAR, 2, { PRC_DFR1, PRC_DLY3, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // OUTSIDE1 20 // echoing, moderately reflective -{PSET_LINEAR, 2, { PRC_DFR1, PRC_DLY4, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // OUTSIDE2 21 // echoing, dull -{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_DLY5, PRC0, PRC0, PRC0 },1.6, WZERO }, // OUTSIDE3 22 // echoing, very dull -{PSET_LINEAR, 2, { PRC_DLY10, PRC_RVA10,PRC0, PRC0, PRC0, PRC0 },2.8, WZERO }, // CAVERN_S 23 // large, echoing area -{PSET_LINEAR, 2, { PRC_DLY11, PRC_RVA10,PRC0, PRC0, PRC0, PRC0 },2.6, WZERO }, // CAVERN_M 24 -{PSET_LINEAR, 3, { PRC_DFR1, PRC_DLY12,PRC_RVA11,PRC0, PRC0, PRC0 },2.6, WZERO }, // CAVERN_L 25 -{PSET_LINEAR, 2, { PRC_DLY7, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },2.0, WZERO }, // WEIRDO1 26 -{PSET_LINEAR, 2, { PRC_DLY8, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },1.9, WZERO }, // WEIRDO2 27 -{PSET_LINEAR, 2, { PRC_DLY9, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WEIRDO3 28 -{PSET_LINEAR, 2, { PRC_DLY9, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WEIRDO4 29 - -// presets 30-40 are new presets -{PSET_SIMPLE, 1, { PRC_FLT2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 30 lowpass - facing away -{PSET_LINEAR, 2, { PRC_FLT3, PRC_DLY14,PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 31 lowpass - facing away+80ms delay -//{PSET_PARALLEL2,2, { PRC_AMP6, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 32 explosion ring 1 -//{PSET_PARALLEL2,2, { PRC_AMP7, PRC_LFO3, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 33 explosion ring 2 -//{PSET_PARALLEL2,2, { PRC_AMP8, PRC_LFO4, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 34 explosion ring 3 -{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_FLT3, PRC0, PRC0, PRC0 },0.25, WZERO }, // 32 explosion ring -{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_FLT3, PRC0, PRC0, PRC0 },0.25, WZERO }, // 33 explosion ring 2 -{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_FLT3, PRC0, PRC0, PRC0 },0.25, WZERO }, // 34 explosion ring 3 -{PSET_PARALLEL2,2, { PRC_DFR1, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },0.25, WZERO }, // 35 shock muffle 1 -{PSET_PARALLEL2,2, { PRC_DFR1, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },0.25, WZERO }, // 36 shock muffle 2 -{PSET_PARALLEL2,2, { PRC_DFR1, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },0.25, WZERO }, // 37 shock muffle 3 -//{PSET_LINEAR, 3, { PRC_DFR1, PRC_LFO4, PRC_FLT3, PRC0, PRC0, PRC0 },1.0, WZERO }, // 35 shock muffle 1 -//{PSET_LINEAR, 3, { PRC_DFR1, PRC_LFO4, PRC_FLT3, PRC0, PRC0, PRC0 },1.0, WZERO }, // 36 shock muffle 2 -//{PSET_LINEAR, 3, { PRC_DFR1, PRC_LFO4, PRC_FLT3, PRC0, PRC0, PRC0 },1.0, WZERO }, // 37 shock muffle 3 -{PSET_FEEDBACK3,3, { PRC_DLY13, PRC_PTC4, PRC_FLT2, PRC0, PRC0, PRC0 },0.25, WZERO }, // 38 fade pitchdown 1 -{PSET_LINEAR, 3, { PRC_AMP3, PRC_FLT5, PRC_FLT6, PRC0, PRC0, PRC0 },2.0, WZERO }, // 39 distorted speaker 1 - -// fade out fade in - -// presets 40+ are test presets -{PSET_SIMPLE, 1, { PRC_NULL1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 39 null -{PSET_SIMPLE, 1, { PRC_DLY1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 40 delay -{PSET_SIMPLE, 1, { PRC_RVA1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 41 parallel reverb -{PSET_SIMPLE, 1, { PRC_DFR1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 42 series diffusor -{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA1, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 43 diff & reverb -{PSET_SIMPLE, 1, { PRC_DLY2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 44 lowpass delay -{PSET_SIMPLE, 1, { PRC_MDY2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 45 modulating delay -{PSET_SIMPLE, 1, { PRC_PTC1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 46 pitch shift -{PSET_SIMPLE, 1, { PRC_PTC2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 47 pitch shift -{PSET_SIMPLE, 1, { PRC_FLT1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 48 filter -{PSET_SIMPLE, 1, { PRC_CRS1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 49 chorus -{PSET_SIMPLE, 1, { PRC_ENV1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 50 -{PSET_SIMPLE, 1, { PRC_LFO1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 51 lfo -{PSET_SIMPLE, 1, { PRC_EFO1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 52 -{PSET_SIMPLE, 1, { PRC_MDY1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 53 modulating delay -{PSET_SIMPLE, 1, { PRC_FLT2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 54 lowpass - facing away -{PSET_PARALLEL2, 2, { PRC_PTC2, PRC_PTC1, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 55 ptc1/ptc2 -{PSET_FEEDBACK, 6, { PRC_DLY1, PRC0, PRC0, PRC_PTC1, PRC_FLT1, PRC0 },1.0, WZERO }, // 56 dly/ptc1 -{PSET_MOD, 4, { PRC_EFO1, PRC0, PRC_PTC1, PRC0, PRC0, PRC0 },1.0, WZERO }, // 57 efo mod ptc -{PSET_LINEAR, 3, { PRC_DLY1, PRC_RVA1, PRC_CRS1, PRC0, PRC0, PRC0 },1.0, WZERO } // 58 dly/rvb/crs -}; - - -// number of presets currently defined above - -#define CPSETTEMPLATES 60 //(sizeof( psets ) / sizeof( pset_t )) - -// init a preset - just clear state array -void PSET_Init( pset_t *ppset ) -{ - // clear state array - if( ppset ) Q_memset( ppset->w, 0, sizeof( int ) * ( CPSET_STATES )); -} - -// clear runtime slots -void PSET_InitAll( void ) -{ - int i; - - for( i = 0; i < CPSETS; i++ ) - Q_memset( &psets[i], 0, sizeof( pset_t )); -} - -// free the preset - free all processors - -void PSET_Free( pset_t *ppset ) -{ - if( ppset ) - { - // free processors - PRC_FreeAll( ppset->prcs, ppset->cprcs ); - - // clear - Q_memset( ppset, 0, sizeof( pset_t )); - } -} - -void PSET_FreeAll() { int i; for( i = 0; i < CPSETS; i++ ) PSET_Free( &psets[i] ); }; - -// return preset struct, given index into preset template array -// NOTE: should not ever be more than 2 or 3 of these active simultaneously -pset_t *PSET_Alloc( int ipsettemplate ) -{ - pset_t *ppset; - qboolean fok; - int i; - - // don't excede array bounds - if( ipsettemplate >= CPSETTEMPLATES ) - ipsettemplate = 0; - - // find free slot - for( i = 0; i < CPSETS; i++) - { - if( !psets[i].fused ) - break; - } - - if( i == CPSETS ) - return NULL; - - ppset = &psets[i]; - - // copy template into preset - *ppset = psettemplates[ipsettemplate]; - - ppset->fused = true; - - // clear state array - PSET_Init( ppset ); - - // init all processors, set up processor function pointers - fok = PRC_InitAll( ppset->prcs, ppset->cprcs ); - - if( !fok ) - { - // failed to init one or more processors - MsgDev( D_ERROR, "Sound DSP: preset failed to init.\n"); - PRC_FreeAll( ppset->prcs, ppset->cprcs ); - return NULL; - } - return ppset; -} - -// batch version of PSET_GetNext for linear array of processors. For performance. - -// ppset - preset array -// pbuffer - input sample data -// SampleCount - size of input buffer -// OP: OP_LEFT - process left channel in place -// OP_RIGHT - process right channel in place -// OP_LEFT_DUPLICATe - process left channel, duplicate into right - -_inline void PSET_GetNextN( pset_t *ppset, portable_samplepair_t *pbf, int SampleCount, int op ) -{ - prc_t *pprc; - int i, count = ppset->cprcs; - - switch( ppset->type ) - { - default: - case PSET_SIMPLE: - { - // x(n)--->P(0)--->y(n) - ppset->prcs[0].pfnGetNextN( ppset->prcs[0].pdata, pbf, SampleCount, op ); - break; - } - case PSET_LINEAR: - { - - // w0 w1 w2 - // x(n)--->P(0)-->P(1)-->...P(count-1)--->y(n) - - // w0 w1 w2 w3 w4 w5 - // x(n)--->P(0)-->P(1)-->P(2)-->P(3)-->P(4)-->y(n) - - // call batch processors in sequence - no internal state for batch processing - // point to first processor - - pprc = &ppset->prcs[0]; - - for( i = 0; i < count; i++ ) + if( dly->lpdelayline ) { - pprc->pfnGetNextN( pprc->pdata, pbf, SampleCount, op ); - pprc++; + Q_memset( dly->lpdelayline, 0, dly->cdelaysamplesmax * sizeof( int ) ); + dly->lp0 = dly->lp1 = dly->lp2 = 0; } - break; - } + + dly->idelayinput = 0; + dly->idelayoutput = dly->cdelaysamplesmax - dly->delaysamples; + + if( !dly->delaysamples ) + DLY_Free( MONODLY ); + + } } + + sxdly_delay->modified = false; + dly->lp = sxdly_lp->integer; + dly->delayfeedback = 255 * sxdly_feedback->value; } +/* +============= +DLY_DoDelay -// Get next sample from this preset. called once for every sample in buffer -// ppset is pointer to preset -// x is input sample -_inline int PSET_GetNext( pset_t *ppset, int x ) +Do delay processing +============= +*/ +void DLY_DoDelay( int count ) { - int *w = ppset->w; - prc_t *pprc; - int count = ppset->cprcs; - - // initialized 0'th element of state array + dly_t *const dly = &rgsxdly[MONODLY]; + portable_samplepair_t *paint = paintto; + int delay; - w[0] = x; + if( !dly->lpdelayline || !count ) + return; // inactive - switch( ppset->type ) + for( ; count; count--, paint++ ) { - default: - case PSET_SIMPLE: + delay = dly->lpdelayline[dly->idelayoutput]; + + // don't process if delay line and left/right samples are zero + if( delay || paint->left || paint->right ) { - // x(n)--->P(0)--->y(n) - return ppset->prcs[0].pfnGetNext (ppset->prcs[0].pdata, x); - } - case PSET_LINEAR: - { - // w0 w1 w2 - // x(n)--->P(0)-->P(1)-->...P(count-1)--->y(n) + // calculate delayed value from average + int val = (( paint->left + paint->right ) / 2 ) + + (( dly->delayfeedback * delay ) >> 8); + val = CLIP( val ); - // w0 w1 w2 w3 w4 w5 - // x(n)--->P(0)-->P(1)-->P(2)-->P(3)-->P(4)-->y(n) - - // call processors in reverse order, from count to 1 - - // point to last processor - - pprc = &ppset->prcs[count-1]; - - switch( count ) + if( dly->lp ) // lowpass { - default: - case 5: - w[5] = pprc->pfnGetNext (pprc->pdata, w[4]); - pprc--; - case 4: - w[4] = pprc->pfnGetNext (pprc->pdata, w[3]); - pprc--; - case 3: - w[3] = pprc->pfnGetNext (pprc->pdata, w[2]); - pprc--; - case 2: - w[2] = pprc->pfnGetNext (pprc->pdata, w[1]); - pprc--; - case 1: - w[1] = pprc->pfnGetNext (pprc->pdata, w[0]); + dly->lp0 = dly->lp1; + dly->lp1 = val; + val = ( dly->lp0 + dly->lp1 + (val << 1) ) >> 2; } - return w[count]; - } - case PSET_PARALLEL6: + dly->lpdelayline[dly->idelayinput] = val; + + val >>= 2; + + paint->left = CLIP( paint->left + val ); + paint->right = CLIP( paint->right + val ); + } + else { - // w0 w1 w2 w3 w6 w7 - // x(n)-P(0)-->P(1)-->P(2)-->(+)---P(5)--->y(n) - // | ^ - // | w4 w5 | - // -->P(3)-->P(4)----> - - pprc = &ppset->prcs[0]; - - // start with all adders - - w[6] = w[3] + w[5]; - - // top branch - evaluate in reverse order - - w[7] = pprc[5].pfnGetNext( pprc[5].pdata, w[6] ); - w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[2] ); - w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[1] ); - - // bottom branch - evaluate in reverse order - - w[5] = pprc[4].pfnGetNext( pprc[4].pdata, w[4] ); - w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[1] ); - - w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] ); - - return w[7]; - } - case PSET_PARALLEL2: - { // w0 w1 w3 - // x(n)--->P(0)-->(+)-->y(n) - // ^ - // w0 w2 | - // x(n)--->P(1)----- - - pprc = &ppset->prcs[0]; - - w[3] = w[1] + w[2]; - - w[1] = pprc->pfnGetNext( pprc->pdata, w[0] ); - pprc++; - w[2] = pprc->pfnGetNext( pprc->pdata, w[0] ); - - return w[3]; + dly->lpdelayline[dly->idelayinput] = 0; + dly->lp0 = dly->lp1 = 0; } - case PSET_PARALLEL4: - { - // w0 w1 w2 w5 - // x(n)--->P(0)-->P(1)-->(+)-->y(n) - // ^ - // w0 w3 w4 | - // x(n)--->P(2)-->P(3)----- - - pprc = &ppset->prcs[0]; - - w[5] = w[2] + w[4]; - - w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[1] ); - w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[3] ); - - w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] ); - w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[0] ); - - return w[5]; - } - - case PSET_PARALLEL5: - { - // w0 w1 w2 w5 w6 - // x(n)--->P(0)-->P(1)-->(+)-->P(4)-->y(n) - // ^ - // w0 w3 w4 | - // x(n)--->P(2)-->P(3)----- - - pprc = &ppset->prcs[0]; - - w[5] = w[2] + w[4]; - - w[6] = pprc[4].pfnGetNext( pprc[4].pdata, w[5] ); - - w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[1] ); - w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[3] ); - - w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] ); - w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[0] ); - - return w[6]; - } - - case PSET_FEEDBACK: - { - // w0 w1 w2 w3 w4 w7 - // x(n)-P(0)--(+)-->P(1)-->P(2)-->P(5)->y(n) - // ^ | - // | w6 w5 v - // -----P(4)<--P(3)-- - - pprc = &ppset->prcs[0]; - - // start with adders - - w[2] = w[1] + w[6]; - - // evaluate in reverse order - - w[7] = pprc[5].pfnGetNext( pprc[5].pdata, w[4] ); - w[6] = pprc[4].pfnGetNext( pprc[4].pdata, w[5] ); - w[5] = pprc[3].pfnGetNext( pprc[3].pdata, w[4] ); - w[4] = pprc[2].pfnGetNext( pprc[2].pdata, w[3] ); - w[3] = pprc[1].pfnGetNext( pprc[1].pdata, w[2] ); - w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] ); - - return w[7]; - } - case PSET_FEEDBACK3: - { - // w0 w1 w2 - // x(n)---(+)-->P(0)--------->y(n) - // ^ | - // | w4 w3 v - // -----P(2)<--P(1)-- - - pprc = &ppset->prcs[0]; - - // start with adders - - w[1] = w[0] + w[4]; - - // evaluate in reverse order - - w[4] = pprc[2].pfnGetNext( pprc[2].pdata, w[3] ); - w[3] = pprc[1].pfnGetNext( pprc[1].pdata, w[2] ); - w[2] = pprc[0].pfnGetNext( pprc[0].pdata, w[1] ); - - return w[2]; - } - case PSET_FEEDBACK4: - { - // w0 w1 w2 w5 - // x(n)---(+)-->P(0)-------->P(3)--->y(n) - // ^ | - // | w4 w3 v - // ---P(2)<--P(1)-- - - pprc = &ppset->prcs[0]; - - // start with adders - - w[1] = w[0] + w[4]; - - // evaluate in reverse order - - w[5] = pprc[3].pfnGetNext( pprc[3].pdata, w[2] ); - w[4] = pprc[2].pfnGetNext( pprc[2].pdata, w[3] ); - w[3] = pprc[1].pfnGetNext( pprc[1].pdata, w[2] ); - w[2] = pprc[0].pfnGetNext( pprc[0].pdata, w[1] ); - - return w[2]; - } - case PSET_MOD: - { - // w0 w1 w3 w4 - // x(n)------>P(1)--P(2)--P(3)--->y(n) - // w0 w2 ^ - // x(n)------>P(0)....: - - pprc = &ppset->prcs[0]; - - w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[3] ); - - w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[1] ); - - // modulate processor 2 - - pprc[2].pfnMod( pprc[2].pdata, ((float)w[2] / (float)PMAX)); - - // get modulator output - - w[2] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] ); - - w[1] = pprc[1].pfnGetNext( pprc[1].pdata, w[0] ); - - return w[4]; - } - case PSET_MOD2: - { - // w0 w2 - // x(n)---------P(1)-->y(n) - // w0 w1 ^ - // x(n)-->P(0)....: - - pprc = &ppset->prcs[0]; - - // modulate processor 1 - - pprc[1].pfnMod( pprc[1].pdata, ((float)w[1] / (float)PMAX)); - - // get modulator output - - w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] ); - - w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[0] ); - - return w[2]; - - } - case PSET_MOD3: - { - // w0 w2 w3 - // x(n)----------P(1)-->P(2)-->y(n) - // w0 w1 ^ - // x(n)-->P(0).....: - - pprc = &ppset->prcs[0]; - - w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[2] ); - - // modulate processor 1 - - pprc[1].pfnMod( pprc[1].pdata, ((float)w[1] / (float)PMAX)); - - // get modulator output - - w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] ); - - w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[0] ); - - return w[2]; - } + DLY_MovePointer( dly ); } } +/* +=========== +RVB_SetUpDly -///////////// -// DSP system -///////////// - -// Main interface - -// Whenever the preset # changes on any of these processors, the old processor is faded out, new is faded in. -// dsp_chan is optionally set when a sound is played - a preset is sent with the start_static/dynamic sound. -// -// sound1---->dsp_chan--> -------------(+)---->dsp_water--->dsp_player--->out -// sound2---->dsp_chan--> | | -// sound3---------------> ----dsp_room--- -// | | -// --dsp_indirect- - -// dsp_room - set this cvar to a preset # to change the room dsp. room fx are more prevalent farther from player. -// use: when player moves into a new room, all sounds played in room take on its reverberant character -// dsp_water - set this cvar (once) to a preset # for serial underwater sound. -// use: when player goes under water, all sounds pass through this dsp (such as low pass filter) -// dsp_player - set this cvar to a preset # to cause all sounds to run through the effect (serial, in-line). -// use: player is deafened, player fires special weapon, player is hit by special weapon. -// dsp_facingaway- set this cvar to a preset # appropriate for sounds which are played facing away from player (weapon,voice) - -// Dsp presets - -convar_t *dsp_room; // room dsp preset - sounds more distant from player (1ch) - -int ipset_room_prev; - -// legacy room_type support -convar_t *dsp_room_type; -int ipset_room_typeprev; - - -// DSP processors - -int idsp_room; -convar_t *dsp_stereo; // set to 1 for true stereo processing. 2x perf hit. - -// DSP preset executor -#define CDSPS 32 // max number dsp executors active -#define DSPCHANMAX 4 // max number of channels dsp can process (allocs a separte processor for each chan) - -typedef struct +Set up dly for reverb +=========== +*/ +void RVB_SetUpDly( int pos, float delay, int kmod ) { - qboolean fused; - int cchan; // 1-4 channels, ie: mono, FrontLeft, FrontRight, RearLeft, RearRight - pset_t *ppset[DSPCHANMAX]; // current preset (1-4 channels) - int ipset; // current ipreset - pset_t *ppsetprev[DSPCHANMAX]; // previous preset (1-4 channels) - int ipsetprev; // previous ipreset - float xfade; // crossfade time between previous preset and new - rmp_t xramp; // crossfade ramp -} dsp_t; + int samples; -dsp_t dsps[CDSPS]; + delay = min( delay, MAX_REVERB_DELAY ); + samples = (int)(delay * SOUND_DMA_SPEED) << sxhires; -void DSP_Init( int idsp ) -{ - dsp_t *pdsp; - - if( idsp < 0 || idsp > CDSPS ) - return; - - pdsp = &dsps[idsp]; - Q_memset( pdsp, 0, sizeof( dsp_t )); -} - -void DSP_Free( int idsp ) -{ - dsp_t *pdsp; - int i; - - if( idsp < 0 || idsp > CDSPS ) - return; - - pdsp = &dsps[idsp]; - - for( i = 0; i < pdsp->cchan; i++ ) + if( !rgsxdly[pos].lpdelayline ) { - if( pdsp->ppset[i] ) - PSET_Free( pdsp->ppset[i] ); - - if( pdsp->ppsetprev[i] ) - PSET_Free( pdsp->ppsetprev[i] ); + rgsxdly[pos].delaysamples = samples; + DLY_Init( pos, MAX_REVERB_DELAY ); } - Q_memset( pdsp, 0, sizeof( dsp_t )); -} + rgsxdly[pos].modcur = rgsxdly[pos].mod = (int)(kmod * SOUND_DMA_SPEED / SOUND_11k) << sxhires; -// Init all dsp processors - called once, during engine startup -void DSP_InitAll( void ) -{ - int idsp; - - // order is important, don't rearange. - FLT_InitAll(); - DLY_InitAll(); - RVA_InitAll(); - LFOWAV_InitAll(); - LFO_InitAll(); - - CRS_InitAll(); - PTC_InitAll(); - ENV_InitAll(); - EFO_InitAll(); - MDY_InitAll(); - AMP_InitAll(); - - PSET_InitAll(); - - for( idsp = 0; idsp < CDSPS; idsp++ ) - DSP_Init( idsp ); -} - -// free all resources associated with dsp - called once, during engine shutdown - -void DSP_FreeAll( void ) -{ - int idsp; - - // order is important, don't rearange. - for( idsp = 0; idsp < CDSPS; idsp++ ) - DSP_Free( idsp ); - - AMP_FreeAll(); - MDY_FreeAll(); - EFO_FreeAll(); - ENV_FreeAll(); - PTC_FreeAll(); - CRS_FreeAll(); - - LFO_FreeAll(); - LFOWAV_FreeAll(); - RVA_FreeAll(); - DLY_FreeAll(); - FLT_FreeAll(); -} - - -// allocate a new dsp processor chain, kill the old processor. Called by DSP_CheckNewPreset() -// ipset is new preset -// xfade is crossfade time when switching between presets (milliseconds) -// cchan is how many simultaneous preset channels to allocate (1-4) -// return index to new dsp -int DSP_Alloc( int ipset, float xfade, int cchan ) -{ - dsp_t *pdsp; - int i, idsp; - int cchans = bound( 1, cchan, DSPCHANMAX); - - // find free slot - for( idsp = 0; idsp < CDSPS; idsp++ ) + // set up crossfade, if delay has changed + if( rgsxdly[pos].delaysamples != samples ) { - if( !dsps[idsp].fused ) - break; + rgsxdly[pos].idelayoutputxf = rgsxdly[pos].idelayinput - samples; + if( rgsxdly[pos].idelayoutputxf < 0 ) + rgsxdly[pos].idelayoutputxf += rgsxdly[pos].cdelaysamplesmax; + rgsxdly[pos].xfade = 32; } - if( idsp == CDSPS ) - return -1; + if( !rgsxdly[pos].delaysamples ) + DLY_Free( pos ); - pdsp = &dsps[idsp]; - - DSP_Init( idsp ); - - pdsp->fused = true; - pdsp->cchan = cchans; - - // allocate a preset processor for each channel - pdsp->ipset = ipset; - pdsp->ipsetprev = 0; - - for( i = 0; i < pdsp->cchan; i++ ) - { - pdsp->ppset[i] = PSET_Alloc( ipset ); - pdsp->ppsetprev[i] = NULL; - } - - // set up crossfade time in seconds - pdsp->xfade = xfade / 1000.0f; - - RMP_SetEnd( &pdsp->xramp ); - - return idsp; } -// return gain for current preset associated with dsp -// get crossfade to new gain if switching from previous preset (from preset crossfader value) -// Returns 1.0 gain if no preset (preset 0) -float DSP_GetGain( int idsp ) +/* +=========== +RVB_CheckNewReverbVal + +Update reverb settings if we are in new room +=========== +*/ +void RVB_CheckNewReverbVal( void ) { - float gain_target = 0.0; - float gain_prev = 0.0; - float gain; - dsp_t *pdsp; - int r; - - if( idsp < 0 || idsp > CDSPS ) - return 1.0f; - - pdsp = &dsps[idsp]; + dly_t *const dly1 = &rgsxdly[REVERBPOS]; + dly_t *const dly2 = &rgsxdly[REVERBPOS + 1]; + float delay = sxrvb_size->value; - // get current preset's gain - if( pdsp->ppset[0] ) - gain_target = pdsp->ppset[0]->gain; - else gain_target = 1.0f; - - // if not crossfading, return current preset gain - if( RMP_HitEnd( &pdsp->xramp )) + if( sxrvb_size->modified ) { - // return current preset's gain - return gain_target; - } - - // get previous preset gain - - if( pdsp->ppsetprev[0] ) - gain_prev = pdsp->ppsetprev[0]->gain; - else gain_prev = 1.0; - - // if current gain = target preset gain, return - if( gain_target == gain_prev ) - { - if( gain_target == 0.0f ) - return 1.0f; - return gain_target; + if( delay == 0.0f ) + { + DLY_Free( REVERBPOS ); + DLY_Free( REVERBPOS + 1 ); + } + else + { + RVB_SetUpDly( REVERBPOS, sxrvb_size->value, 500 ); + RVB_SetUpDly( REVERBPOS+1, sxrvb_size->value * 0.71f, 700 ); + } } - // get crossfade ramp value (updated elsewhere, when actually crossfading preset data) - r = RMP_GetCurrent( &pdsp->xramp ); + sxrvb_size->modified = false; + dly1->lp = dly2->lp = sxrvb_lp->integer; + dly1->delayfeedback = dly2->delayfeedback = (int)(255 * sxrvb_feedback->value); +} - // crossfade from previous to current preset gain - if( gain_target > gain_prev ) +/* +=========== +RVB_DoReverbForOneDly + +Do reverberation for one dly +=========== +*/ +int RVB_DoReverbForOneDly( dly_t *dly, const int vlr, const portable_samplepair_t *samplepair ) +{ + int delay; + int samplexf; + int val, valt; + int voutm = 0; + + if( --dly->modcur < 0 ) + dly->modcur = dly->mod; + + delay = dly->lpdelayline[dly->idelayoutput]; + + if( dly->xfade || delay || samplepair->left || samplepair->right ) { - // ramping gain up - ramp up gain to target in last 10% of ramp - float rf = (float)r; - float pmax = (float)PMAX; + // modulate delay rate + if( !dly->xfade && !dly->modcur && dly->mod ) + dly->idelayoutputxf = dly->idelayoutput + ((Com_RandomLong( 0, 255 ) * delay) >> 9 ); - rf = rf / pmax; // rf 0->1.0 + dly->idelayoutputxf %= dly->cdelaysamplesmax; - if( rf < 0.9 ) rf = 0.0; - else rf = (rf - 0.9) / (1.0 - 0.9); // 0->1.0 after rf > 0.9 + if( dly->xfade ) + { + samplexf = (dly->lpdelayline[dly->idelayoutputxf] * (32 - dly->xfade)) / 32; + delay = delay * dly->xfade / 32 + samplexf; - // crossfade gain from prev to target over rf - gain = gain_prev + (gain_target - gain_prev) * rf; + if( ++dly->idelayoutputxf >= dly->cdelaysamplesmax ) + dly->idelayoutputxf = 0; - return gain; + if( --dly->xfade == 0 ) + dly->idelayoutput = dly->idelayoutputxf; + } + + val = delay ? vlr + ((dly->delayfeedback * delay) >> 8) : vlr; + val = CLIP( val ); + + if( dly->lp ) + { + valt = (dly->lp0 + val) >> 1; + dly->lp0 = val; + } + else valt = val; + + voutm = dly->lpdelayline[dly->idelayinput] = valt; } else { - // ramping gain down - drop gain to target in first 10% of ramp - float rf = (float) r; - float pmax = (float)PMAX; - - rf = rf / pmax; // rf 0.0->1.0 - - if( rf < 0.1 ) rf = (rf - 0.1) / (0.0 - 0.1); // 1.0->0.0 if rf < 0.1 - else rf = 0.0; - - // crossfade gain from prev to target over rf - gain = gain_prev + (gain_target - gain_prev) * (1.0 - rf); - - return gain; + voutm = dly->lpdelayline[dly->idelayinput] = 0; + dly->lp0 = dly->lp1 = 0; } -} -// free previous preset if not 0 -_inline void DSP_FreePrevPreset( dsp_t *pdsp ) -{ - // free previous presets if non-null - ie: rapid change of preset just kills old without xfade - if( pdsp->ipsetprev ) - { - int i; + DLY_MovePointer( dly ); - for( i = 0; i < pdsp->cchan; i++ ) - { - if( pdsp->ppsetprev[i] ) - { - PSET_Free( pdsp->ppsetprev[i] ); - pdsp->ppsetprev[i] = NULL; - } - } - pdsp->ipsetprev = 0; - } + return voutm; } -// alloc new preset if different from current -// xfade from prev to new preset -// free previous preset, copy current into previous, set up xfade from previous to new -void DSP_SetPreset( int idsp, int ipsetnew ) +/* +=========== +RVB_DoReverb + +Do reverberation processing +=========== +*/ +void RVB_DoReverb( int count ) { - dsp_t *pdsp; - pset_t *ppsetnew[DSPCHANMAX]; - int i; + dly_t *const dly1 = &rgsxdly[REVERBPOS]; + dly_t *const dly2 = &rgsxdly[REVERBPOS+1]; + portable_samplepair_t *paint = paintto; + int vlr, voutm; - ASSERT( idsp >= 0 && idsp < CDSPS ); - - pdsp = &dsps[idsp]; - - // validate new preset range - if( ipsetnew >= CPSETTEMPLATES || ipsetnew < 0 ) + if( !dly1->lpdelayline || !count ) return; - // ignore if new preset is same as current preset - if( ipsetnew == pdsp->ipset ) + for( ; count; count--, paint++ ) + { + vlr = ( paint->left + paint->right ) >> 1; + + voutm = RVB_DoReverbForOneDly( dly1, vlr, paint ); + voutm += RVB_DoReverbForOneDly( dly2, vlr, paint ); + + voutm = 11 * voutm >> 6; + + paint->left = CLIP( paint->left + voutm); + paint->right = CLIP( paint->right + voutm); + } +} + +/* +=========== +RVB_DoAMod + +Do amplification modulation processing +=========== +*/ +void RVB_DoAMod( int count ) +{ + portable_samplepair_t *paint = paintto; + + if( !sxmod_lowpass->integer && !sxmod_mod->integer ) return; - // alloc new presets (each channel is a duplicate preset) - ASSERT( pdsp->cchan <= DSPCHANMAX ); + if( !count ) return; - for( i = 0; i < pdsp->cchan; i++ ) + for( ; count; count--, paint++ ) { - ppsetnew[i] = PSET_Alloc( ipsetnew ); + portable_samplepair_t res = *paint; - if( !ppsetnew[i] ) + if( sxmod_lowpass->value ) { - MsgDev( D_NOTE, "DSP preset failed to allocate.\n" ); - return; + res.left = rgsxlp[0] + rgsxlp[1] + rgsxlp[2] + rgsxlp[3] + rgsxlp[4] + res.left; + res.right = rgsxlp[5] + rgsxlp[6] + rgsxlp[7] + rgsxlp[8] + rgsxlp[9] + res.right; + + res.left >>= 2; + res.right >>= 2; + + rgsxlp[0] = rgsxlp[1]; + rgsxlp[1] = rgsxlp[2]; + rgsxlp[2] = rgsxlp[3]; + rgsxlp[3] = rgsxlp[4]; + rgsxlp[4] = paint->left; + + rgsxlp[5] = rgsxlp[6]; + rgsxlp[6] = rgsxlp[7]; + rgsxlp[7] = rgsxlp[8]; + rgsxlp[8] = rgsxlp[9]; + rgsxlp[9] = paint->right; } - } - ASSERT( pdsp ); + if( sxmod_mod->integer ) + { + if( --sxmod1cur < 0 ) + sxmod1cur = sxmod1; - // free PREVIOUS previous preset if not 0 - DSP_FreePrevPreset( pdsp ); + if( !sxmod1 ) + sxamodlt = Com_RandomLong( 32, 255 ); - for( i = 0; i < pdsp->cchan; i++ ) - { - // current becomes previous - pdsp->ppsetprev[i] = pdsp->ppset[i]; - - // new becomes current - pdsp->ppset[i] = ppsetnew[i]; - } - - pdsp->ipsetprev = pdsp->ipset; - pdsp->ipset = ipsetnew; + if( --sxmod2cur < 0 ) + sxmod2cur = sxmod2; - // clear ramp - RMP_SetEnd( &pdsp->xramp ); - - // make sure previous dsp preset has data - ASSERT( pdsp->ppsetprev[0] ); + if( !sxmod2 ) + sxamodrt = Com_RandomLong( 32, 255 ); - // shouldn't be crossfading if current dsp preset == previous dsp preset - ASSERT( pdsp->ipset != pdsp->ipsetprev ); + res.left = (sxamodl * res.left) >> 8; + res.right = (sxamodr * res.right) >> 8; - RMP_Init( &pdsp->xramp, pdsp->xfade, 0, PMAX ); -} + if( sxamodl < sxamodlt ) + sxamodl++; + else if( sxamodl > sxamodlt ) + sxamodl--; -/////////////////////////////////////// -// Helpers: called only from DSP_Process -/////////////////////////////////////// + if( sxamodr < sxamodrt ) + sxamodr++; + else if( sxamodr > sxamodrt ) + sxamodr--; + } -// return true if batch processing version of preset exists -_inline qboolean FBatchPreset( pset_t *ppset ) -{ - switch( ppset->type ) - { - case PSET_LINEAR: - return true; - case PSET_SIMPLE: - return true; - default: - return false; + paint->left = CLIP(res.left); + paint->right = CLIP(res.right); } } -// Helper: called only from DSP_Process -// mix front stereo buffer to mono buffer, apply dsp fx -_inline void DSP_ProcessStereoToMono( dsp_t *pdsp, portable_samplepair_t *pbfront, int sampleCount, qboolean bcrossfading ) -{ - portable_samplepair_t *pbf = pbfront; // pointer to buffer of front stereo samples to process - int count = sampleCount; - int av, x; - - if( !bcrossfading ) - { - if( FBatchPreset( pdsp->ppset[0] )) - { - // convert Stereo to Mono in place, then batch process fx: perf KDB - - // front->left + front->right / 2 into front->left, front->right duplicated. - while( count-- ) - { - pbf->left = (pbf->left + pbf->right) >> 1; - pbf++; - } - - // process left (mono), duplicate output into right - PSET_GetNextN( pdsp->ppset[0], pbfront, sampleCount, OP_LEFT_DUPLICATE); - } - else - { - // avg left and right -> mono fx -> duplcate out left and right - while( count-- ) - { - av = ( ( pbf->left + pbf->right ) >> 1 ); - x = PSET_GetNext( pdsp->ppset[0], av ); - x = CLIP_DSP( x ); - pbf->left = pbf->right = x; - pbf++; - } - } - return; - } - - // crossfading to current preset from previous preset - if( bcrossfading ) - { - int r = -1; - int fl, flp; - int xf_fl; - - while( count-- ) - { - av = ( ( pbf->left + pbf->right ) >> 1 ); - - // get current preset values - fl = PSET_GetNext( pdsp->ppset[0], av ); - - // get previous preset values - flp = PSET_GetNext( pdsp->ppsetprev[0], av ); - - fl = CLIP_DSP(fl); - flp = CLIP_DSP(flp); - - // get current ramp value - r = RMP_GetNext( &pdsp->xramp ); - - // crossfade from previous to current preset - xf_fl = XFADE( fl, flp, r ); // crossfade front left previous to front left - - pbf->left = xf_fl; // crossfaded front left, duplicate in right channel - pbf->right = xf_fl; - - pbf++; - - } - - } -} - -// Helper: called only from DSP_Process -// DSP_Process stereo in to stereo out (if more than 2 procs, ignore them) -_inline void DSP_ProcessStereoToStereo( dsp_t *pdsp, portable_samplepair_t *pbfront, int sampleCount, qboolean bcrossfading ) -{ - portable_samplepair_t *pbf = pbfront; // pointer to buffer of front stereo samples to process - int count = sampleCount; - int fl, fr; - - if( !bcrossfading ) - { - - if( FBatchPreset( pdsp->ppset[0] ) && FBatchPreset( pdsp->ppset[1] )) - { - // process left & right - PSET_GetNextN( pdsp->ppset[0], pbfront, sampleCount, OP_LEFT ); - PSET_GetNextN( pdsp->ppset[1], pbfront, sampleCount, OP_RIGHT ); - } - else - { - // left -> left fx, right -> right fx - while( count-- ) - { - fl = PSET_GetNext( pdsp->ppset[0], pbf->left ); - fr = PSET_GetNext( pdsp->ppset[1], pbf->right ); - - fl = CLIP_DSP( fl ); - fr = CLIP_DSP( fr ); - - pbf->left = fl; - pbf->right = fr; - pbf++; - } - } - return; - } - - // crossfading to current preset from previous preset - if( bcrossfading ) - { - int r, flp, frp; - int xf_fl, xf_fr; - - while( count-- ) - { - // get current preset values - fl = PSET_GetNext( pdsp->ppset[0], pbf->left ); - fr = PSET_GetNext( pdsp->ppset[1], pbf->right ); - - // get previous preset values - flp = PSET_GetNext( pdsp->ppsetprev[0], pbf->left ); - frp = PSET_GetNext( pdsp->ppsetprev[1], pbf->right ); - - // get current ramp value - r = RMP_GetNext( &pdsp->xramp ); - - fl = CLIP_DSP( fl ); - fr = CLIP_DSP( fr ); - flp = CLIP_DSP( flp ); - frp = CLIP_DSP( frp ); - - // crossfade from previous to current preset - xf_fl = XFADE( fl, flp, r ); // crossfade front left previous to front left - xf_fr = XFADE( fr, frp, r ); - - pbf->left = xf_fl; // crossfaded front left - pbf->right = xf_fr; - - pbf++; - } - } -} - -void DSP_ClearState( void ) -{ - if( !dsp_room ) return; // not init - - Cvar_SetFloat( "dsp_room", 0.0f ); - Cvar_SetFloat( "room_type", 0.0f ); - - CheckNewDspPresets(); - - // don't crossfade - dsps[0].xramp.fhitend = true; -} - -// Main DSP processing routine: -// process samples in buffers using pdsp processor -// continue crossfade between 2 dsp processors if crossfading on switch -// pfront - front stereo buffer to process -// prear - rear stereo buffer to process (may be NULL) -// sampleCount - number of samples in pbuf to process -// This routine also maps the # processing channels in the pdsp to the number of channels -// supplied. ie: if the pdsp has 4 channels and pbfront and pbrear are both non-null, the channels -// map 1:1 through the processors. +/* +=========== +DSP_Process +(xash dsp interface) +=========== +*/ void DSP_Process( int idsp, portable_samplepair_t *pbfront, int sampleCount ) { - qboolean bcrossfading; - int cprocs; // output cannels (1, 2 or 4) - dsp_t *pdsp; - - if( idsp < 0 || idsp >= CDSPS ) + if( dsp_off->value != 0.0f ) return; - ASSERT ( idsp < CDSPS ); // make sure idsp is valid - - pdsp = &dsps[idsp]; - - // if current and previous preset 0, return - preset 0 is 'off' - if( !pdsp->ipset && !pdsp->ipsetprev ) + // HACKHACK: don't process while in menu + if( cls.key_dest == key_menu ) return; - ASSERT( pbfront ); + // preset is already installed by CheckNewDspPresets + paintto = pbfront; - // return right away if fx processing is turned off - if( dsp_off->integer ) - return; - - if( sampleCount < 0 ) - return; - - bcrossfading = !RMP_HitEnd( &pdsp->xramp ); - - // if not crossfading, and previous channel is not null, free previous - if( !bcrossfading ) DSP_FreePrevPreset( pdsp ); - - cprocs = pdsp->cchan; - - // NOTE: when mixing between different channel sizes, - // always AVERAGE down to fewer channels and DUPLICATE up more channels. - // The following routines always process cchan_in channels. - // ie: QuadToMono still updates 4 values in buffer - - // DSP_Process stereo in to mono out (ie: left and right are averaged) - if( cprocs == 1 ) - { - DSP_ProcessStereoToMono( pdsp, pbfront, sampleCount, bcrossfading ); - return; - } - - // DSP_Process stereo in to stereo out (if more than 2 procs, ignore them) - if( cprocs >= 2 ) - { - DSP_ProcessStereoToStereo( pdsp, pbfront, sampleCount, bcrossfading ); - return; - } + RVB_DoAMod( sampleCount ); + RVB_DoReverb( sampleCount ); + DLY_DoDelay( sampleCount ); + DLY_DoStereoDelay( sampleCount ); } -// DSP helpers +/* +=========== +DSP_ClearState -// free all dsp processors -void FreeDsps( void ) +(xash dsp interface) +=========== +*/ +void DSP_ClearState( void ) { - DSP_Free( idsp_room ); - idsp_room = 0; - - DSP_FreeAll(); + Cvar_SetFloat( "room_type", 0.0f ); + SX_ReloadRoomFX(); } -// alloc dsp processors +/* +=========== +AllocDsps + +(xash dsp interface) +=========== +*/ qboolean AllocDsps( void ) { - DSP_InitAll(); + SX_Init(); - idsp_room = -1.0; - - // initialize DSP cvars - dsp_room = Cvar_Get( "dsp_room", "0", 0, "room dsp preset - sounds more distant from player (1ch)" ); - dsp_room_type = Cvar_Get( "room_type", "0", 0, "duplicate for dsp_room cvar for backward compatibility" ); - dsp_stereo = Cvar_Get( "dsp_stereo", "0", 0, "set to 1 for true stereo processing. 2x perf hits" ); - - // alloc dsp room channel (mono, stereo if dsp_stereo is 1) - - // dsp room is mono, 300ms fade time - idsp_room = DSP_Alloc( dsp_room->integer, 300, dsp_stereo->integer * 2 ); - - // init prev values - ipset_room_prev = dsp_room->integer; - ipset_room_typeprev = dsp_room_type->integer; - - if( idsp_room < 0 ) - { - MsgDev( D_WARN, "DSP processor failed to initialize! \n" ); - - FreeDsps(); - return false; - } - return true; + return 1; } +/* +=========== +FreeDsps -// Helper to check for change in preset of any of 4 processors -// if switching to a new preset, alloc new preset, simulate both presets in DSP_Process & xfade, +(xash dsp interface) +=========== +*/ +void FreeDsps( void ) +{ + SX_Free(); +} + +/* +=========== +CheckNewDspPresets + +(xash dsp interface) +=========== +*/ void CheckNewDspPresets( void ) { - int iroomtype = dsp_room_type->integer; - int iroom; + sxhires = 1; + sxhiresprev = 1; - if( dsp_off->integer ) + if( dsp_off->value != 0.0f ) return; - if( s_listener.waterlevel > 2 ) - iroom = 15; - else if( s_listener.inmenu ) - iroom = 0; - else iroom = dsp_room->integer; + if( cl.frame.client.waterlevel > 2 ) + idsp_room = roomwater_type->value; + else idsp_room = room_type->value; - // legacy code support for "room_type" Cvar - if( iroomtype != ipset_room_typeprev ) + if( idsp_room == room_typeprev && idsp_room == 0 ) + return; + + if( idsp_room > MAX_ROOM_TYPES ) + return; + + if( idsp_room != room_typeprev ) { - // force dsp_room = room_type - ipset_room_typeprev = iroomtype; - Cvar_SetFloat( "dsp_room", iroomtype ); + const sx_preset_t *cur = rgsxpre + idsp_room; + + Cvar_SetFloat( "room_lp", cur->room_lp ); + Cvar_SetFloat( "room_mod", cur->room_mod ); + Cvar_SetFloat( "room_size", cur->room_size ); + Cvar_SetFloat( "room_refl", cur->room_refl ); + Cvar_SetFloat( "room_rvblp", cur->room_rvblp ); + Cvar_SetFloat( "room_delay", cur->room_delay ); + Cvar_SetFloat( "room_feedback", cur->room_feedback ); + Cvar_SetFloat( "room_dlylp", cur->room_dlylp ); + Cvar_SetFloat( "room_left", cur->room_left ); } - if( iroom != ipset_room_prev ) - { - DSP_SetPreset( idsp_room, iroom ); - ipset_room_prev = iroom; + room_typeprev = idsp_room; - // force room_type = dsp_room - Cvar_SetFloat( "room_type", iroom ); - ipset_room_typeprev = iroom; + RVB_CheckNewReverbVal( ); + DLY_CheckNewDelayVal( ); + DLY_CheckNewStereoDelayVal(); +} + +/* +=========== +DSP_GetGain + +(xash dsp interface) +=========== +*/ +float DSP_GetGain( int idsp ) +{ + return 1.0f; +} + +void SX_Profiling_f( void ) +{ + portable_samplepair_t testbuffer[512]; + int i, calls = 10000; + double start, end; + float oldroom = room_type->value; + + for( i = 0; i < 512; i++ ) + { + testbuffer[i].left = Com_RandomLong( 0, 3000 ); + testbuffer[i].right = Com_RandomLong( 0, 3000 ); + } + + if( Cmd_Argc() > 1 ) + { + Cvar_SetFloat( "room_type", Q_atof( Cmd_Argv( 1 ))); + SX_ReloadRoomFX(); + CheckNewDspPresets(); // we just need idsp_room immediately, for message below + } + + MsgDev( D_INFO, "Profiling 10000 calls to DSP. Sample count is 512, room_type is %i\n", idsp_room ); + + start = Sys_DoubleTime(); + for( ; calls; calls-- ) + { + DSP_Process( idsp_room, testbuffer, 512 ); + } + end = Sys_DoubleTime(); + + MsgDev( D_INFO, "----------\nTook %g seconds.\n", end - start ); + + if( Cmd_Argc() > 1 ) + { + Cvar_SetFloat( "room_type", oldroom ); + SX_ReloadRoomFX(); + CheckNewDspPresets(); } } \ No newline at end of file diff --git a/engine/client/s_main.c b/engine/client/s_main.c index e42a055b..38339dcf 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -51,7 +51,6 @@ convar_t *snd_gain_max; convar_t *snd_gain_min; convar_t *s_refdist; convar_t *s_refdb; -convar_t *dsp_off; // set to 1 to disable all dsp processing convar_t *s_cull; // cull sounds by geometry convar_t *s_test; // cvar for testing new effects convar_t *s_phs; @@ -357,8 +356,7 @@ channel_t *SND_PickStaticChannel( int entnum, sfx_t *sfx, const vec3_t pos ) { channel_t *ch = NULL; int i, dupe = 0; - -#if 1 +#if 0 // TODO: remove this code when predicting is will be done // check for duplicate sounds for( i = 0; i < total_channels; i++ ) @@ -1609,7 +1607,7 @@ void S_RenderFrame( ref_params_t *fd ) else VectorSet( info.color, 1.0f, 1.0f, 1.0f ); info.index = 0; - Con_NXPrintf( &info, "----(%i)---- painted: %i\n", total - 1, paintedtime ); + Con_NXPrintf( &info, "room_type: %i ----(%i)---- painted: %i\n", idsp_room, total - 1, paintedtime ); } S_StreamBackgroundTrack (); @@ -1773,7 +1771,6 @@ qboolean S_Init( void ) s_mixahead = Cvar_Get( "_snd_mixahead", "0.12", 0, "how much sound to mix ahead of time" ); s_show = Cvar_Get( "s_show", "0", CVAR_ARCHIVE, "show playing sounds" ); s_lerping = Cvar_Get( "s_lerping", "0", CVAR_ARCHIVE, "apply interpolation to sound output" ); - dsp_off = Cvar_Get( "dsp_off", "0", CVAR_ARCHIVE, "set to 1 to disable all dsp processing" ); s_ambient_level = Cvar_Get( "ambient_level", "0.3", 0, "volume of environment noises (water and wind)" ); s_ambient_fade = Cvar_Get( "ambient_fade", "100", 0, "rate of volume fading when client is moving" ); s_combine_sounds = Cvar_Get( "s_combine_channels", "1", CVAR_ARCHIVE, "combine channels with same sounds" ); diff --git a/engine/common/build.c b/engine/common/build.c index 8822c162..d158a082 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -23,7 +23,7 @@ static char mond[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int Q_buildnum( void ) { // do not touch this! Only author of Xash3D can increase buildnumbers! -#if 0 +#if 1 int m = 0, d = 0, y = 0; static int b = 0; @@ -48,6 +48,6 @@ int Q_buildnum( void ) return b; #else - return 3366; + return 3387; #endif } \ No newline at end of file diff --git a/engine/common/cmd.c b/engine/common/cmd.c index ecd3ca9e..28730605 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -17,8 +17,8 @@ GNU General Public License for more details. #include "client.h" #include "server.h" -#define MAX_CMD_BUFFER 16384 -#define MAX_CMD_LINE 1024 +#define MAX_CMD_BUFFER 32768 +#define MAX_CMD_LINE 2048 typedef struct { diff --git a/engine/common/common.h b/engine/common/common.h index 2acfc5d3..41d22730 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -39,6 +39,8 @@ extern "C" { #define MAX_MODS 512 // environment games that engine can keep visible #define EXPORT __declspec( dllexport ) #define BIT( n ) (1<<( n )) +#define GAMMA ( 2.2 ) // Valve Software gamma +#define INVGAMMA ( 1.0 / 2.2 ) // back to 1.0 #ifndef __cplusplus #define NULL ((void *)0) @@ -901,6 +903,8 @@ void S_StopAllSounds( void ); void BuildGammaTable( float gamma, float texGamma ); byte TextureToTexGamma( byte b ); byte TextureToGamma( byte b ); +float TextureToLinear( int c ); +int LinearToTexture( float f ); #ifdef __cplusplus } diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index f0693ea2..d0754071 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -3057,11 +3057,17 @@ char W_HintFromSuf( const char *lumpname ) { char barename[64]; char suffix[8]; + size_t namelen; const wadtype_t *hint; // trying to extract hint from the name FS_FileBase( lumpname, barename ); - Q_strncpy( suffix, barename + Q_strlen( barename ) - HINT_NAMELEN, sizeof( suffix )); + namelen = Q_strlen( barename ); + + if( namelen <= HINT_NAMELEN ) + return IMG_DIFFUSE; + + Q_strncpy( suffix, barename + namelen - HINT_NAMELEN, sizeof( suffix )); // we not known about filetype, so match only by filename for( hint = wad_hints; hint->ext; hint++ ) @@ -3079,6 +3085,7 @@ static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char match char img_type = IMG_DIFFUSE; char barename[64], suffix[8]; int left, right; + size_t namelen; const wadtype_t *hint; if( !wad || !wad->lumps || matchtype == TYP_NONE ) @@ -3086,20 +3093,25 @@ static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char match // trying to extract hint from the name FS_FileBase( name, barename ); - Q_strncpy( suffix, barename + Q_strlen( barename ) - HINT_NAMELEN, sizeof( suffix )); + namelen = Q_strlen( barename ); - // we not known about filetype, so match only by filename - for( hint = wad_hints; hint->ext; hint++ ) + if( namelen > HINT_NAMELEN ) { - if( !Q_stricmp( suffix, hint->ext )) - { - img_type = hint->type; - break; - } - } + Q_strncpy( suffix, barename + namelen - HINT_NAMELEN, sizeof( suffix )); - if( img_type != IMG_DIFFUSE ) - barename[Q_strlen( barename ) - HINT_NAMELEN] = '\0'; // kill the suffix + // we not known about filetype, so match only by filename + for( hint = wad_hints; hint->ext; hint++ ) + { + if( !Q_stricmp( suffix, hint->ext )) + { + img_type = hint->type; + break; + } + } + + if( img_type != IMG_DIFFUSE ) + barename[namelen - HINT_NAMELEN] = '\0'; // kill the suffix + } // look for the file (binary search) left = 0; diff --git a/engine/common/gamma.c b/engine/common/gamma.c index c0632896..1eca92d4 100644 --- a/engine/common/gamma.c +++ b/engine/common/gamma.c @@ -22,6 +22,8 @@ GNU General Public License for more details. //----------------------------------------------------------------------------- static byte gammatable[256]; static byte texgammatable[256]; // palette is sent through this to convert to screen gamma +static float texturetolinear[256]; // texture (0..255) to linear (0..1) +static int lineartotexture[1024]; // linear (0..1) to texture (0..255) void BuildGammaTable( float gamma, float texGamma ) { @@ -47,6 +49,18 @@ void BuildGammaTable( float gamma, float texGamma ) inf = (int)(f + 0.5f); gammatable[i] = bound( 0, inf, 255 ); } + + for( i = 0; i < 256; i++ ) + { + // convert from nonlinear texture space (0..255) to linear space (0..1) + texturetolinear[i] = pow( i / 255.0, GAMMA ); + } + + for( i = 0; i < 1024; i++ ) + { + // convert from linear space (0..1) to nonlinear texture space (0..255) + lineartotexture[i] = pow( i / 1023.0, INVGAMMA ) * 255; + } } byte TextureToTexGamma( byte b ) @@ -65,4 +79,10 @@ byte TextureToGamma( byte b ) b = bound( 0, b, 255 ); return gammatable[b]; -} \ No newline at end of file +} + +// convert texture to linear 0..1 value +float TextureToLinear( int c ) { return texturetolinear[bound( 0, c, 255 )]; } + +// convert texture to linear 0..1 value +int LinearToTexture( float f ) { return lineartotexture[bound( 0, (int)(f * 1023), 1023 )]; } \ No newline at end of file diff --git a/engine/common/imagelib/img_bmp.c b/engine/common/imagelib/img_bmp.c index 8c1177aa..95707b0e 100644 --- a/engine/common/imagelib/img_bmp.c +++ b/engine/common/imagelib/img_bmp.c @@ -14,6 +14,7 @@ GNU General Public License for more details. */ #include "imagelib.h" +#include "mathlib.h" /* ============= @@ -26,6 +27,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) byte palette[256][4]; int i, columns, column, rows, row, bpp = 1; int cbPalBytes = 0, padSize = 0, bps = 0; + int reflectivity[3] = { 0, 0, 0 }; qboolean load_qfont = false; bmp_t bhdr; @@ -280,12 +282,18 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) Mem_Free( image.rgba ); return false; } + if( !Image_CheckFlag( IL_KEEP_8BIT ) && ( red != green || green != blue )) image.flags |= IMAGE_HAS_COLOR; + + reflectivity[0] += red; + reflectivity[1] += green; + reflectivity[2] += blue; } buf_p += padSize; // actual only for 4-bit bmps } + VectorDivide( reflectivity, ( image.width * image.height ), image.fogParams ); if( image.palette ) Image_GetPaletteBMP( image.palette ); return true; diff --git a/engine/common/imagelib/img_dds.c b/engine/common/imagelib/img_dds.c index dd6efdfc..6bd0bd6e 100644 --- a/engine/common/imagelib/img_dds.c +++ b/engine/common/imagelib/img_dds.c @@ -175,7 +175,7 @@ size_t Image_DXTCalcMipmapSize( dds_t *hdr ) int i, width, height; // now correct buffer size - for( i = 0; i < hdr->dwMipMapCount; i++ ) + for( i = 0; i < max( 1, ( hdr->dwMipMapCount )); i++ ) { width = max( 1, ( hdr->dwWidth >> i )); height = max( 1, ( hdr->dwHeight >> i )); @@ -313,6 +313,15 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize ) break; } + if( header.dwReserved1[1] != 0 ) + { + // store texture reflectivity + image.fogParams[0] = ((header.dwReserved1[1] & 0x000000FF) >> 0 ); + image.fogParams[1] = ((header.dwReserved1[1] & 0x0000FF00) >> 8 ); + image.fogParams[2] = ((header.dwReserved1[1] & 0x00FF0000) >> 16); + image.fogParams[3] = ((header.dwReserved1[1] & 0xFF000000) >> 24); + } + // dds files will be uncompressed on a render. requires minimal of info for set this image.rgba = Mem_Alloc( host.imagepool, image.size ); Q_memcpy( image.rgba, fin, image.size ); diff --git a/engine/common/imagelib/img_tga.c b/engine/common/imagelib/img_tga.c index 932f3870..4ca9e6db 100644 --- a/engine/common/imagelib/img_tga.c +++ b/engine/common/imagelib/img_tga.c @@ -14,6 +14,7 @@ GNU General Public License for more details. */ #include "imagelib.h" +#include "mathlib.h" /* ============= @@ -26,6 +27,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize ) byte *buf_p, *pixbuf, *targa_rgba; byte palette[256][4], red = 0, green = 0, blue = 0, alpha = 0; int readpixelcount, pixelcount; + int reflectivity[3] = { 0, 0, 0 }; qboolean compressed; tga_t targa_header; @@ -193,6 +195,10 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize ) if( red != green || green != blue ) image.flags |= IMAGE_HAS_COLOR; + reflectivity[0] += red; + reflectivity[1] += green; + reflectivity[2] += blue; + *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; @@ -206,6 +212,9 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize ) } } } + + VectorDivide( reflectivity, ( image.width * image.height ), image.fogParams ); + return true; } diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index a8edb05c..10e071e3 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -321,6 +321,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) byte *fin, *pal; int ofs[4], rendermode; int i, pixels, numcolors; + int reflectivity[3] = { 0, 0, 0 }; if( filesize < sizeof( mip )) { @@ -462,6 +463,17 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) // calc the decal reflectivity image.fogParams[3] = VectorAvg( image.fogParams ); } + else if( pal != NULL )// calc texture reflectivity + { + for( i = 0; i < 256; i++ ) + { + reflectivity[0] += pal[i*3+0]; + reflectivity[1] += pal[i*3+1]; + reflectivity[2] += pal[i*3+2]; + } + + VectorDivide( reflectivity, 256, image.fogParams ); + } image.type = PF_INDEXED_32; // 32-bit palete return Image_AddIndexedImageToPack( fin, image.width, image.height ); diff --git a/engine/common/model.c b/engine/common/model.c index 4c1b02d1..234470ff 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -2210,6 +2210,7 @@ static void Mod_LoadEntities( const dlump_t *l ) char *pfile; string keyname; char token[2048]; + char wadstring[2048]; // make sure what we really has terminator loadmodel->entities = Mem_Alloc( loadmodel->mempool, l->filelen + 1 ); @@ -2245,23 +2246,19 @@ static void Mod_LoadEntities( const dlump_t *l ) if( !Q_stricmp( keyname, "wad" )) { - char *path = token; - string wadpath; + char *pszWadFile; + + Q_strncpy( wadstring, token, 2046 ); + wadstring[2046] = 0; + + if( !Q_strchr( wadstring, ';' )) + Q_strcat( wadstring, ";" ); // parse wad pathes - while( path ) + for (pszWadFile = strtok( wadstring, ";" ); pszWadFile!= NULL; pszWadFile = strtok( NULL, ";" )) { - char *end = Q_strchr( path, ';' ); - if( !end ) - { - // if specified only once wad - if( !wadlist.count ) - FS_FileBase( path, wadlist.wadnames[wadlist.count++] ); - break; - } - Q_strncpy( wadpath, path, (end - path) + 1 ); - FS_FileBase( wadpath, wadlist.wadnames[wadlist.count++] ); - path += (end - path) + 1; // move pointer + COM_FixSlashes( pszWadFile ); + FS_FileBase( pszWadFile, wadlist.wadnames[wadlist.count++] ); if( wadlist.count >= 256 ) break; // too many wads... } } diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 77e5963c..c9a7099f 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -810,9 +810,9 @@ void Delta_Init( void ) Delta_AddField( "movevars_t", "footsteps", DT_INTEGER, 1, 1.0f, 1.0f ); Delta_AddField( "movevars_t", "rollangle", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); Delta_AddField( "movevars_t", "rollspeed", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f ); - Delta_AddField( "movevars_t", "skycolor_r", DT_FLOAT|DT_SIGNED, 12, 1.0f, 1.0f ); // 0 - 264 - Delta_AddField( "movevars_t", "skycolor_g", DT_FLOAT|DT_SIGNED, 12, 1.0f, 1.0f ); - Delta_AddField( "movevars_t", "skycolor_b", DT_FLOAT|DT_SIGNED, 12, 1.0f, 1.0f ); + Delta_AddField( "movevars_t", "skycolor_r", DT_FLOAT|DT_SIGNED, 16, 1.0f, 1.0f ); // 0 - 264 + Delta_AddField( "movevars_t", "skycolor_g", DT_FLOAT|DT_SIGNED, 16, 1.0f, 1.0f ); + Delta_AddField( "movevars_t", "skycolor_b", DT_FLOAT|DT_SIGNED, 16, 1.0f, 1.0f ); Delta_AddField( "movevars_t", "skyvec_x", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); // 0 - 1 Delta_AddField( "movevars_t", "skyvec_y", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); Delta_AddField( "movevars_t", "skyvec_z", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); diff --git a/engine/common/netchan.h b/engine/common/netchan.h index e2288db7..dad19a10 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -59,7 +59,7 @@ GNU General Public License for more details. // bytes will be stripped by the networking channel layer #define NET_MAX_MESSAGE PAD_NUMBER(( NET_MAX_PAYLOAD + HEADER_BYTES ), 16 ) -#define MASTERSERVER_ADR "celest.in:27010" +#define MASTERSERVER_ADR "ms.xash.su:27010" #define PORT_MASTER 27010 #define PORT_CLIENT 27005 #define PORT_SERVER 27015 diff --git a/engine/server/server.h b/engine/server/server.h index e16d525b..6b13388b 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -443,6 +443,7 @@ void SV_ExecuteUserCommand (char *s); void SV_InitOperatorCommands( void ); void SV_KillOperatorCommands( void ); void SV_UserinfoChanged( sv_client_t *cl, const char *userinfo ); +void SV_RemoteCommand( netadr_t from, sizebuf_t *msg ); void SV_PrepWorldFrame( void ); void SV_ProcessFile( sv_client_t *cl, char *filename ); void SV_SendResourceList( sv_client_t *cl ); diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 38dfc6f8..79be26c2 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -1422,7 +1422,7 @@ edict_t *pfnFindEntityInSphere( edict_t *pStartEdict, const float *org, float fl return ent; } - return NULL; + return svgame.edicts; } /* diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 6f8a9a74..4eefc9ed 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -356,10 +356,26 @@ void SV_ActivateServer( void ) Host_SetServerState( sv.state ); - if( sv_maxclients->integer > 1 && public_server->integer ) + if( sv_maxclients->integer > 1 ) { - MsgDev( D_INFO, "Add your server, to master server list\n" ); - Master_Add( ); + // listenserver is executed on every map change in multiplayer + if( host.type != HOST_DEDICATED ) + { + char *plservercfgfile = Cvar_VariableString( "lservercfgfile" ); + if( *plservercfgfile ) Cbuf_AddText( va( "exec %s\n", plservercfgfile )); + } + + if( public_server->integer ) + { + MsgDev( D_INFO, "Adding your server to master server list\n" ); + Master_Add( ); + } + } + + // mapchangecfgfile + { + char *mapchangecfgfile = Cvar_VariableString( "mapchangecfgfile" ); + if( *mapchangecfgfile ) Cbuf_AddText( va( "exec %s\n", mapchangecfgfile )); } } diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 46832e1d..a502894f 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -329,7 +329,22 @@ void SV_ReadPackets( void ) // check for connectionless packet (0xffffffff) first if( BF_GetMaxBytes( &net_message ) >= 4 && *(int *)net_message.pData == -1 ) { - SV_ConnectionlessPacket( net_from, &net_message ); + if( !svs.initialized ) + { + char *args, *c; + + BF_Clear( &net_message ); + BF_ReadLong( &net_message );// skip the -1 marker + + args = BF_ReadStringLine( &net_message ); + Cmd_TokenizeString( args ); + c = Cmd_Argv( 0 ); + + if( !Q_strcmp( c, "rcon" )) + SV_RemoteCommand( net_from, &net_message ); + } + else SV_ConnectionlessPacket( net_from, &net_message ); + continue; } diff --git a/engine/studio.h b/engine/studio.h index 2a8bb7ba..7ec864de 100644 --- a/engine/studio.h +++ b/engine/studio.h @@ -31,8 +31,8 @@ Studio models are position independent, so the cache manager can move them. #define IDSEQGRPHEADER (('Q'<<24)+('S'<<16)+('D'<<8)+'I') // little-endian "IDSQ" // studio limits -#define MAXSTUDIOTRIANGLES 32768 // max triangles per model -#define MAXSTUDIOVERTS 4096 // max vertices per submodel +#define MAXSTUDIOTRIANGLES 65536 // max triangles per model +#define MAXSTUDIOVERTS 32768 // max vertices per submodel #define MAXSTUDIOSEQUENCES 256 // total animation sequences #define MAXSTUDIOSKINS 256 // total textures #define MAXSTUDIOSRCBONES 512 // bones allowed at source movement