add 4-band equalizer based on the OpenAL extension (config only) (#1714)

* add 4-band equalizer based on the OpenAL extension (config only)

* add comment

* another comment

* expose equalizer settings to config

* remove EQUALIZER_DEFAULT string

* allow to toggle "Apply Equalizer" in the menu

* implement Equalizer presets

The presets are exemplary for now and need some fine-tuning.

* remove header

* back to gain percent scale, dB scale is not fine-grained enough

* apply patch by @ceski-1 (thank you!)

* shorten preset names for now

* Revert "back to gain percent scale, dB scale is not fine-grained enough"

This reverts commit 3210cb07d67f71c4025e73b8b56387e45a0dc995.

* a little clean-up

* declare SetEqualizer as static

* Expose center and bandwidth settings

* Increase gain for temporary presets

* Add preamp

* Add placeholder EQ menu

* Use an alternate set of presets

* disable EQ tab in the General menu

* give equalizer presets less abstract names

* adjust equalizer preset preamp gains

@ceski-1 I will need your confirmation here

* Use safer dB ranges

* Simplify

* Formatting

---------

Co-authored-by: ceski <56656010+ceski-1@users.noreply.github.com>
This commit is contained in:
Fabian Greffrath 2024-06-25 20:35:48 +02:00 committed by GitHub
parent 480cf488d4
commit f8d1dfc078
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 307 additions and 8 deletions

View File

@ -58,6 +58,8 @@
# define FUNCTION_CAST(T, ptr) (T)(ptr)
#endif
#define ALFUNC(T, ptr) (ptr = FUNCTION_CAST(T, alGetProcAddress(#ptr)))
static int snd_resampler;
static boolean snd_limiter;
static boolean snd_hrtf;
@ -119,10 +121,8 @@ static void InitDeferred(void)
if (alIsExtensionPresent("AL_SOFT_deferred_updates") == AL_TRUE)
{
alDeferUpdatesSOFT = FUNCTION_CAST(
LPALDEFERUPDATESSOFT, alGetProcAddress("alDeferUpdatesSOFT"));
alProcessUpdatesSOFT = FUNCTION_CAST(
LPALPROCESSUPDATESSOFT, alGetProcAddress("alProcessUpdatesSOFT"));
ALFUNC(LPALDEFERUPDATESSOFT, alDeferUpdatesSOFT);
ALFUNC(LPALPROCESSUPDATESSOFT, alProcessUpdatesSOFT);
if (alDeferUpdatesSOFT && alProcessUpdatesSOFT)
{
@ -208,8 +208,7 @@ void I_OAL_SetResampler(void)
return;
}
alGetStringiSOFT =
FUNCTION_CAST(LPALGETSTRINGISOFT, alGetProcAddress("alGetStringiSOFT"));
ALFUNC(LPALGETSTRINGISOFT, alGetStringiSOFT);
if (!alGetStringiSOFT)
{
@ -334,8 +333,7 @@ const char **I_OAL_GetResamplerStrings(void)
return NULL;
}
alGetStringiSOFT =
FUNCTION_CAST(LPALGETSTRINGISOFT, alGetProcAddress("alGetStringiSOFT"));
ALFUNC(LPALGETSTRINGISOFT, alGetStringiSOFT);
if (!alGetStringiSOFT)
{
@ -352,9 +350,223 @@ const char **I_OAL_GetResamplerStrings(void)
return strings;
}
typedef enum {
EQ_PRESET_OFF,
EQ_PRESET_CLASSICAL,
EQ_PRESET_ROCK,
EQ_PRESET_VOCAL,
NUM_EQ_PRESETS
} eq_preset_t;
static eq_preset_t snd_equalizer;
static int snd_eq_preamp;
static int snd_eq_low_gain;
static int snd_eq_low_cutoff;
static int snd_eq_mid1_gain;
static int snd_eq_mid1_center;
static int snd_eq_mid1_width;
static int snd_eq_mid2_gain;
static int snd_eq_mid2_center;
static int snd_eq_mid2_width;
static int snd_eq_high_gain;
static int snd_eq_high_cutoff;
static LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;
static LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
static LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;
static LPALGENEFFECTS alGenEffects;
static LPALDELETEEFFECTS alDeleteEffects;
static LPALISEFFECT alIsEffect;
static LPALEFFECTI alEffecti;
static LPALEFFECTF alEffectf;
static LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;
static LPALGENFILTERS alGenFilters;
static LPALDELETEFILTERS alDeleteFilters;
static LPALISFILTER alIsFilter;
static LPALFILTERI alFilteri;
static LPALFILTERF alFilterf;
static void InitEqualizer(void)
{
ALCint iSends = 0;
if (!oal || !oal->EXT_EFX)
{
return;
}
// Check the actual number of Auxiliary Sends available on each Source.
alcGetIntegerv(oal->device, ALC_MAX_AUXILIARY_SENDS, 1, &iSends);
if (iSends < 1)
{
return;
}
ALFUNC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots);
ALFUNC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots);
ALFUNC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot);
ALFUNC(LPALGENEFFECTS, alGenEffects);
ALFUNC(LPALDELETEEFFECTS, alDeleteEffects);
ALFUNC(LPALISEFFECT, alIsEffect);
ALFUNC(LPALEFFECTI, alEffecti);
ALFUNC(LPALEFFECTF, alEffectf);
ALFUNC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti);
ALFUNC(LPALGENFILTERS, alGenFilters);
ALFUNC(LPALDELETEFILTERS, alDeleteFilters);
ALFUNC(LPALISFILTER, alIsFilter);
ALFUNC(LPALFILTERI, alFilteri);
ALFUNC(LPALFILTERF, alFilterf);
}
void I_OAL_SetEqualizer(void)
{
static ALuint uiEffectSlot = AL_INVALID;
static ALuint uiEffect = AL_INVALID;
static ALuint uiFilter = AL_INVALID;
if (!alGenAuxiliaryEffectSlots ||
!alDeleteAuxiliaryEffectSlots ||
!alIsAuxiliaryEffectSlot ||
!alGenEffects ||
!alDeleteEffects ||
!alIsEffect ||
!alEffecti ||
!alEffectf ||
!alAuxiliaryEffectSloti ||
!alGenFilters ||
!alDeleteFilters ||
!alIsFilter ||
!alFilteri ||
!alFilterf)
{
return;
}
// Unload all effects first.
if (alIsAuxiliaryEffectSlot(uiEffectSlot))
{
for (int i = 0; i < MAX_CHANNELS; i++)
{
alSourcei(oal->sources[i], AL_DIRECT_FILTER, AL_FILTER_NULL);
alSource3i(oal->sources[i], AL_AUXILIARY_SEND_FILTER,
AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);
}
alDeleteAuxiliaryEffectSlots(1, &uiEffectSlot);
}
if (alIsEffect(uiEffect))
{
alDeleteEffects(1, &uiEffect);
}
if (alIsFilter(uiFilter))
{
alDeleteFilters(1, &uiFilter);
}
if (snd_equalizer == EQ_PRESET_OFF)
{
return;
}
// Apply equalizer effect.
alGenAuxiliaryEffectSlots(1, &uiEffectSlot);
alGenEffects(1, &uiEffect);
alGenFilters(1, &uiFilter);
alEffecti(uiEffect, AL_EFFECT_TYPE, AL_EFFECT_EQUALIZER);
// AL_LOWPASS_GAIN actually controls overall gain for the filter it's
// applied to (OpenAL Effects Extension Guide 1.1, pp. 16-17, 135).
alFilteri(uiFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
// Gains vary from 0.251 up to 3.981, which means from -12dB attenuation
// up to +12dB amplification, i.e. 20*log10(gain).
#define DB_TO_GAIN(db) powf(10.0f, (db) / 20.0f)
#define EQ_GAIN(db) ((ALfloat)BETWEEN(0.251f, 3.981f, DB_TO_GAIN(db)))
#define LP_GAIN(db) ((ALfloat)BETWEEN(0.063f, 1.0f, DB_TO_GAIN(db)))
#define OCTAVE(x) ((ALfloat)BETWEEN(0.01f, 1.0f, (x) / 100.0f))
// Low
alEffectf(uiEffect, AL_EQUALIZER_LOW_GAIN, EQ_GAIN(snd_eq_low_gain));
alEffectf(uiEffect, AL_EQUALIZER_LOW_CUTOFF, (ALfloat)snd_eq_low_cutoff);
// Mid 1
alEffectf(uiEffect, AL_EQUALIZER_MID1_GAIN, EQ_GAIN(snd_eq_mid1_gain));
alEffectf(uiEffect, AL_EQUALIZER_MID1_CENTER, (ALfloat)snd_eq_mid1_center);
alEffectf(uiEffect, AL_EQUALIZER_MID1_WIDTH, OCTAVE(snd_eq_mid1_width));
// Mid 2
alEffectf(uiEffect, AL_EQUALIZER_MID2_GAIN, EQ_GAIN(snd_eq_mid2_gain));
alEffectf(uiEffect, AL_EQUALIZER_MID2_CENTER, (ALfloat)snd_eq_mid2_center);
alEffectf(uiEffect, AL_EQUALIZER_MID2_WIDTH, OCTAVE(snd_eq_mid2_width));
// High
alEffectf(uiEffect, AL_EQUALIZER_HIGH_GAIN, EQ_GAIN(snd_eq_high_gain));
alEffectf(uiEffect, AL_EQUALIZER_HIGH_CUTOFF, (ALfloat)snd_eq_high_cutoff);
alAuxiliaryEffectSloti(uiEffectSlot, AL_EFFECTSLOT_EFFECT, uiEffect);
for (int i = 0; i < MAX_CHANNELS; i++)
{
// Mute the dry path.
alFilterf(uiFilter, AL_LOWPASS_GAIN, 0.0f);
alSourcei(oal->sources[i], AL_DIRECT_FILTER, uiFilter);
// Keep the wet path.
alFilterf(uiFilter, AL_LOWPASS_GAIN, LP_GAIN(snd_eq_preamp));
alSource3i(oal->sources[i], AL_AUXILIARY_SEND_FILTER, uiEffectSlot, 0,
uiFilter);
}
}
void I_OAL_EqualizerPreset(void)
{
struct
{
int *var;
int val[NUM_EQ_PRESETS];
} eq_presets[] =
{ // Preamp Off, Classical, Rock, Vocal
{&snd_eq_preamp, { 0, -4, -5, -4}}, // -24 to 0
// Low
{&snd_eq_low_gain, { 0, 4, 0, -2}}, // -12 to 12
{&snd_eq_low_cutoff, { 200, 125, 200, 125}}, // 50 to 800
// Mid 1
{&snd_eq_mid1_gain, { 0, 1, 3, 3}}, // -12 to 12
{&snd_eq_mid1_center, { 500, 200, 250, 650}}, // 200 to 3000
{&snd_eq_mid1_width, { 100, 100, 100, 100}}, // 1 to 100
// Mid 2
{&snd_eq_mid2_gain, { 0, 0, 1, 3}}, // -12 to 12
{&snd_eq_mid2_center, { 3000, 3000, 3000, 1550}}, // 1000 to 8000
{&snd_eq_mid2_width, { 100, 100, 100, 100}}, // 1 to 100
// High
{&snd_eq_high_gain, { 0, 2, 5, 1}}, // -12 to 12
{&snd_eq_high_cutoff, { 6000, 8000, 6000, 10000}}, // 4000 to 16000
};
for (int i = 0; i < arrlen(eq_presets); i++)
{
*eq_presets[i].var = eq_presets[i].val[snd_equalizer];
}
I_OAL_SetEqualizer();
}
static void UpdateUserSoundSettings(void)
{
I_OAL_SetResampler();
I_OAL_SetEqualizer();
if (oal_snd_module == SND_MODULE_3D)
{
@ -469,6 +681,39 @@ void I_OAL_BindSoundVariables(void)
BIND_NUM(snd_doppler, 0, 0, 10,
"[OpenAL 3D] Doppler effect (0 = Off; 10 = Max)");
BIND_BOOL(snd_limiter, false, "Use sound output limiter");
BIND_NUM(snd_equalizer, EQ_PRESET_OFF, EQ_PRESET_OFF, EQ_PRESET_VOCAL,
"Equalizer preset (0 = Off; 1 = Classical; 2 = Rock; 3 = Vocal");
BIND_NUM(snd_eq_preamp, 0, -24, 0,
"Equalizer preamp gain [dB]");
// Low
BIND_NUM(snd_eq_low_gain, 0, -12, 12,
"Equalizer low frequency range gain [dB]");
BIND_NUM(snd_eq_low_cutoff, 200, 50, 800,
"Equalizer low cut-off frequency [Hz]");
// Mid 1
BIND_NUM(snd_eq_mid1_gain, 0, -12, 12,
"Equalizer mid1 frequency range gain [dB]");
BIND_NUM(snd_eq_mid1_center, 500, 200, 3000,
"Equalizer mid1 center frequency [Hz]");
BIND_NUM(snd_eq_mid1_width, 100, 1, 100,
"Equalizer mid1 bandwidth [octave] (1 = 0.01; 100 = 1.0)");
// Mid 2
BIND_NUM(snd_eq_mid2_gain, 0, -12, 12,
"Equalizer mid2 frequency range gain [dB]");
BIND_NUM(snd_eq_mid2_center, 3000, 1000, 8000,
"Equalizer mid2 center frequency [Hz]");
BIND_NUM(snd_eq_mid2_width, 100, 1, 100,
"Equalizer mid2 bandwidth [octave] (1 = 0.01; 100 = 1.0)");
// High
BIND_NUM(snd_eq_high_gain, 0, -12, 12,
"Equalizer high frequency range gain [dB]");
BIND_NUM(snd_eq_high_cutoff, 6000, 4000, 16000,
"Equalizer high cut-off frequency [Hz]");
}
boolean I_OAL_InitSound(int snd_module)
@ -519,6 +764,8 @@ boolean I_OAL_InitSound(int snd_module)
(alcIsExtensionPresent(oal->device, "ALC_EXT_EFX") == ALC_TRUE);
oal->EXT_SOURCE_RADIUS =
(alIsExtensionPresent("AL_EXT_SOURCE_RADIUS") == AL_TRUE);
InitEqualizer();
InitDeferred();
ResetParams();

View File

@ -70,4 +70,8 @@ void I_OAL_SetPan(int channel, int separation);
void I_OAL_BindSoundVariables(void);
void I_OAL_SetEqualizer(void);
void I_OAL_EqualizerPreset(void);
#endif

View File

@ -308,6 +308,7 @@ enum
str_gamma,
str_sound_module,
str_resampler,
str_equalizer_preset,
str_mouse_accel,
@ -2054,6 +2055,8 @@ static void SetMidiPlayer(void)
S_RestartMusic();
}
static const char *equalizer_preset_strings[] = {"Off", "Classical", "Rock", "Vocal"};
static setup_menu_t gen_settings2[] = {
{"Sound Volume", S_THERMO, M_X_THRM8, M_THRM_SPC, {"sfx_volume"},
@ -2075,6 +2078,9 @@ static setup_menu_t gen_settings2[] = {
// [FG] play sounds in full length
{"Disable Sound Cutoffs", S_ONOFF, M_X, M_SPC, {"full_sounds"}},
{"Equalizer Preset", S_CHOICE, M_X, M_SPC, {"snd_equalizer"}, m_null, input_null,
str_equalizer_preset, I_OAL_EqualizerPreset},
{"Resampler", S_CHOICE | S_NEXT_LINE, M_X, M_SPC, {"snd_resampler"}, m_null,
input_null, str_resampler, I_OAL_SetResampler},
@ -2088,6 +2094,47 @@ static setup_menu_t gen_settings2[] = {
MI_END
};
/*
static setup_menu_t gen_settings_eq[] = {
{"Preamp dB", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_preamp"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
{"Low Gain dB", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_low_gain"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
{"Mid 1 Gain dB", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_mid1_gain"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
{"Mid 2 Gain dB", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_mid2_gain"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
{"High Gain dB", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_high_gain"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
{"Low Cutoff Hz", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_low_cutoff"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
{"Mid 1 Center Hz", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_mid1_center"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
{"Mid 2 Center Hz", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_mid2_center"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
{"High Cutoff Hz", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_high_cutoff"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
{"Mid 1 Width Oct", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_mid1_width"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
{"Mid 2 Width Oct", S_THERMO | S_THRM_SIZE11, M_X_THRM11, M_THRM_SPC,
{"snd_eq_mid2_width"}, m_null, input_null, str_empty, I_OAL_SetEqualizer},
MI_END
};
*/
static const char **GetResamplerStrings(void)
{
const char **strings = I_OAL_GetResamplerStrings();
@ -3804,6 +3851,7 @@ static const char **selectstrings[] = {
gamma_strings,
sound_module_strings,
NULL, // str_resampler
equalizer_preset_strings,
NULL, // str_mouse_accel
default_skill_strings,
default_complevel_strings,