axmol/thirdparty/openal/al/effects/echo.cpp

570 lines
14 KiB
C++

#include "config.h"
#include "AL/al.h"
#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 {
static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short");
static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short");
void Echo_setParami(EffectProps*, ALenum param, int)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
void Echo_setParamiv(EffectProps*, ALenum param, const int*)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
void Echo_setParamf(EffectProps *props, ALenum param, float val)
{
switch(param)
{
case AL_ECHO_DELAY:
if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"};
props->Echo.Delay = val;
break;
case AL_ECHO_LRDELAY:
if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"};
props->Echo.LRDelay = val;
break;
case AL_ECHO_DAMPING:
if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"};
props->Echo.Damping = val;
break;
case AL_ECHO_FEEDBACK:
if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"};
props->Echo.Feedback = val;
break;
case AL_ECHO_SPREAD:
if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"};
props->Echo.Spread = val;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
}
}
void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals)
{ Echo_setParamf(props, param, vals[0]); }
void Echo_getParami(const EffectProps*, ALenum param, int*)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
void Echo_getParamiv(const EffectProps*, ALenum param, int*)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
void Echo_getParamf(const EffectProps *props, ALenum param, float *val)
{
switch(param)
{
case AL_ECHO_DELAY:
*val = props->Echo.Delay;
break;
case AL_ECHO_LRDELAY:
*val = props->Echo.LRDelay;
break;
case AL_ECHO_DAMPING:
*val = props->Echo.Damping;
break;
case AL_ECHO_FEEDBACK:
*val = props->Echo.Feedback;
break;
case AL_ECHO_SPREAD:
*val = props->Echo.Spread;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
}
}
void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals)
{ Echo_getParamf(props, param, vals); }
EffectProps genDefaultProps() noexcept
{
EffectProps props{};
props.Echo.Delay = AL_ECHO_DEFAULT_DELAY;
props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING;
props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD;
return props;
}
} // namespace
DEFINE_ALEFFECT_VTABLE(Echo);
const EffectProps EchoEffectProps{genDefaultProps()};
#ifdef ALSOFT_EAX
namespace {
using EaxEchoEffectDirtyFlagsValue = std::uint_least8_t;
struct EaxEchoEffectDirtyFlags
{
using EaxIsBitFieldStruct = bool;
EaxEchoEffectDirtyFlagsValue flDelay : 1;
EaxEchoEffectDirtyFlagsValue flLRDelay : 1;
EaxEchoEffectDirtyFlagsValue flDamping : 1;
EaxEchoEffectDirtyFlagsValue flFeedback : 1;
EaxEchoEffectDirtyFlagsValue flSpread : 1;
}; // EaxEchoEffectDirtyFlags
class EaxEchoEffect final :
public EaxEffect
{
public:
EaxEchoEffect();
void dispatch(const EaxEaxCall& eax_call) override;
// [[nodiscard]]
bool apply_deferred() override;
private:
EAXECHOPROPERTIES eax_{};
EAXECHOPROPERTIES eax_d_{};
EaxEchoEffectDirtyFlags eax_dirty_flags_{};
void set_eax_defaults();
void set_efx_delay();
void set_efx_lr_delay();
void set_efx_damping();
void set_efx_feedback();
void set_efx_spread();
void set_efx_defaults();
void get(const EaxEaxCall& eax_call);
void validate_delay(float flDelay);
void validate_lr_delay(float flLRDelay);
void validate_damping(float flDamping);
void validate_feedback(float flFeedback);
void validate_spread(float flSpread);
void validate_all(const EAXECHOPROPERTIES& all);
void defer_delay(float flDelay);
void defer_lr_delay(float flLRDelay);
void defer_damping(float flDamping);
void defer_feedback(float flFeedback);
void defer_spread(float flSpread);
void defer_all(const EAXECHOPROPERTIES& all);
void defer_delay(const EaxEaxCall& eax_call);
void defer_lr_delay(const EaxEaxCall& eax_call);
void defer_damping(const EaxEaxCall& eax_call);
void defer_feedback(const EaxEaxCall& eax_call);
void defer_spread(const EaxEaxCall& eax_call);
void defer_all(const EaxEaxCall& eax_call);
void set(const EaxEaxCall& eax_call);
}; // EaxEchoEffect
class EaxEchoEffectException :
public EaxException
{
public:
explicit EaxEchoEffectException(
const char* message)
:
EaxException{"EAX_ECHO_EFFECT", message}
{
}
}; // EaxEchoEffectException
EaxEchoEffect::EaxEchoEffect()
: EaxEffect{AL_EFFECT_ECHO}
{
set_eax_defaults();
set_efx_defaults();
}
void EaxEchoEffect::dispatch(
const EaxEaxCall& eax_call)
{
eax_call.is_get() ? get(eax_call) : set(eax_call);
}
void EaxEchoEffect::set_eax_defaults()
{
eax_.flDelay = EAXECHO_DEFAULTDELAY;
eax_.flLRDelay = EAXECHO_DEFAULTLRDELAY;
eax_.flDamping = EAXECHO_DEFAULTDAMPING;
eax_.flFeedback = EAXECHO_DEFAULTFEEDBACK;
eax_.flSpread = EAXECHO_DEFAULTSPREAD;
eax_d_ = eax_;
}
void EaxEchoEffect::set_efx_delay()
{
const auto delay = clamp(
eax_.flDelay,
AL_ECHO_MIN_DELAY,
AL_ECHO_MAX_DELAY);
al_effect_props_.Echo.Delay = delay;
}
void EaxEchoEffect::set_efx_lr_delay()
{
const auto lr_delay = clamp(
eax_.flLRDelay,
AL_ECHO_MIN_LRDELAY,
AL_ECHO_MAX_LRDELAY);
al_effect_props_.Echo.LRDelay = lr_delay;
}
void EaxEchoEffect::set_efx_damping()
{
const auto damping = clamp(
eax_.flDamping,
AL_ECHO_MIN_DAMPING,
AL_ECHO_MAX_DAMPING);
al_effect_props_.Echo.Damping = damping;
}
void EaxEchoEffect::set_efx_feedback()
{
const auto feedback = clamp(
eax_.flFeedback,
AL_ECHO_MIN_FEEDBACK,
AL_ECHO_MAX_FEEDBACK);
al_effect_props_.Echo.Feedback = feedback;
}
void EaxEchoEffect::set_efx_spread()
{
const auto spread = clamp(
eax_.flSpread,
AL_ECHO_MIN_SPREAD,
AL_ECHO_MAX_SPREAD);
al_effect_props_.Echo.Spread = spread;
}
void EaxEchoEffect::set_efx_defaults()
{
set_efx_delay();
set_efx_lr_delay();
set_efx_damping();
set_efx_feedback();
set_efx_spread();
}
void EaxEchoEffect::get(const EaxEaxCall& eax_call)
{
switch(eax_call.get_property_id())
{
case EAXECHO_NONE:
break;
case EAXECHO_ALLPARAMETERS:
eax_call.set_value<EaxEchoEffectException>(eax_);
break;
case EAXECHO_DELAY:
eax_call.set_value<EaxEchoEffectException>(eax_.flDelay);
break;
case EAXECHO_LRDELAY:
eax_call.set_value<EaxEchoEffectException>(eax_.flLRDelay);
break;
case EAXECHO_DAMPING:
eax_call.set_value<EaxEchoEffectException>(eax_.flDamping);
break;
case EAXECHO_FEEDBACK:
eax_call.set_value<EaxEchoEffectException>(eax_.flFeedback);
break;
case EAXECHO_SPREAD:
eax_call.set_value<EaxEchoEffectException>(eax_.flSpread);
break;
default:
throw EaxEchoEffectException{"Unsupported property id."};
}
}
void EaxEchoEffect::validate_delay(
float flDelay)
{
eax_validate_range<EaxEchoEffectException>(
"Delay",
flDelay,
EAXECHO_MINDELAY,
EAXECHO_MAXDELAY);
}
void EaxEchoEffect::validate_lr_delay(
float flLRDelay)
{
eax_validate_range<EaxEchoEffectException>(
"LR Delay",
flLRDelay,
EAXECHO_MINLRDELAY,
EAXECHO_MAXLRDELAY);
}
void EaxEchoEffect::validate_damping(
float flDamping)
{
eax_validate_range<EaxEchoEffectException>(
"Damping",
flDamping,
EAXECHO_MINDAMPING,
EAXECHO_MAXDAMPING);
}
void EaxEchoEffect::validate_feedback(
float flFeedback)
{
eax_validate_range<EaxEchoEffectException>(
"Feedback",
flFeedback,
EAXECHO_MINFEEDBACK,
EAXECHO_MAXFEEDBACK);
}
void EaxEchoEffect::validate_spread(
float flSpread)
{
eax_validate_range<EaxEchoEffectException>(
"Spread",
flSpread,
EAXECHO_MINSPREAD,
EAXECHO_MAXSPREAD);
}
void EaxEchoEffect::validate_all(
const EAXECHOPROPERTIES& all)
{
validate_delay(all.flDelay);
validate_lr_delay(all.flLRDelay);
validate_damping(all.flDamping);
validate_feedback(all.flFeedback);
validate_spread(all.flSpread);
}
void EaxEchoEffect::defer_delay(
float flDelay)
{
eax_d_.flDelay = flDelay;
eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay);
}
void EaxEchoEffect::defer_lr_delay(
float flLRDelay)
{
eax_d_.flLRDelay = flLRDelay;
eax_dirty_flags_.flLRDelay = (eax_.flLRDelay != eax_d_.flLRDelay);
}
void EaxEchoEffect::defer_damping(
float flDamping)
{
eax_d_.flDamping = flDamping;
eax_dirty_flags_.flDamping = (eax_.flDamping != eax_d_.flDamping);
}
void EaxEchoEffect::defer_feedback(
float flFeedback)
{
eax_d_.flFeedback = flFeedback;
eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback);
}
void EaxEchoEffect::defer_spread(
float flSpread)
{
eax_d_.flSpread = flSpread;
eax_dirty_flags_.flSpread = (eax_.flSpread != eax_d_.flSpread);
}
void EaxEchoEffect::defer_all(
const EAXECHOPROPERTIES& all)
{
defer_delay(all.flDelay);
defer_lr_delay(all.flLRDelay);
defer_damping(all.flDamping);
defer_feedback(all.flFeedback);
defer_spread(all.flSpread);
}
void EaxEchoEffect::defer_delay(
const EaxEaxCall& eax_call)
{
const auto& delay =
eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flDelay)>();
validate_delay(delay);
defer_delay(delay);
}
void EaxEchoEffect::defer_lr_delay(
const EaxEaxCall& eax_call)
{
const auto& lr_delay =
eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flLRDelay)>();
validate_lr_delay(lr_delay);
defer_lr_delay(lr_delay);
}
void EaxEchoEffect::defer_damping(
const EaxEaxCall& eax_call)
{
const auto& damping =
eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flDamping)>();
validate_damping(damping);
defer_damping(damping);
}
void EaxEchoEffect::defer_feedback(
const EaxEaxCall& eax_call)
{
const auto& feedback =
eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flFeedback)>();
validate_feedback(feedback);
defer_feedback(feedback);
}
void EaxEchoEffect::defer_spread(
const EaxEaxCall& eax_call)
{
const auto& spread =
eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flSpread)>();
validate_spread(spread);
defer_spread(spread);
}
void EaxEchoEffect::defer_all(
const EaxEaxCall& eax_call)
{
const auto& all =
eax_call.get_value<EaxEchoEffectException, const EAXECHOPROPERTIES>();
validate_all(all);
defer_all(all);
}
// [[nodiscard]]
bool EaxEchoEffect::apply_deferred()
{
if (eax_dirty_flags_ == EaxEchoEffectDirtyFlags{})
{
return false;
}
eax_ = eax_d_;
if (eax_dirty_flags_.flDelay)
{
set_efx_delay();
}
if (eax_dirty_flags_.flLRDelay)
{
set_efx_lr_delay();
}
if (eax_dirty_flags_.flDamping)
{
set_efx_damping();
}
if (eax_dirty_flags_.flFeedback)
{
set_efx_feedback();
}
if (eax_dirty_flags_.flSpread)
{
set_efx_spread();
}
eax_dirty_flags_ = EaxEchoEffectDirtyFlags{};
return true;
}
void EaxEchoEffect::set(const EaxEaxCall& eax_call)
{
switch(eax_call.get_property_id())
{
case EAXECHO_NONE:
break;
case EAXECHO_ALLPARAMETERS:
defer_all(eax_call);
break;
case EAXECHO_DELAY:
defer_delay(eax_call);
break;
case EAXECHO_LRDELAY:
defer_lr_delay(eax_call);
break;
case EAXECHO_DAMPING:
defer_damping(eax_call);
break;
case EAXECHO_FEEDBACK:
defer_feedback(eax_call);
break;
case EAXECHO_SPREAD:
defer_spread(eax_call);
break;
default:
throw EaxEchoEffectException{"Unsupported property id."};
}
}
} // namespace
EaxEffectUPtr eax_create_eax_echo_effect()
{
return std::make_unique<EaxEchoEffect>();
}
#endif // ALSOFT_EAX