aboutsummaryrefslogtreecommitdiffstats
path: root/al/effects/modulator.cpp
diff options
context:
space:
mode:
authorBoris I. Bendovsky <[email protected]>2022-01-30 14:47:32 +0200
committerGitHub <[email protected]>2022-01-30 04:47:32 -0800
commit19ed994dc30ed84ea7cbbb5152577669fc25caf6 (patch)
treef68933bf8f778806618bd6c0b1bf9ced1b0ccf08 /al/effects/modulator.cpp
parent619249371a40f03cf988d1f5750d643df797c485 (diff)
Add EAX extensions (EAX 2.0-5.0, X-RAM) (#632)
* Add EAX extensions (EAX 2.0-5.0, X-RAM) * Comment out C++17 leftovers * Remove everything related to patching * Update alsoftrc.sample * Rewrite integration * Fix GCC compilation under Linux * Always reset EAX effect properties when loading it into FX slot
Diffstat (limited to 'al/effects/modulator.cpp')
-rw-r--r--al/effects/modulator.cpp411
1 files changed, 411 insertions, 0 deletions
diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp
index d8a1bed6..6a30dc09 100644
--- a/al/effects/modulator.cpp
+++ b/al/effects/modulator.cpp
@@ -10,6 +10,15 @@
#include "aloptional.h"
#include "effects.h"
+#if ALSOFT_EAX
+#include <cassert>
+
+#include "alnumeric.h"
+
+#include "al/eax_exception.h"
+#include "al/eax_utils.h"
+#endif // ALSOFT_EAX
+
namespace {
@@ -134,3 +143,405 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Modulator);
const EffectProps ModulatorEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxRingModulatorEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxRingModulatorEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxRingModulatorEffectDirtyFlagsValue flFrequency : 1;
+ EaxRingModulatorEffectDirtyFlagsValue flHighPassCutOff : 1;
+ EaxRingModulatorEffectDirtyFlagsValue ulWaveform : 1;
+}; // EaxPitchShifterEffectDirtyFlags
+
+
+class EaxRingModulatorEffect final :
+ public EaxEffect
+{
+public:
+ EaxRingModulatorEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXRINGMODULATORPROPERTIES eax_{};
+ EAXRINGMODULATORPROPERTIES eax_d_{};
+ EaxRingModulatorEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_frequency();
+
+ void set_efx_high_pass_cutoff();
+
+ void set_efx_waveform();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_frequency(
+ float flFrequency);
+
+ void validate_high_pass_cutoff(
+ float flHighPassCutOff);
+
+ void validate_waveform(
+ unsigned long ulWaveform);
+
+ void validate_all(
+ const EAXRINGMODULATORPROPERTIES& all);
+
+
+ void defer_frequency(
+ float flFrequency);
+
+ void defer_high_pass_cutoff(
+ float flHighPassCutOff);
+
+ void defer_waveform(
+ unsigned long ulWaveform);
+
+ void defer_all(
+ const EAXRINGMODULATORPROPERTIES& all);
+
+
+ void defer_frequency(
+ const EaxEaxCall& eax_call);
+
+ void defer_high_pass_cutoff(
+ const EaxEaxCall& eax_call);
+
+ void defer_waveform(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxRingModulatorEffect
+
+
+class EaxRingModulatorEffectException :
+ public EaxException
+{
+public:
+ explicit EaxRingModulatorEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_RING_MODULATOR_EFFECT", message}
+ {
+ }
+}; // EaxRingModulatorEffectException
+
+
+EaxRingModulatorEffect::EaxRingModulatorEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxRingModulatorEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxRingModulatorEffect::set_eax_defaults()
+{
+ eax_.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
+ eax_.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
+ eax_.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
+
+ eax_d_ = eax_;
+}
+
+void EaxRingModulatorEffect::set_efx_frequency()
+{
+ const auto frequency = clamp(
+ eax_.flFrequency,
+ AL_RING_MODULATOR_MIN_FREQUENCY,
+ AL_RING_MODULATOR_MAX_FREQUENCY);
+
+ al_effect_props_.Modulator.Frequency = frequency;
+}
+
+void EaxRingModulatorEffect::set_efx_high_pass_cutoff()
+{
+ const auto high_pass_cutoff = clamp(
+ eax_.flHighPassCutOff,
+ AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF,
+ AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF);
+
+ al_effect_props_.Modulator.HighPassCutoff = high_pass_cutoff;
+}
+
+void EaxRingModulatorEffect::set_efx_waveform()
+{
+ const auto waveform = clamp(
+ static_cast<ALint>(eax_.ulWaveform),
+ AL_RING_MODULATOR_MIN_WAVEFORM,
+ AL_RING_MODULATOR_MAX_WAVEFORM);
+
+ const auto efx_waveform = WaveformFromEmum(waveform);
+ assert(efx_waveform.has_value());
+ al_effect_props_.Modulator.Waveform = *efx_waveform;
+}
+
+void EaxRingModulatorEffect::set_efx_defaults()
+{
+ set_efx_frequency();
+ set_efx_high_pass_cutoff();
+ set_efx_waveform();
+}
+
+// [[nodiscard]]
+bool EaxRingModulatorEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXRINGMODULATOR_NONE:
+ break;
+
+ case EAXRINGMODULATOR_ALLPARAMETERS:
+ eax_call.set_value<EaxRingModulatorEffectException>(eax_);
+ break;
+
+ case EAXRINGMODULATOR_FREQUENCY:
+ eax_call.set_value<EaxRingModulatorEffectException>(eax_.flFrequency);
+ break;
+
+ case EAXRINGMODULATOR_HIGHPASSCUTOFF:
+ eax_call.set_value<EaxRingModulatorEffectException>(eax_.flHighPassCutOff);
+ break;
+
+ case EAXRINGMODULATOR_WAVEFORM:
+ eax_call.set_value<EaxRingModulatorEffectException>(eax_.ulWaveform);
+ break;
+
+ default:
+ throw EaxRingModulatorEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxRingModulatorEffect::validate_frequency(
+ float flFrequency)
+{
+ eax_validate_range<EaxRingModulatorEffectException>(
+ "Frequency",
+ flFrequency,
+ EAXRINGMODULATOR_MINFREQUENCY,
+ EAXRINGMODULATOR_MAXFREQUENCY);
+}
+
+void EaxRingModulatorEffect::validate_high_pass_cutoff(
+ float flHighPassCutOff)
+{
+ eax_validate_range<EaxRingModulatorEffectException>(
+ "High-Pass Cutoff",
+ flHighPassCutOff,
+ EAXRINGMODULATOR_MINHIGHPASSCUTOFF,
+ EAXRINGMODULATOR_MAXHIGHPASSCUTOFF);
+}
+
+void EaxRingModulatorEffect::validate_waveform(
+ unsigned long ulWaveform)
+{
+ eax_validate_range<EaxRingModulatorEffectException>(
+ "Waveform",
+ ulWaveform,
+ EAXRINGMODULATOR_MINWAVEFORM,
+ EAXRINGMODULATOR_MAXWAVEFORM);
+}
+
+void EaxRingModulatorEffect::validate_all(
+ const EAXRINGMODULATORPROPERTIES& all)
+{
+ validate_frequency(all.flFrequency);
+ validate_high_pass_cutoff(all.flHighPassCutOff);
+ validate_waveform(all.ulWaveform);
+}
+
+void EaxRingModulatorEffect::defer_frequency(
+ float flFrequency)
+{
+ eax_d_.flFrequency = flFrequency;
+ eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency);
+}
+
+void EaxRingModulatorEffect::defer_high_pass_cutoff(
+ float flHighPassCutOff)
+{
+ eax_d_.flHighPassCutOff = flHighPassCutOff;
+ eax_dirty_flags_.flHighPassCutOff = (eax_.flHighPassCutOff != eax_d_.flHighPassCutOff);
+}
+
+void EaxRingModulatorEffect::defer_waveform(
+ unsigned long ulWaveform)
+{
+ eax_d_.ulWaveform = ulWaveform;
+ eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
+}
+
+void EaxRingModulatorEffect::defer_all(
+ const EAXRINGMODULATORPROPERTIES& all)
+{
+ defer_frequency(all.flFrequency);
+ defer_high_pass_cutoff(all.flHighPassCutOff);
+ defer_waveform(all.ulWaveform);
+}
+
+void EaxRingModulatorEffect::defer_frequency(
+ const EaxEaxCall& eax_call)
+{
+ const auto& frequency =
+ eax_call.get_value<
+ EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flFrequency)>();
+
+ validate_frequency(frequency);
+ defer_frequency(frequency);
+}
+
+void EaxRingModulatorEffect::defer_high_pass_cutoff(
+ const EaxEaxCall& eax_call)
+{
+ const auto& high_pass_cutoff =
+ eax_call.get_value<
+ EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flHighPassCutOff)>();
+
+ validate_high_pass_cutoff(high_pass_cutoff);
+ defer_high_pass_cutoff(high_pass_cutoff);
+}
+
+void EaxRingModulatorEffect::defer_waveform(
+ const EaxEaxCall& eax_call)
+{
+ const auto& waveform =
+ eax_call.get_value<
+ EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::ulWaveform)>();
+
+ validate_waveform(waveform);
+ defer_waveform(waveform);
+}
+
+void EaxRingModulatorEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all =
+ eax_call.get_value<EaxRingModulatorEffectException, const EAXRINGMODULATORPROPERTIES>();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxRingModulatorEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxRingModulatorEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.flFrequency)
+ {
+ set_efx_frequency();
+ }
+
+ if (eax_dirty_flags_.flHighPassCutOff)
+ {
+ set_efx_high_pass_cutoff();
+ }
+
+ if (eax_dirty_flags_.ulWaveform)
+ {
+ set_efx_waveform();
+ }
+
+ eax_dirty_flags_ = EaxRingModulatorEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxRingModulatorEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXRINGMODULATOR_NONE:
+ break;
+
+ case EAXRINGMODULATOR_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXRINGMODULATOR_FREQUENCY:
+ defer_frequency(eax_call);
+ break;
+
+ case EAXRINGMODULATOR_HIGHPASSCUTOFF:
+ defer_high_pass_cutoff(eax_call);
+ break;
+
+ case EAXRINGMODULATOR_WAVEFORM:
+ defer_waveform(eax_call);
+ break;
+
+ default:
+ throw EaxRingModulatorEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_ring_modulator_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxRingModulatorEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX