|
|
#include "config.h"
|
|
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
|
|
#include <algorithm>
|
|
|
|
#include "AL/efx.h"
|
|
|
|
#include "alc/effects/base.h"
|
|
#include "effects.h"
|
|
|
|
#ifdef ALSOFT_EAX
|
|
#include "alnumeric.h"
|
|
|
|
#include "al/eax_exception.h"
|
|
#include "al/eax_utils.h"
|
|
#endif // ALSOFT_EAX
|
|
|
|
|
|
namespace {
|
|
|
|
void Autowah_setParamf(EffectProps *props, ALenum param, float val)
|
|
{
|
|
switch(param)
|
|
{
|
|
case AL_AUTOWAH_ATTACK_TIME:
|
|
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
|
|
throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"};
|
|
props->Autowah.AttackTime = val;
|
|
break;
|
|
|
|
case AL_AUTOWAH_RELEASE_TIME:
|
|
if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
|
|
throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"};
|
|
props->Autowah.ReleaseTime = val;
|
|
break;
|
|
|
|
case AL_AUTOWAH_RESONANCE:
|
|
if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
|
|
throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"};
|
|
props->Autowah.Resonance = val;
|
|
break;
|
|
|
|
case AL_AUTOWAH_PEAK_GAIN:
|
|
if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
|
|
throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"};
|
|
props->Autowah.PeakGain = val;
|
|
break;
|
|
|
|
default:
|
|
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
|
}
|
|
}
|
|
void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
|
{ Autowah_setParamf(props, param, vals[0]); }
|
|
|
|
void Autowah_setParami(EffectProps*, ALenum param, int)
|
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
|
void Autowah_setParamiv(EffectProps*, ALenum param, const int*)
|
|
{
|
|
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
|
param};
|
|
}
|
|
|
|
void Autowah_getParamf(const EffectProps *props, ALenum param, float *val)
|
|
{
|
|
switch(param)
|
|
{
|
|
case AL_AUTOWAH_ATTACK_TIME:
|
|
*val = props->Autowah.AttackTime;
|
|
break;
|
|
|
|
case AL_AUTOWAH_RELEASE_TIME:
|
|
*val = props->Autowah.ReleaseTime;
|
|
break;
|
|
|
|
case AL_AUTOWAH_RESONANCE:
|
|
*val = props->Autowah.Resonance;
|
|
break;
|
|
|
|
case AL_AUTOWAH_PEAK_GAIN:
|
|
*val = props->Autowah.PeakGain;
|
|
break;
|
|
|
|
default:
|
|
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
|
}
|
|
|
|
}
|
|
void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
|
{ Autowah_getParamf(props, param, vals); }
|
|
|
|
void Autowah_getParami(const EffectProps*, ALenum param, int*)
|
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
|
void Autowah_getParamiv(const EffectProps*, ALenum param, int*)
|
|
{
|
|
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
|
param};
|
|
}
|
|
|
|
EffectProps genDefaultProps() noexcept
|
|
{
|
|
EffectProps props{};
|
|
props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
|
|
props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
|
|
props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
|
|
props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
|
|
return props;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
DEFINE_ALEFFECT_VTABLE(Autowah);
|
|
|
|
const EffectProps AutowahEffectProps{genDefaultProps()};
|
|
|
|
#ifdef ALSOFT_EAX
|
|
namespace {
|
|
|
|
using EaxAutoWahEffectDirtyFlagsValue = std::uint_least8_t;
|
|
|
|
struct EaxAutoWahEffectDirtyFlags
|
|
{
|
|
using EaxIsBitFieldStruct = bool;
|
|
|
|
EaxAutoWahEffectDirtyFlagsValue flAttackTime : 1;
|
|
EaxAutoWahEffectDirtyFlagsValue flReleaseTime : 1;
|
|
EaxAutoWahEffectDirtyFlagsValue lResonance : 1;
|
|
EaxAutoWahEffectDirtyFlagsValue lPeakLevel : 1;
|
|
}; // EaxAutoWahEffectDirtyFlags
|
|
|
|
|
|
class EaxAutoWahEffect final :
|
|
public EaxEffect
|
|
{
|
|
public:
|
|
EaxAutoWahEffect();
|
|
|
|
|
|
void dispatch(const EaxEaxCall& eax_call) override;
|
|
|
|
// [[nodiscard]]
|
|
bool apply_deferred() override;
|
|
|
|
private:
|
|
EAXAUTOWAHPROPERTIES eax_{};
|
|
EAXAUTOWAHPROPERTIES eax_d_{};
|
|
EaxAutoWahEffectDirtyFlags eax_dirty_flags_{};
|
|
|
|
|
|
void set_eax_defaults();
|
|
|
|
|
|
void set_efx_attack_time();
|
|
|
|
void set_efx_release_time();
|
|
|
|
void set_efx_resonance();
|
|
|
|
void set_efx_peak_gain();
|
|
|
|
void set_efx_defaults();
|
|
|
|
|
|
void get(const EaxEaxCall& eax_call);
|
|
|
|
|
|
void validate_attack_time(
|
|
float flAttackTime);
|
|
|
|
void validate_release_time(
|
|
float flReleaseTime);
|
|
|
|
void validate_resonance(
|
|
long lResonance);
|
|
|
|
void validate_peak_level(
|
|
long lPeakLevel);
|
|
|
|
void validate_all(
|
|
const EAXAUTOWAHPROPERTIES& eax_all);
|
|
|
|
|
|
void defer_attack_time(
|
|
float flAttackTime);
|
|
|
|
void defer_release_time(
|
|
float flReleaseTime);
|
|
|
|
void defer_resonance(
|
|
long lResonance);
|
|
|
|
void defer_peak_level(
|
|
long lPeakLevel);
|
|
|
|
void defer_all(
|
|
const EAXAUTOWAHPROPERTIES& eax_all);
|
|
|
|
|
|
void defer_attack_time(
|
|
const EaxEaxCall& eax_call);
|
|
|
|
void defer_release_time(
|
|
const EaxEaxCall& eax_call);
|
|
|
|
void defer_resonance(
|
|
const EaxEaxCall& eax_call);
|
|
|
|
void defer_peak_level(
|
|
const EaxEaxCall& eax_call);
|
|
|
|
void defer_all(
|
|
const EaxEaxCall& eax_call);
|
|
|
|
void set(const EaxEaxCall& eax_call);
|
|
}; // EaxAutoWahEffect
|
|
|
|
|
|
class EaxAutoWahEffectException :
|
|
public EaxException
|
|
{
|
|
public:
|
|
explicit EaxAutoWahEffectException(
|
|
const char* message)
|
|
:
|
|
EaxException{"EAX_AUTO_WAH_EFFECT", message}
|
|
{
|
|
}
|
|
}; // EaxAutoWahEffectException
|
|
|
|
|
|
EaxAutoWahEffect::EaxAutoWahEffect()
|
|
: EaxEffect{AL_EFFECT_AUTOWAH}
|
|
{
|
|
set_eax_defaults();
|
|
set_efx_defaults();
|
|
}
|
|
|
|
void EaxAutoWahEffect::dispatch(const EaxEaxCall& eax_call)
|
|
{
|
|
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
|
}
|
|
|
|
void EaxAutoWahEffect::set_eax_defaults()
|
|
{
|
|
eax_.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
|
|
eax_.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
|
|
eax_.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
|
|
eax_.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
|
|
|
|
eax_d_ = eax_;
|
|
}
|
|
|
|
void EaxAutoWahEffect::set_efx_attack_time()
|
|
{
|
|
const auto attack_time = clamp(
|
|
eax_.flAttackTime,
|
|
AL_AUTOWAH_MIN_ATTACK_TIME,
|
|
AL_AUTOWAH_MAX_ATTACK_TIME);
|
|
|
|
al_effect_props_.Autowah.AttackTime = attack_time;
|
|
}
|
|
|
|
void EaxAutoWahEffect::set_efx_release_time()
|
|
{
|
|
const auto release_time = clamp(
|
|
eax_.flReleaseTime,
|
|
AL_AUTOWAH_MIN_RELEASE_TIME,
|
|
AL_AUTOWAH_MAX_RELEASE_TIME);
|
|
|
|
al_effect_props_.Autowah.ReleaseTime = release_time;
|
|
}
|
|
|
|
void EaxAutoWahEffect::set_efx_resonance()
|
|
{
|
|
const auto resonance = clamp(
|
|
level_mb_to_gain(static_cast<float>(eax_.lResonance)),
|
|
AL_AUTOWAH_MIN_RESONANCE,
|
|
AL_AUTOWAH_MAX_RESONANCE);
|
|
|
|
al_effect_props_.Autowah.Resonance = resonance;
|
|
}
|
|
|
|
void EaxAutoWahEffect::set_efx_peak_gain()
|
|
{
|
|
const auto peak_gain = clamp(
|
|
level_mb_to_gain(static_cast<float>(eax_.lPeakLevel)),
|
|
AL_AUTOWAH_MIN_PEAK_GAIN,
|
|
AL_AUTOWAH_MAX_PEAK_GAIN);
|
|
|
|
al_effect_props_.Autowah.PeakGain = peak_gain;
|
|
}
|
|
|
|
void EaxAutoWahEffect::set_efx_defaults()
|
|
{
|
|
set_efx_attack_time();
|
|
set_efx_release_time();
|
|
set_efx_resonance();
|
|
set_efx_peak_gain();
|
|
}
|
|
|
|
void EaxAutoWahEffect::get(const EaxEaxCall& eax_call)
|
|
{
|
|
switch (eax_call.get_property_id())
|
|
{
|
|
case EAXAUTOWAH_NONE:
|
|
break;
|
|
|
|
case EAXAUTOWAH_ALLPARAMETERS:
|
|
eax_call.set_value<EaxAutoWahEffectException>(eax_);
|
|
break;
|
|
|
|
case EAXAUTOWAH_ATTACKTIME:
|
|
eax_call.set_value<EaxAutoWahEffectException>(eax_.flAttackTime);
|
|
break;
|
|
|
|
case EAXAUTOWAH_RELEASETIME:
|
|
eax_call.set_value<EaxAutoWahEffectException>(eax_.flReleaseTime);
|
|
break;
|
|
|
|
case EAXAUTOWAH_RESONANCE:
|
|
eax_call.set_value<EaxAutoWahEffectException>(eax_.lResonance);
|
|
break;
|
|
|
|
case EAXAUTOWAH_PEAKLEVEL:
|
|
eax_call.set_value<EaxAutoWahEffectException>(eax_.lPeakLevel);
|
|
break;
|
|
|
|
default:
|
|
throw EaxAutoWahEffectException{"Unsupported property id."};
|
|
}
|
|
}
|
|
|
|
void EaxAutoWahEffect::validate_attack_time(
|
|
float flAttackTime)
|
|
{
|
|
eax_validate_range<EaxAutoWahEffectException>(
|
|
"Attack Time",
|
|
flAttackTime,
|
|
EAXAUTOWAH_MINATTACKTIME,
|
|
EAXAUTOWAH_MAXATTACKTIME);
|
|
}
|
|
|
|
void EaxAutoWahEffect::validate_release_time(
|
|
float flReleaseTime)
|
|
{
|
|
eax_validate_range<EaxAutoWahEffectException>(
|
|
"Release Time",
|
|
flReleaseTime,
|
|
EAXAUTOWAH_MINRELEASETIME,
|
|
EAXAUTOWAH_MAXRELEASETIME);
|
|
}
|
|
|
|
void EaxAutoWahEffect::validate_resonance(
|
|
long lResonance)
|
|
{
|
|
eax_validate_range<EaxAutoWahEffectException>(
|
|
"Resonance",
|
|
lResonance,
|
|
EAXAUTOWAH_MINRESONANCE,
|
|
EAXAUTOWAH_MAXRESONANCE);
|
|
}
|
|
|
|
void EaxAutoWahEffect::validate_peak_level(
|
|
long lPeakLevel)
|
|
{
|
|
eax_validate_range<EaxAutoWahEffectException>(
|
|
"Peak Level",
|
|
lPeakLevel,
|
|
EAXAUTOWAH_MINPEAKLEVEL,
|
|
EAXAUTOWAH_MAXPEAKLEVEL);
|
|
}
|
|
|
|
void EaxAutoWahEffect::validate_all(
|
|
const EAXAUTOWAHPROPERTIES& eax_all)
|
|
{
|
|
validate_attack_time(eax_all.flAttackTime);
|
|
validate_release_time(eax_all.flReleaseTime);
|
|
validate_resonance(eax_all.lResonance);
|
|
validate_peak_level(eax_all.lPeakLevel);
|
|
}
|
|
|
|
void EaxAutoWahEffect::defer_attack_time(
|
|
float flAttackTime)
|
|
{
|
|
eax_d_.flAttackTime = flAttackTime;
|
|
eax_dirty_flags_.flAttackTime = (eax_.flAttackTime != eax_d_.flAttackTime);
|
|
}
|
|
|
|
void EaxAutoWahEffect::defer_release_time(
|
|
float flReleaseTime)
|
|
{
|
|
eax_d_.flReleaseTime = flReleaseTime;
|
|
eax_dirty_flags_.flReleaseTime = (eax_.flReleaseTime != eax_d_.flReleaseTime);
|
|
}
|
|
|
|
void EaxAutoWahEffect::defer_resonance(
|
|
long lResonance)
|
|
{
|
|
eax_d_.lResonance = lResonance;
|
|
eax_dirty_flags_.lResonance = (eax_.lResonance != eax_d_.lResonance);
|
|
}
|
|
|
|
void EaxAutoWahEffect::defer_peak_level(
|
|
long lPeakLevel)
|
|
{
|
|
eax_d_.lPeakLevel = lPeakLevel;
|
|
eax_dirty_flags_.lPeakLevel = (eax_.lPeakLevel != eax_d_.lPeakLevel);
|
|
}
|
|
|
|
void EaxAutoWahEffect::defer_all(
|
|
const EAXAUTOWAHPROPERTIES& eax_all)
|
|
{
|
|
validate_all(eax_all);
|
|
|
|
defer_attack_time(eax_all.flAttackTime);
|
|
defer_release_time(eax_all.flReleaseTime);
|
|
defer_resonance(eax_all.lResonance);
|
|
defer_peak_level(eax_all.lPeakLevel);
|
|
}
|
|
|
|
void EaxAutoWahEffect::defer_attack_time(
|
|
const EaxEaxCall& eax_call)
|
|
{
|
|
const auto& attack_time =
|
|
eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::flAttackTime)>();
|
|
|
|
validate_attack_time(attack_time);
|
|
defer_attack_time(attack_time);
|
|
}
|
|
|
|
void EaxAutoWahEffect::defer_release_time(
|
|
const EaxEaxCall& eax_call)
|
|
{
|
|
const auto& release_time =
|
|
eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::flReleaseTime)>();
|
|
|
|
validate_release_time(release_time);
|
|
defer_release_time(release_time);
|
|
}
|
|
|
|
void EaxAutoWahEffect::defer_resonance(
|
|
const EaxEaxCall& eax_call)
|
|
{
|
|
const auto& resonance =
|
|
eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::lResonance)>();
|
|
|
|
validate_resonance(resonance);
|
|
defer_resonance(resonance);
|
|
}
|
|
|
|
void EaxAutoWahEffect::defer_peak_level(
|
|
const EaxEaxCall& eax_call)
|
|
{
|
|
const auto& peak_level =
|
|
eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::lPeakLevel)>();
|
|
|
|
validate_peak_level(peak_level);
|
|
defer_peak_level(peak_level);
|
|
}
|
|
|
|
void EaxAutoWahEffect::defer_all(
|
|
const EaxEaxCall& eax_call)
|
|
{
|
|
const auto& all =
|
|
eax_call.get_value<EaxAutoWahEffectException, const EAXAUTOWAHPROPERTIES>();
|
|
|
|
validate_all(all);
|
|
defer_all(all);
|
|
}
|
|
|
|
// [[nodiscard]]
|
|
bool EaxAutoWahEffect::apply_deferred()
|
|
{
|
|
if (eax_dirty_flags_ == EaxAutoWahEffectDirtyFlags{})
|
|
{
|
|
return false;
|
|
}
|
|
|
|
eax_ = eax_d_;
|
|
|
|
if (eax_dirty_flags_.flAttackTime)
|
|
{
|
|
set_efx_attack_time();
|
|
}
|
|
|
|
if (eax_dirty_flags_.flReleaseTime)
|
|
{
|
|
set_efx_release_time();
|
|
}
|
|
|
|
if (eax_dirty_flags_.lResonance)
|
|
{
|
|
set_efx_resonance();
|
|
}
|
|
|
|
if (eax_dirty_flags_.lPeakLevel)
|
|
{
|
|
set_efx_peak_gain();
|
|
}
|
|
|
|
eax_dirty_flags_ = EaxAutoWahEffectDirtyFlags{};
|
|
|
|
return true;
|
|
}
|
|
|
|
void EaxAutoWahEffect::set(const EaxEaxCall& eax_call)
|
|
{
|
|
switch (eax_call.get_property_id())
|
|
{
|
|
case EAXAUTOWAH_NONE:
|
|
break;
|
|
|
|
case EAXAUTOWAH_ALLPARAMETERS:
|
|
defer_all(eax_call);
|
|
break;
|
|
|
|
case EAXAUTOWAH_ATTACKTIME:
|
|
defer_attack_time(eax_call);
|
|
break;
|
|
|
|
case EAXAUTOWAH_RELEASETIME:
|
|
defer_release_time(eax_call);
|
|
break;
|
|
|
|
case EAXAUTOWAH_RESONANCE:
|
|
defer_resonance(eax_call);
|
|
break;
|
|
|
|
case EAXAUTOWAH_PEAKLEVEL:
|
|
defer_peak_level(eax_call);
|
|
break;
|
|
|
|
default:
|
|
throw EaxAutoWahEffectException{"Unsupported property id."};
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
EaxEffectUPtr eax_create_eax_auto_wah_effect()
|
|
{
|
|
return std::make_unique<::EaxAutoWahEffect>();
|
|
}
|
|
|
|
#endif // ALSOFT_EAX
|