You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
951 lines
23 KiB
951 lines
23 KiB
/* |
|
s_dsp.c - digital signal processing algorithms for audio FX |
|
Copyright (C) 2009 Uncle Mike |
|
Copyright (C) 2016-2024 Alibek Omarov |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
*/ |
|
|
|
#include "common.h" |
|
#include "client.h" |
|
#include "sound.h" |
|
|
|
#define MAX_DELAY 0.4f |
|
#define MAX_ROOM_TYPES ARRAYSIZE( rgsxpre ) |
|
|
|
#define MONODLY 0 |
|
#define MAX_MONO_DELAY 0.4f |
|
|
|
#define REVERBPOS 1 |
|
#define MAX_REVERB_DELAY 0.1f |
|
|
|
#define STEREODLY 3 |
|
#define MAX_STEREO_DELAY 0.1f |
|
|
|
#define REVERB_XFADE 32 |
|
|
|
#define MAXDLY (STEREODLY + 1) |
|
#define MAXLP 10 |
|
|
|
typedef struct sx_preset_s |
|
{ |
|
float room_lp; // lowpass |
|
float room_mod; // modulation |
|
|
|
// reverb |
|
float room_size; |
|
float room_refl; |
|
float room_rvblp; |
|
|
|
// delay |
|
float room_delay; |
|
float room_feedback; |
|
float room_dlylp; |
|
float room_left; |
|
} sx_preset_t; |
|
|
|
typedef struct dly_s |
|
{ |
|
size_t cdelaysamplesmax; // delay line array size |
|
|
|
// delay line pointers |
|
size_t idelayinput; |
|
size_t idelayoutput; |
|
|
|
// crossfade |
|
int idelayoutputxf; // output pointer |
|
int xfade; // value |
|
|
|
int delaysamples; // delay setting |
|
int delayfeedback; // feedback setting |
|
|
|
// lowpass |
|
int lp; // is lowpass enabled |
|
int lp0, lp1, lp2; // lowpass buffer |
|
|
|
// modulation |
|
int mod; |
|
int modcur; |
|
|
|
// delay line |
|
int *lpdelayline; |
|
} dly_t; |
|
|
|
static const sx_preset_t rgsxpre[] = |
|
{ |
|
// -------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 |
|
}; |
|
|
|
// 0x0045dca8 enginegl.exe |
|
// SHA256: 42383d32cd712e59ee2c1bd78b7ba48814e680e7026c4223e730111f34a60d66 |
|
static const sx_preset_t rgsxpre_hlalpha052[] = |
|
{ |
|
// -------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.08, 0.8, 2.0, 0.0 }, // 1 generic |
|
{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.02, 0.75, 0.0, 0.001 }, // 2 metalic |
|
{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.03, 0.78, 0.0, 0.002 }, // 3 |
|
{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.06, 0.77, 0.0, 0.003 }, // 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.003 }, // 8 chamber |
|
{ 0.0, 0.0, 0.05, 0.9, 1.0, 0.0, 0.0, 2.0, 0.002 }, // 9 |
|
{ 0.0, 0.0, 0.05, 0.95, 1.0, 0.0, 0.0, 2.0, 0.001 }, // 10 |
|
{ 0.0, 0.0, 0.05, 0.7, 0.0, 0.0, 0.0, 2.0, 0.003 }, // 11 brite |
|
{ 0.0, 0.0, 0.055, 0.78, 0.0, 0.0, 0.0, 2.0, 0.002 }, // 12 |
|
{ 0.0, 0.0, 0.05, 0.86, 0.0, 0.0, 0.0, 2.0, 0.001 }, // 13 |
|
{ 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.01 }, // 14 water |
|
{ 1.0, 1.0, 0.0, 0.0, 1.0, 0.06, 0.85, 2.0, 0.02 }, // 15 |
|
{ 1.0, 1.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.15, 0.48, 2.0, 0.008 }, // 17 concrete |
|
{ 0.0, 0.0, 0.06, 0.9, 1.0, 0.22, 0.52, 2.0, 0.005 }, // 18 |
|
{ 0.0, 0.0, 0.07, 0.94, 1.0, 0.3, 0.6, 2.0, 0.001 }, // 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 |
|
}; |
|
|
|
static const sx_preset_t *ptable = rgsxpre; |
|
|
|
// cvars |
|
static CVAR_DEFINE_AUTO( dsp_off, "0", FCVAR_ARCHIVE, "disable DSP processing" ); |
|
static CVAR_DEFINE_AUTO( dsp_coeff_table, "0", FCVAR_ARCHIVE, "select DSP coefficient table: 0 for release or 1 for alpha 0.52" ); |
|
static CVAR_DEFINE_AUTO( room_type, "0", 0, "current room type preset" ); |
|
|
|
static CVAR_DEFINE( roomwater_type, "waterroom_type", "14", 0, "water room type" ); |
|
static CVAR_DEFINE( hisound, "room_hires", "2", FCVAR_ARCHIVE, "dsp quality. 1 for 22k, 2 for 44k(recommended) and 3 for 96k" ); |
|
|
|
// underwater/special fx modulations |
|
static CVAR_DEFINE( sxmod_mod, "room_mod", "0", 0, "stereo amptitude modulation for room" ); |
|
static CVAR_DEFINE( sxmod_lowpass, "room_lp", "0", 0, "for water fx, lowpass for entire room" ); |
|
|
|
// stereo delay(no feedback) |
|
static CVAR_DEFINE( sxste_delay, "room_left", "0", 0, "left channel delay time" ); |
|
|
|
// mono reverb |
|
static CVAR_DEFINE( sxrvb_lp, "room_rvblp", "1", 0, "reverb: low pass filtering level" ); |
|
static CVAR_DEFINE( sxrvb_feedback, "room_refl", "0", 0, "reverb: decay time" ); |
|
static CVAR_DEFINE( sxrvb_size, "room_size", "0", 0, "reverb: initial reflection size" ); |
|
|
|
// mono delay |
|
static CVAR_DEFINE( sxdly_lp, "room_dlylp", "1", 0, "mono delay: low pass filtering level" ); |
|
static CVAR_DEFINE( sxdly_feedback, "room_feedback", "0.2", 0, "mono delay: decay time" ); |
|
static CVAR_DEFINE( sxdly_delay, "room_delay", "0.8", 0, "mono delay: delay time" ); |
|
|
|
static int idsp_dma_speed; |
|
int idsp_room; |
|
static int room_typeprev; |
|
|
|
// routines |
|
static int sxamodl, sxamodr; // amplitude modulation values |
|
static int sxamodlt, sxamodrt; // modulation targets |
|
static int sxmod1cur, sxmod2cur; |
|
static int sxmod1, sxmod2; |
|
static int sxhires; |
|
|
|
static portable_samplepair_t *paintto = NULL; |
|
|
|
static dly_t rgsxdly[MAXDLY]; // stereo is last |
|
static int rgsxlp[MAXLP]; |
|
|
|
static void SX_Profiling_f( void ); |
|
|
|
/* |
|
============ |
|
SX_ReloadRoomFX |
|
|
|
============ |
|
*/ |
|
static void SX_ReloadRoomFX( void ) |
|
{ |
|
SetBits( sxste_delay.flags, FCVAR_CHANGED ); |
|
SetBits( sxrvb_feedback.flags, FCVAR_CHANGED ); |
|
SetBits( sxdly_delay.flags, FCVAR_CHANGED ); |
|
SetBits( room_type.flags, FCVAR_CHANGED ); |
|
} |
|
|
|
/* |
|
============ |
|
SX_Init() |
|
|
|
Starts sound crackling system |
|
============ |
|
*/ |
|
void SX_Init( void ) |
|
{ |
|
memset( rgsxdly, 0, sizeof( rgsxdly )); |
|
memset( rgsxlp, 0, sizeof( rgsxlp )); |
|
|
|
sxamodr = sxamodl = sxamodrt = sxamodlt = 255; |
|
idsp_dma_speed = SOUND_11k; |
|
|
|
Cvar_RegisterVariable( &hisound ); |
|
sxhires = 2; |
|
|
|
sxmod1cur = sxmod1 = 350 * ( idsp_dma_speed / SOUND_11k ); |
|
sxmod2cur = sxmod2 = 450 * ( idsp_dma_speed / SOUND_11k ); |
|
|
|
Cvar_RegisterVariable( &dsp_off ); |
|
Cvar_RegisterVariable( &dsp_coeff_table ); |
|
|
|
Cvar_RegisterVariable( &roomwater_type ); |
|
Cvar_RegisterVariable( &room_type ); |
|
|
|
Cvar_RegisterVariable( &sxmod_lowpass ); |
|
Cvar_RegisterVariable( &sxmod_mod ); |
|
|
|
Cvar_RegisterVariable( &sxrvb_size ); |
|
Cvar_RegisterVariable( &sxrvb_feedback ); |
|
Cvar_RegisterVariable( &sxrvb_lp ); |
|
|
|
Cvar_RegisterVariable( &sxdly_delay ); |
|
Cvar_RegisterVariable( &sxdly_feedback ); |
|
Cvar_RegisterVariable( &sxdly_lp ); |
|
|
|
Cvar_RegisterVariable( &sxste_delay ); |
|
|
|
Cmd_AddCommand( "dsp_profile", SX_Profiling_f, "dsp stress-test, first argument is room_type" ); |
|
|
|
SX_ReloadRoomFX(); |
|
} |
|
|
|
/* |
|
=========== |
|
DLY_Free |
|
|
|
Free memory allocated for DSP |
|
=========== |
|
*/ |
|
static 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 |
|
|
|
Stop DSP processor |
|
========== |
|
*/ |
|
void SX_Free( void ) |
|
{ |
|
int i; |
|
|
|
for( i = 0; i <= 3; i++ ) |
|
DLY_Free( i ); |
|
|
|
Cmd_RemoveCommand( "dsp_profile" ); |
|
} |
|
|
|
|
|
/* |
|
=========== |
|
DLY_Init |
|
|
|
Initialize dly |
|
=========== |
|
*/ |
|
static 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 * idsp_dma_speed) << sxhires) + 1; |
|
cur->lpdelayline = (int *)Z_Calloc( 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 |
|
============ |
|
*/ |
|
static 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 |
|
============= |
|
*/ |
|
static void DLY_CheckNewStereoDelayVal( void ) |
|
{ |
|
dly_t *const dly = &rgsxdly[STEREODLY]; |
|
float delay = sxste_delay.value; |
|
|
|
if( !FBitSet( sxste_delay.flags, FCVAR_CHANGED )) |
|
return; |
|
|
|
if( delay == 0 ) |
|
{ |
|
DLY_Free( STEREODLY ); |
|
} |
|
else |
|
{ |
|
int samples; |
|
|
|
delay = Q_min( delay, MAX_STEREO_DELAY ); |
|
samples = (int)(delay * idsp_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 ); |
|
} |
|
} |
|
|
|
/* |
|
============= |
|
DLY_DoStereoDelay |
|
|
|
Do stereo processing |
|
============= |
|
*/ |
|
static void DLY_DoStereoDelay( int count ) |
|
{ |
|
int delay, samplexf; |
|
dly_t *const dly = &rgsxdly[STEREODLY]; |
|
portable_samplepair_t *paint = paintto; |
|
|
|
if( !dly->lpdelayline ) |
|
return; // inactive |
|
|
|
for( ; count; count--, paint++ ) |
|
{ |
|
if( dly->mod && --dly->modcur < 0 ) |
|
dly->modcur = dly->mod; |
|
|
|
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 ) |
|
{ |
|
dly->idelayoutputxf = dly->idelayoutput + ((COM_RandomLong( 0, 255 ) * dly->delaysamples ) >> 9 ); |
|
|
|
dly->xfade = 128; |
|
} |
|
|
|
dly->idelayoutputxf %= dly->cdelaysamplesmax; |
|
|
|
// modify delay, if crossfading |
|
if( dly->xfade ) |
|
{ |
|
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; |
|
} |
|
|
|
// save left value to delay line |
|
dly->lpdelayline[dly->idelayinput] = CLIP( paint->left ); |
|
|
|
// paint new delay value |
|
paint->left = delay; |
|
} |
|
else |
|
{ |
|
// clear delay line |
|
dly->lpdelayline[dly->idelayinput] = 0; |
|
} |
|
|
|
DLY_MovePointer( dly ); |
|
} |
|
} |
|
|
|
/* |
|
============= |
|
DLY_CheckNewDelayVal |
|
|
|
Update delay processor settings if we are in new room |
|
============= |
|
*/ |
|
static void DLY_CheckNewDelayVal( void ) |
|
{ |
|
float delay = sxdly_delay.value; |
|
dly_t *const dly = &rgsxdly[MONODLY]; |
|
|
|
if( FBitSet( sxdly_delay.flags, FCVAR_CHANGED )) |
|
{ |
|
if( delay == 0 ) |
|
{ |
|
DLY_Free( MONODLY ); |
|
} |
|
else |
|
{ |
|
delay = Q_min( delay, MAX_MONO_DELAY ); |
|
dly->delaysamples = (int)(delay * idsp_dma_speed) << sxhires; |
|
|
|
// init dly |
|
if( !dly->lpdelayline ) |
|
DLY_Init( MONODLY, MAX_MONO_DELAY ); |
|
|
|
if( dly->lpdelayline ) |
|
{ |
|
memset( dly->lpdelayline, 0, dly->cdelaysamplesmax * sizeof( int ) ); |
|
dly->lp0 = dly->lp1 = dly->lp2 = 0; |
|
} |
|
|
|
dly->idelayinput = 0; |
|
dly->idelayoutput = dly->cdelaysamplesmax - dly->delaysamples; |
|
|
|
if( !dly->delaysamples ) |
|
DLY_Free( MONODLY ); |
|
|
|
} |
|
} |
|
|
|
dly->lp = sxdly_lp.value; |
|
dly->delayfeedback = 255 * sxdly_feedback.value; |
|
} |
|
|
|
/* |
|
============= |
|
DLY_DoDelay |
|
|
|
Do delay processing |
|
============= |
|
*/ |
|
static void DLY_DoDelay( int count ) |
|
{ |
|
dly_t *const dly = &rgsxdly[MONODLY]; |
|
portable_samplepair_t *paint = paintto; |
|
int delay; |
|
|
|
if( !dly->lpdelayline || !count ) |
|
return; // inactive |
|
|
|
for( ; count; count--, paint++ ) |
|
{ |
|
delay = dly->lpdelayline[dly->idelayoutput]; |
|
|
|
// don't process if delay line and left/right samples are zero |
|
if( delay || paint->left || paint->right ) |
|
{ |
|
// calculate delayed value from average |
|
int val = (( paint->left + paint->right ) >> 1 ) + (( dly->delayfeedback * delay ) >> 8); |
|
val = CLIP( val ); |
|
|
|
if( dly->lp ) // lowpass |
|
{ |
|
val = ( dly->lp0 + dly->lp1 + val ) / 3; |
|
dly->lp0 = dly->lp1; |
|
dly->lp1 = val; |
|
} |
|
|
|
dly->lpdelayline[dly->idelayinput] = val; |
|
|
|
val >>= 2; |
|
|
|
paint->left = CLIP( paint->left + val ); |
|
paint->right = CLIP( paint->right + val ); |
|
} |
|
else |
|
{ |
|
dly->lpdelayline[dly->idelayinput] = 0; |
|
dly->lp0 = dly->lp1 = dly->lp2 = 0; |
|
} |
|
|
|
DLY_MovePointer( dly ); |
|
} |
|
} |
|
|
|
/* |
|
=========== |
|
RVB_SetUpDly |
|
|
|
Set up dly for reverb |
|
=========== |
|
*/ |
|
static void RVB_SetUpDly( int pos, float delay, int kmod ) |
|
{ |
|
int samples; |
|
|
|
delay = Q_min( delay, MAX_REVERB_DELAY ); |
|
samples = (int)(delay * idsp_dma_speed) << sxhires; |
|
|
|
if( !rgsxdly[pos].lpdelayline ) |
|
{ |
|
rgsxdly[pos].delaysamples = samples; |
|
DLY_Init( pos, MAX_REVERB_DELAY ); |
|
} |
|
|
|
rgsxdly[pos].modcur = rgsxdly[pos].mod = (int)(kmod * idsp_dma_speed / SOUND_11k) << sxhires; |
|
|
|
// set up crossfade, if delay has changed |
|
if( rgsxdly[pos].delaysamples != samples ) |
|
{ |
|
rgsxdly[pos].idelayoutputxf = rgsxdly[pos].idelayinput - samples; |
|
if( rgsxdly[pos].idelayoutputxf < 0 ) |
|
rgsxdly[pos].idelayoutputxf += rgsxdly[pos].cdelaysamplesmax; |
|
rgsxdly[pos].xfade = REVERB_XFADE; |
|
} |
|
|
|
if( !rgsxdly[pos].delaysamples ) |
|
DLY_Free( pos ); |
|
|
|
} |
|
|
|
/* |
|
=========== |
|
RVB_CheckNewReverbVal |
|
|
|
Update reverb settings if we are in new room |
|
=========== |
|
*/ |
|
static void RVB_CheckNewReverbVal( void ) |
|
{ |
|
dly_t *const dly1 = &rgsxdly[REVERBPOS]; |
|
dly_t *const dly2 = &rgsxdly[REVERBPOS + 1]; |
|
float delay = sxrvb_size.value; |
|
|
|
if( FBitSet( sxrvb_size.flags, FCVAR_CHANGED )) |
|
{ |
|
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 ); |
|
} |
|
} |
|
|
|
dly1->lp = dly2->lp = sxrvb_lp.value; |
|
dly1->delayfeedback = dly2->delayfeedback = (int)(255 * sxrvb_feedback.value); |
|
} |
|
|
|
/* |
|
=========== |
|
RVB_DoReverbForOneDly |
|
|
|
Do reverberation for one dly |
|
=========== |
|
*/ |
|
static 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 ) |
|
{ |
|
// modulate delay rate |
|
if( !dly->mod ) |
|
{ |
|
dly->idelayoutputxf = dly->idelayoutput + ((COM_RandomLong( 0, 255 ) * delay) >> 9 ); |
|
|
|
dly->idelayoutputxf %= dly->cdelaysamplesmax; |
|
|
|
dly->xfade = REVERB_XFADE; |
|
} |
|
|
|
if( dly->xfade ) |
|
{ |
|
samplexf = (dly->lpdelayline[dly->idelayoutputxf] * (REVERB_XFADE - dly->xfade)) / REVERB_XFADE; |
|
delay = ((delay * dly->xfade) / REVERB_XFADE) + samplexf; |
|
|
|
if( ++dly->idelayoutputxf >= dly->cdelaysamplesmax ) |
|
dly->idelayoutputxf = 0; |
|
|
|
if( --dly->xfade == 0 ) |
|
dly->idelayoutput = dly->idelayoutputxf; |
|
} |
|
|
|
|
|
if( delay ) |
|
{ |
|
val = vlr + ( ( dly->delayfeedback * delay ) >> 8 ); |
|
val = CLIP( val ); |
|
} |
|
else |
|
val = vlr; |
|
|
|
if( dly->lp ) |
|
{ |
|
valt = (dly->lp0 + val) >> 1; |
|
dly->lp0 = val; |
|
} |
|
else |
|
valt = val; |
|
|
|
voutm = dly->lpdelayline[dly->idelayinput] = valt; |
|
} |
|
else |
|
{ |
|
voutm = dly->lpdelayline[dly->idelayinput] = 0; |
|
dly->lp0 = 0; |
|
} |
|
|
|
DLY_MovePointer( dly ); |
|
|
|
return voutm; |
|
|
|
} |
|
|
|
/* |
|
=========== |
|
RVB_DoReverb |
|
|
|
Do reverberation processing |
|
=========== |
|
*/ |
|
static void RVB_DoReverb( int count ) |
|
{ |
|
dly_t *const dly1 = &rgsxdly[REVERBPOS]; |
|
dly_t *const dly2 = &rgsxdly[REVERBPOS+1]; |
|
portable_samplepair_t *paint = paintto; |
|
int vlr, voutm; |
|
|
|
if( !dly1->lpdelayline ) |
|
return; |
|
|
|
for( ; count; count--, paint++ ) |
|
{ |
|
vlr = ( paint->left + paint->right ) >> 1; |
|
|
|
voutm = RVB_DoReverbForOneDly( dly1, vlr, paint ); |
|
voutm += RVB_DoReverbForOneDly( dly2, vlr, paint ); |
|
|
|
if( dsp_coeff_table.value == 1.0f ) |
|
voutm /= 6; // alpha |
|
else voutm = (11 * voutm) >> 6; |
|
|
|
paint->left = CLIP( paint->left + voutm ); |
|
paint->right = CLIP( paint->right + voutm ); |
|
} |
|
} |
|
|
|
/* |
|
=========== |
|
RVB_DoAMod |
|
|
|
Do amplification modulation processing |
|
=========== |
|
*/ |
|
static void RVB_DoAMod( int count ) |
|
{ |
|
portable_samplepair_t *paint = paintto; |
|
|
|
if( !sxmod_lowpass.value && !sxmod_mod.value ) |
|
return; |
|
|
|
for( ; count; count--, paint++ ) |
|
{ |
|
portable_samplepair_t res = *paint; |
|
|
|
if( sxmod_lowpass.value ) |
|
{ |
|
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[4] = paint->left; |
|
rgsxlp[9] = paint->right; |
|
|
|
rgsxlp[0] = rgsxlp[1]; |
|
rgsxlp[1] = rgsxlp[2]; |
|
rgsxlp[2] = rgsxlp[3]; |
|
rgsxlp[3] = rgsxlp[4]; |
|
rgsxlp[4] = rgsxlp[5]; |
|
rgsxlp[5] = rgsxlp[6]; |
|
rgsxlp[6] = rgsxlp[7]; |
|
rgsxlp[7] = rgsxlp[8]; |
|
rgsxlp[8] = rgsxlp[9]; |
|
} |
|
|
|
if( sxmod_mod.value ) |
|
{ |
|
if( --sxmod1cur < 0 ) |
|
sxmod1cur = sxmod1; |
|
|
|
if( !sxmod1 ) |
|
sxamodlt = COM_RandomLong( 32, 255 ); |
|
|
|
if( --sxmod2cur < 0 ) |
|
sxmod2cur = sxmod2; |
|
|
|
if( !sxmod2 ) |
|
sxamodrt = COM_RandomLong( 32, 255 ); |
|
|
|
res.left = (sxamodl * res.left) >> 8; |
|
res.right = (sxamodr * res.right) >> 8; |
|
|
|
if( sxamodl < sxamodlt ) |
|
sxamodl++; |
|
else if( sxamodl > sxamodlt ) |
|
sxamodl--; |
|
|
|
if( sxamodr < sxamodrt ) |
|
sxamodr++; |
|
else if( sxamodr > sxamodrt ) |
|
sxamodr--; |
|
} |
|
|
|
paint->left = CLIP(res.left); |
|
paint->right = CLIP(res.right); |
|
} |
|
} |
|
|
|
/* |
|
=========== |
|
DSP_Process |
|
|
|
(xash dsp interface) |
|
=========== |
|
*/ |
|
void DSP_Process( portable_samplepair_t *pbfront, int sampleCount ) |
|
{ |
|
if( dsp_off.value || !sampleCount ) |
|
return; |
|
|
|
// preset is already installed by CheckNewDspPresets |
|
paintto = pbfront; |
|
|
|
RVB_DoAMod( sampleCount ); |
|
RVB_DoReverb( sampleCount ); |
|
DLY_DoDelay( sampleCount ); |
|
DLY_DoStereoDelay( sampleCount ); |
|
} |
|
|
|
/* |
|
=========== |
|
DSP_ClearState |
|
|
|
(xash dsp interface) |
|
=========== |
|
*/ |
|
void DSP_ClearState( void ) |
|
{ |
|
Cvar_DirectSet( &room_type, "0" ); |
|
SX_ReloadRoomFX(); |
|
} |
|
|
|
/* |
|
=========== |
|
CheckNewDspPresets |
|
|
|
(xash dsp interface) |
|
=========== |
|
*/ |
|
void CheckNewDspPresets( void ) |
|
{ |
|
if( dsp_off.value != 0.0f ) |
|
return; |
|
|
|
if( FBitSet( dsp_coeff_table.flags, FCVAR_CHANGED )) |
|
{ |
|
switch( (int)dsp_coeff_table.value ) |
|
{ |
|
case 0: // release |
|
ptable = rgsxpre; |
|
break; |
|
case 1: // alpha |
|
ptable = rgsxpre_hlalpha052; |
|
break; |
|
default: |
|
ptable = rgsxpre; |
|
break; |
|
} |
|
|
|
SX_ReloadRoomFX(); |
|
room_typeprev = -1; |
|
|
|
ClearBits( dsp_coeff_table.flags, FCVAR_CHANGED ); |
|
} |
|
|
|
if( s_listener.waterlevel > 2 ) |
|
idsp_room = roomwater_type.value; |
|
else idsp_room = room_type.value; |
|
|
|
// don't pass invalid presets |
|
idsp_room = bound( 0, idsp_room, MAX_ROOM_TYPES ); |
|
|
|
if( FBitSet( hisound.flags, FCVAR_CHANGED )) |
|
{ |
|
sxhires = hisound.value; |
|
ClearBits( hisound.flags, FCVAR_CHANGED ); |
|
} |
|
|
|
if( idsp_room == room_typeprev && idsp_room == 0 ) |
|
return; |
|
|
|
if( idsp_room != room_typeprev ) |
|
{ |
|
const sx_preset_t *cur; |
|
|
|
cur = ptable + idsp_room; |
|
|
|
Cvar_DirectSetValue( &sxmod_lowpass, cur->room_lp ); |
|
Cvar_DirectSetValue( &sxmod_mod, cur->room_mod ); |
|
Cvar_DirectSetValue( &sxrvb_size, cur->room_size ); |
|
Cvar_DirectSetValue( &sxrvb_feedback, cur->room_refl ); |
|
Cvar_DirectSetValue( &sxrvb_lp, cur->room_rvblp ); |
|
Cvar_DirectSetValue( &sxdly_delay, cur->room_delay ); |
|
Cvar_DirectSetValue( &sxdly_feedback, cur->room_feedback ); |
|
Cvar_DirectSetValue( &sxdly_lp, cur->room_dlylp ); |
|
Cvar_DirectSetValue( &sxste_delay, cur->room_left ); |
|
} |
|
|
|
room_typeprev = idsp_room; |
|
|
|
RVB_CheckNewReverbVal( ); |
|
DLY_CheckNewDelayVal( ); |
|
DLY_CheckNewStereoDelayVal(); |
|
|
|
ClearBits( sxrvb_size.flags, FCVAR_CHANGED ); |
|
ClearBits( sxdly_delay.flags, FCVAR_CHANGED ); |
|
ClearBits( sxste_delay.flags, FCVAR_CHANGED ); |
|
} |
|
|
|
static void SX_Profiling_f( void ) |
|
{ |
|
portable_samplepair_t testbuffer[512]; |
|
float oldroom = room_type.value; |
|
double start, end; |
|
int i, calls; |
|
|
|
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_DirectSetValue( &room_type, Q_atof( Cmd_Argv( 1 ))); |
|
SX_ReloadRoomFX(); |
|
CheckNewDspPresets(); // we just need idsp_room immediately, for message below |
|
} |
|
|
|
Con_Printf( "Profiling 10000 calls to DSP. Sample count is 512, room_type is %i\n", idsp_room ); |
|
|
|
start = Sys_DoubleTime(); |
|
for( calls = 10000; calls; calls-- ) |
|
{ |
|
DSP_Process( testbuffer, 512 ); |
|
} |
|
end = Sys_DoubleTime(); |
|
|
|
Con_Printf( "----------\nTook %g seconds.\n", end - start ); |
|
|
|
if( Cmd_Argc() > 1 ) |
|
{ |
|
Cvar_DirectSetValue( &room_type, oldroom ); |
|
SX_ReloadRoomFX(); |
|
CheckNewDspPresets(); |
|
} |
|
}
|
|
|