aboutsummaryrefslogtreecommitdiffstats
path: root/al/effects/distortion.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'al/effects/distortion.cpp')
-rw-r--r--al/effects/distortion.cpp536
1 files changed, 536 insertions, 0 deletions
diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp
index f5d64a9a..062cdc54 100644
--- a/al/effects/distortion.cpp
+++ b/al/effects/distortion.cpp
@@ -7,6 +7,13 @@
#include "alc/effects/base.h"
#include "effects.h"
+#if ALSOFT_EAX
+#include "alnumeric.h"
+
+#include "al/eax_exception.h"
+#include "al/eax_utils.h"
+#endif // ALSOFT_EAX
+
namespace {
@@ -112,3 +119,532 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Distortion);
const EffectProps DistortionEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxDistortionEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxDistortionEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxDistortionEffectDirtyFlagsValue flEdge : 1;
+ EaxDistortionEffectDirtyFlagsValue lGain : 1;
+ EaxDistortionEffectDirtyFlagsValue flLowPassCutOff : 1;
+ EaxDistortionEffectDirtyFlagsValue flEQCenter : 1;
+ EaxDistortionEffectDirtyFlagsValue flEQBandwidth : 1;
+}; // EaxDistortionEffectDirtyFlags
+
+
+class EaxDistortionEffect final :
+ public EaxEffect
+{
+public:
+ EaxDistortionEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXDISTORTIONPROPERTIES eax_{};
+ EAXDISTORTIONPROPERTIES eax_d_{};
+ EaxDistortionEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_edge();
+
+ void set_efx_gain();
+
+ void set_efx_lowpass_cutoff();
+
+ void set_efx_eq_center();
+
+ void set_efx_eq_bandwidth();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_edge(
+ float flEdge);
+
+ void validate_gain(
+ long lGain);
+
+ void validate_lowpass_cutoff(
+ float flLowPassCutOff);
+
+ void validate_eq_center(
+ float flEQCenter);
+
+ void validate_eq_bandwidth(
+ float flEQBandwidth);
+
+ void validate_all(
+ const EAXDISTORTIONPROPERTIES& eax_all);
+
+
+ void defer_edge(
+ float flEdge);
+
+ void defer_gain(
+ long lGain);
+
+ void defer_low_pass_cutoff(
+ float flLowPassCutOff);
+
+ void defer_eq_center(
+ float flEQCenter);
+
+ void defer_eq_bandwidth(
+ float flEQBandwidth);
+
+ void defer_all(
+ const EAXDISTORTIONPROPERTIES& eax_all);
+
+
+ void defer_edge(
+ const EaxEaxCall& eax_call);
+
+ void defer_gain(
+ const EaxEaxCall& eax_call);
+
+ void defer_low_pass_cutoff(
+ const EaxEaxCall& eax_call);
+
+ void defer_eq_center(
+ const EaxEaxCall& eax_call);
+
+ void defer_eq_bandwidth(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxDistortionEffect
+
+
+class EaxDistortionEffectException :
+ public EaxException
+{
+public:
+ explicit EaxDistortionEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_DISTORTION_EFFECT", message}
+ {
+ }
+}; // EaxDistortionEffectException
+
+
+EaxDistortionEffect::EaxDistortionEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxDistortionEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxDistortionEffect::set_eax_defaults()
+{
+ eax_.flEdge = EAXDISTORTION_DEFAULTEDGE;
+ eax_.lGain = EAXDISTORTION_DEFAULTGAIN;
+ eax_.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
+ eax_.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
+ eax_.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
+
+ eax_d_ = eax_;
+}
+
+void EaxDistortionEffect::set_efx_edge()
+{
+ const auto edge = clamp(
+ eax_.flEdge,
+ AL_DISTORTION_MIN_EDGE,
+ AL_DISTORTION_MAX_EDGE);
+
+ al_effect_props_.Distortion.Edge = edge;
+}
+
+void EaxDistortionEffect::set_efx_gain()
+{
+ const auto gain = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lGain)),
+ AL_DISTORTION_MIN_GAIN,
+ AL_DISTORTION_MAX_GAIN);
+
+ al_effect_props_.Distortion.Gain = gain;
+}
+
+void EaxDistortionEffect::set_efx_lowpass_cutoff()
+{
+ const auto lowpass_cutoff = clamp(
+ eax_.flLowPassCutOff,
+ AL_DISTORTION_MIN_LOWPASS_CUTOFF,
+ AL_DISTORTION_MAX_LOWPASS_CUTOFF);
+
+ al_effect_props_.Distortion.LowpassCutoff = lowpass_cutoff;
+}
+
+void EaxDistortionEffect::set_efx_eq_center()
+{
+ const auto eq_center = clamp(
+ eax_.flEQCenter,
+ AL_DISTORTION_MIN_EQCENTER,
+ AL_DISTORTION_MAX_EQCENTER);
+
+ al_effect_props_.Distortion.EQCenter = eq_center;
+}
+
+void EaxDistortionEffect::set_efx_eq_bandwidth()
+{
+ const auto eq_bandwidth = clamp(
+ eax_.flEdge,
+ AL_DISTORTION_MIN_EQBANDWIDTH,
+ AL_DISTORTION_MAX_EQBANDWIDTH);
+
+ al_effect_props_.Distortion.EQBandwidth = eq_bandwidth;
+}
+
+void EaxDistortionEffect::set_efx_defaults()
+{
+ set_efx_edge();
+ set_efx_gain();
+ set_efx_lowpass_cutoff();
+ set_efx_eq_center();
+ set_efx_eq_bandwidth();
+}
+
+// [[nodiscard]]
+bool EaxDistortionEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXDISTORTION_NONE:
+ break;
+
+ case EAXDISTORTION_ALLPARAMETERS:
+ eax_call.set_value<EaxDistortionEffectException>(eax_);
+ break;
+
+ case EAXDISTORTION_EDGE:
+ eax_call.set_value<EaxDistortionEffectException>(eax_.flEdge);
+ break;
+
+ case EAXDISTORTION_GAIN:
+ eax_call.set_value<EaxDistortionEffectException>(eax_.lGain);
+ break;
+
+ case EAXDISTORTION_LOWPASSCUTOFF:
+ eax_call.set_value<EaxDistortionEffectException>(eax_.flLowPassCutOff);
+ break;
+
+ case EAXDISTORTION_EQCENTER:
+ eax_call.set_value<EaxDistortionEffectException>(eax_.flEQCenter);
+ break;
+
+ case EAXDISTORTION_EQBANDWIDTH:
+ eax_call.set_value<EaxDistortionEffectException>(eax_.flEQBandwidth);
+ break;
+
+ default:
+ throw EaxDistortionEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxDistortionEffect::validate_edge(
+ float flEdge)
+{
+ eax_validate_range<EaxDistortionEffectException>(
+ "Edge",
+ flEdge,
+ EAXDISTORTION_MINEDGE,
+ EAXDISTORTION_MAXEDGE);
+}
+
+void EaxDistortionEffect::validate_gain(
+ long lGain)
+{
+ eax_validate_range<EaxDistortionEffectException>(
+ "Gain",
+ lGain,
+ EAXDISTORTION_MINGAIN,
+ EAXDISTORTION_MAXGAIN);
+}
+
+void EaxDistortionEffect::validate_lowpass_cutoff(
+ float flLowPassCutOff)
+{
+ eax_validate_range<EaxDistortionEffectException>(
+ "Low-pass Cut-off",
+ flLowPassCutOff,
+ EAXDISTORTION_MINLOWPASSCUTOFF,
+ EAXDISTORTION_MAXLOWPASSCUTOFF);
+}
+
+void EaxDistortionEffect::validate_eq_center(
+ float flEQCenter)
+{
+ eax_validate_range<EaxDistortionEffectException>(
+ "EQ Center",
+ flEQCenter,
+ EAXDISTORTION_MINEQCENTER,
+ EAXDISTORTION_MAXEQCENTER);
+}
+
+void EaxDistortionEffect::validate_eq_bandwidth(
+ float flEQBandwidth)
+{
+ eax_validate_range<EaxDistortionEffectException>(
+ "EQ Bandwidth",
+ flEQBandwidth,
+ EAXDISTORTION_MINEQBANDWIDTH,
+ EAXDISTORTION_MAXEQBANDWIDTH);
+}
+
+void EaxDistortionEffect::validate_all(
+ const EAXDISTORTIONPROPERTIES& eax_all)
+{
+ validate_edge(eax_all.flEdge);
+ validate_gain(eax_all.lGain);
+ validate_lowpass_cutoff(eax_all.flLowPassCutOff);
+ validate_eq_center(eax_all.flEQCenter);
+ validate_eq_bandwidth(eax_all.flEQBandwidth);
+}
+
+void EaxDistortionEffect::defer_edge(
+ float flEdge)
+{
+ eax_d_.flEdge = flEdge;
+ eax_dirty_flags_.flEdge = (eax_.flEdge != eax_d_.flEdge);
+}
+
+void EaxDistortionEffect::defer_gain(
+ long lGain)
+{
+ eax_d_.lGain = lGain;
+ eax_dirty_flags_.lGain = (eax_.lGain != eax_d_.lGain);
+}
+
+void EaxDistortionEffect::defer_low_pass_cutoff(
+ float flLowPassCutOff)
+{
+ eax_d_.flLowPassCutOff = flLowPassCutOff;
+ eax_dirty_flags_.flLowPassCutOff = (eax_.flLowPassCutOff != eax_d_.flLowPassCutOff);
+}
+
+void EaxDistortionEffect::defer_eq_center(
+ float flEQCenter)
+{
+ eax_d_.flEQCenter = flEQCenter;
+ eax_dirty_flags_.flEQCenter = (eax_.flEQCenter != eax_d_.flEQCenter);
+}
+
+void EaxDistortionEffect::defer_eq_bandwidth(
+ float flEQBandwidth)
+{
+ eax_d_.flEQBandwidth = flEQBandwidth;
+ eax_dirty_flags_.flEQBandwidth = (eax_.flEQBandwidth != eax_d_.flEQBandwidth);
+}
+
+void EaxDistortionEffect::defer_all(
+ const EAXDISTORTIONPROPERTIES& eax_all)
+{
+ defer_edge(eax_all.flEdge);
+ defer_gain(eax_all.lGain);
+ defer_low_pass_cutoff(eax_all.flLowPassCutOff);
+ defer_eq_center(eax_all.flEQCenter);
+ defer_eq_bandwidth(eax_all.flEQBandwidth);
+}
+
+void EaxDistortionEffect::defer_edge(
+ const EaxEaxCall& eax_call)
+{
+ const auto& edge =
+ eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEdge)>();
+
+ validate_edge(edge);
+ defer_edge(edge);
+}
+
+void EaxDistortionEffect::defer_gain(
+ const EaxEaxCall& eax_call)
+{
+ const auto& gain =
+ eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::lGain)>();
+
+ validate_gain(gain);
+ defer_gain(gain);
+}
+
+void EaxDistortionEffect::defer_low_pass_cutoff(
+ const EaxEaxCall& eax_call)
+{
+ const auto& lowpass_cutoff =
+ eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flLowPassCutOff)>();
+
+ validate_lowpass_cutoff(lowpass_cutoff);
+ defer_low_pass_cutoff(lowpass_cutoff);
+}
+
+void EaxDistortionEffect::defer_eq_center(
+ const EaxEaxCall& eax_call)
+{
+ const auto& eq_center =
+ eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEQCenter)>();
+
+ validate_eq_center(eq_center);
+ defer_eq_center(eq_center);
+}
+
+void EaxDistortionEffect::defer_eq_bandwidth(
+ const EaxEaxCall& eax_call)
+{
+ const auto& eq_bandwidth =
+ eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEQBandwidth)>();
+
+ validate_eq_bandwidth(eq_bandwidth);
+ defer_eq_bandwidth(eq_bandwidth);
+}
+
+void EaxDistortionEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all =
+ eax_call.get_value<EaxDistortionEffectException, const EAXDISTORTIONPROPERTIES>();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxDistortionEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxDistortionEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.flEdge)
+ {
+ set_efx_edge();
+ }
+
+ if (eax_dirty_flags_.lGain)
+ {
+ set_efx_gain();
+ }
+
+ if (eax_dirty_flags_.flLowPassCutOff)
+ {
+ set_efx_lowpass_cutoff();
+ }
+
+ if (eax_dirty_flags_.flEQCenter)
+ {
+ set_efx_eq_center();
+ }
+
+ if (eax_dirty_flags_.flEQBandwidth)
+ {
+ set_efx_eq_bandwidth();
+ }
+
+ eax_dirty_flags_ = EaxDistortionEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxDistortionEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXDISTORTION_NONE:
+ break;
+
+ case EAXDISTORTION_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXDISTORTION_EDGE:
+ defer_edge(eax_call);
+ break;
+
+ case EAXDISTORTION_GAIN:
+ defer_gain(eax_call);
+ break;
+
+ case EAXDISTORTION_LOWPASSCUTOFF:
+ defer_low_pass_cutoff(eax_call);
+ break;
+
+ case EAXDISTORTION_EQCENTER:
+ defer_eq_center(eax_call);
+ break;
+
+ case EAXDISTORTION_EQBANDWIDTH:
+ defer_eq_bandwidth(eax_call);
+ break;
+
+ default:
+ throw EaxDistortionEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_distortion_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxDistortionEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX