aboutsummaryrefslogtreecommitdiffstats
path: root/al/effects/vmorpher.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/vmorpher.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/vmorpher.cpp')
-rw-r--r--al/effects/vmorpher.cpp624
1 files changed, 624 insertions, 0 deletions
diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp
index 1b4710ff..2a9e0702 100644
--- a/al/effects/vmorpher.cpp
+++ b/al/effects/vmorpher.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 {
@@ -247,3 +256,618 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Vmorpher);
const EffectProps VmorpherEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxVocalMorpherEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxVocalMorpherEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxVocalMorpherEffectDirtyFlagsValue ulPhonemeA : 1;
+ EaxVocalMorpherEffectDirtyFlagsValue lPhonemeACoarseTuning : 1;
+ EaxVocalMorpherEffectDirtyFlagsValue ulPhonemeB : 1;
+ EaxVocalMorpherEffectDirtyFlagsValue lPhonemeBCoarseTuning : 1;
+ EaxVocalMorpherEffectDirtyFlagsValue ulWaveform : 1;
+ EaxVocalMorpherEffectDirtyFlagsValue flRate : 1;
+}; // EaxPitchShifterEffectDirtyFlags
+
+
+class EaxVocalMorpherEffect final :
+ public EaxEffect
+{
+public:
+ EaxVocalMorpherEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXVOCALMORPHERPROPERTIES eax_{};
+ EAXVOCALMORPHERPROPERTIES eax_d_{};
+ EaxVocalMorpherEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_phoneme_a();
+
+ void set_efx_phoneme_a_coarse_tuning();
+
+ void set_efx_phoneme_b();
+
+ void set_efx_phoneme_b_coarse_tuning();
+
+ void set_efx_waveform();
+
+ void set_efx_rate();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_phoneme_a(
+ unsigned long ulPhonemeA);
+
+ void validate_phoneme_a_coarse_tuning(
+ long lPhonemeACoarseTuning);
+
+ void validate_phoneme_b(
+ unsigned long ulPhonemeB);
+
+ void validate_phoneme_b_coarse_tuning(
+ long lPhonemeBCoarseTuning);
+
+ void validate_waveform(
+ unsigned long ulWaveform);
+
+ void validate_rate(
+ float flRate);
+
+ void validate_all(
+ const EAXVOCALMORPHERPROPERTIES& all);
+
+
+ void defer_phoneme_a(
+ unsigned long ulPhonemeA);
+
+ void defer_phoneme_a_coarse_tuning(
+ long lPhonemeACoarseTuning);
+
+ void defer_phoneme_b(
+ unsigned long ulPhonemeB);
+
+ void defer_phoneme_b_coarse_tuning(
+ long lPhonemeBCoarseTuning);
+
+ void defer_waveform(
+ unsigned long ulWaveform);
+
+ void defer_rate(
+ float flRate);
+
+ void defer_all(
+ const EAXVOCALMORPHERPROPERTIES& all);
+
+
+ void defer_phoneme_a(
+ const EaxEaxCall& eax_call);
+
+ void defer_phoneme_a_coarse_tuning(
+ const EaxEaxCall& eax_call);
+
+ void defer_phoneme_b(
+ const EaxEaxCall& eax_call);
+
+ void defer_phoneme_b_coarse_tuning(
+ const EaxEaxCall& eax_call);
+
+ void defer_waveform(
+ const EaxEaxCall& eax_call);
+
+ void defer_rate(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxVocalMorpherEffect
+
+
+class EaxVocalMorpherEffectException :
+ public EaxException
+{
+public:
+ explicit EaxVocalMorpherEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_VOCAL_MORPHER_EFFECT", message}
+ {
+ }
+}; // EaxVocalMorpherEffectException
+
+
+EaxVocalMorpherEffect::EaxVocalMorpherEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxVocalMorpherEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxVocalMorpherEffect::set_eax_defaults()
+{
+ eax_.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
+ eax_.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
+ eax_.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
+ eax_.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
+ eax_.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
+ eax_.flRate = EAXVOCALMORPHER_DEFAULTRATE;
+
+ eax_d_ = eax_;
+}
+
+void EaxVocalMorpherEffect::set_efx_phoneme_a()
+{
+ const auto phoneme_a = clamp(
+ static_cast<ALint>(eax_.ulPhonemeA),
+ AL_VOCAL_MORPHER_MIN_PHONEMEA,
+ AL_VOCAL_MORPHER_MAX_PHONEMEA);
+
+ const auto efx_phoneme_a = PhenomeFromEnum(phoneme_a);
+ assert(efx_phoneme_a.has_value());
+ al_effect_props_.Vmorpher.PhonemeA = *efx_phoneme_a;
+}
+
+void EaxVocalMorpherEffect::set_efx_phoneme_a_coarse_tuning()
+{
+ const auto phoneme_a_coarse_tuning = clamp(
+ static_cast<ALint>(eax_.lPhonemeACoarseTuning),
+ AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING,
+ AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING);
+
+ al_effect_props_.Vmorpher.PhonemeACoarseTuning = phoneme_a_coarse_tuning;
+}
+
+void EaxVocalMorpherEffect::set_efx_phoneme_b()
+{
+ const auto phoneme_b = clamp(
+ static_cast<ALint>(eax_.ulPhonemeB),
+ AL_VOCAL_MORPHER_MIN_PHONEMEB,
+ AL_VOCAL_MORPHER_MAX_PHONEMEB);
+
+ const auto efx_phoneme_b = PhenomeFromEnum(phoneme_b);
+ assert(efx_phoneme_b.has_value());
+ al_effect_props_.Vmorpher.PhonemeB = *efx_phoneme_b;
+}
+
+void EaxVocalMorpherEffect::set_efx_phoneme_b_coarse_tuning()
+{
+ const auto phoneme_b_coarse_tuning = clamp(
+ static_cast<ALint>(eax_.lPhonemeBCoarseTuning),
+ AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING,
+ AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING);
+
+ al_effect_props_.Vmorpher.PhonemeBCoarseTuning = phoneme_b_coarse_tuning;
+}
+
+void EaxVocalMorpherEffect::set_efx_waveform()
+{
+ const auto waveform = clamp(
+ static_cast<ALint>(eax_.ulWaveform),
+ AL_VOCAL_MORPHER_MIN_WAVEFORM,
+ AL_VOCAL_MORPHER_MAX_WAVEFORM);
+
+ const auto wfx_waveform = WaveformFromEmum(waveform);
+ assert(wfx_waveform.has_value());
+ al_effect_props_.Vmorpher.Waveform = *wfx_waveform;
+}
+
+void EaxVocalMorpherEffect::set_efx_rate()
+{
+ const auto rate = clamp(
+ eax_.flRate,
+ AL_VOCAL_MORPHER_MIN_RATE,
+ AL_VOCAL_MORPHER_MAX_RATE);
+
+ al_effect_props_.Vmorpher.Rate = rate;
+}
+
+void EaxVocalMorpherEffect::set_efx_defaults()
+{
+ set_efx_phoneme_a();
+ set_efx_phoneme_a_coarse_tuning();
+ set_efx_phoneme_b();
+ set_efx_phoneme_b_coarse_tuning();
+ set_efx_waveform();
+ set_efx_rate();
+}
+
+// [[nodiscard]]
+bool EaxVocalMorpherEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXVOCALMORPHER_NONE:
+ break;
+
+ case EAXVOCALMORPHER_ALLPARAMETERS:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEA:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.ulPhonemeA);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.lPhonemeACoarseTuning);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEB:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.ulPhonemeB);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.lPhonemeBCoarseTuning);
+ break;
+
+ case EAXVOCALMORPHER_WAVEFORM:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.ulWaveform);
+ break;
+
+ case EAXVOCALMORPHER_RATE:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.flRate);
+ break;
+
+ default:
+ throw EaxVocalMorpherEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxVocalMorpherEffect::validate_phoneme_a(
+ unsigned long ulPhonemeA)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Phoneme A",
+ ulPhonemeA,
+ EAXVOCALMORPHER_MINPHONEMEA,
+ EAXVOCALMORPHER_MAXPHONEMEA);
+}
+
+void EaxVocalMorpherEffect::validate_phoneme_a_coarse_tuning(
+ long lPhonemeACoarseTuning)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Phoneme A Coarse Tuning",
+ lPhonemeACoarseTuning,
+ EAXVOCALMORPHER_MINPHONEMEACOARSETUNING,
+ EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING);
+}
+
+void EaxVocalMorpherEffect::validate_phoneme_b(
+ unsigned long ulPhonemeB)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Phoneme B",
+ ulPhonemeB,
+ EAXVOCALMORPHER_MINPHONEMEB,
+ EAXVOCALMORPHER_MAXPHONEMEB);
+}
+
+void EaxVocalMorpherEffect::validate_phoneme_b_coarse_tuning(
+ long lPhonemeBCoarseTuning)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Phoneme B Coarse Tuning",
+ lPhonemeBCoarseTuning,
+ EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING,
+ EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING);
+}
+
+void EaxVocalMorpherEffect::validate_waveform(
+ unsigned long ulWaveform)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Waveform",
+ ulWaveform,
+ EAXVOCALMORPHER_MINWAVEFORM,
+ EAXVOCALMORPHER_MAXWAVEFORM);
+}
+
+void EaxVocalMorpherEffect::validate_rate(
+ float flRate)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Rate",
+ flRate,
+ EAXVOCALMORPHER_MINRATE,
+ EAXVOCALMORPHER_MAXRATE);
+}
+
+void EaxVocalMorpherEffect::validate_all(
+ const EAXVOCALMORPHERPROPERTIES& all)
+{
+ validate_phoneme_a(all.ulPhonemeA);
+ validate_phoneme_a_coarse_tuning(all.lPhonemeACoarseTuning);
+ validate_phoneme_b(all.ulPhonemeB);
+ validate_phoneme_b_coarse_tuning(all.lPhonemeBCoarseTuning);
+ validate_waveform(all.ulWaveform);
+ validate_rate(all.flRate);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_a(
+ unsigned long ulPhonemeA)
+{
+ eax_d_.ulPhonemeA = ulPhonemeA;
+ eax_dirty_flags_.ulPhonemeA = (eax_.ulPhonemeA != eax_d_.ulPhonemeA);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_a_coarse_tuning(
+ long lPhonemeACoarseTuning)
+{
+ eax_d_.lPhonemeACoarseTuning = lPhonemeACoarseTuning;
+ eax_dirty_flags_.lPhonemeACoarseTuning = (eax_.lPhonemeACoarseTuning != eax_d_.lPhonemeACoarseTuning);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_b(
+ unsigned long ulPhonemeB)
+{
+ eax_d_.ulPhonemeB = ulPhonemeB;
+ eax_dirty_flags_.ulPhonemeB = (eax_.ulPhonemeB != eax_d_.ulPhonemeB);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_b_coarse_tuning(
+ long lPhonemeBCoarseTuning)
+{
+ eax_d_.lPhonemeBCoarseTuning = lPhonemeBCoarseTuning;
+ eax_dirty_flags_.lPhonemeBCoarseTuning = (eax_.lPhonemeBCoarseTuning != eax_d_.lPhonemeBCoarseTuning);
+}
+
+void EaxVocalMorpherEffect::defer_waveform(
+ unsigned long ulWaveform)
+{
+ eax_d_.ulWaveform = ulWaveform;
+ eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
+}
+
+void EaxVocalMorpherEffect::defer_rate(
+ float flRate)
+{
+ eax_d_.flRate = flRate;
+ eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate);
+}
+
+void EaxVocalMorpherEffect::defer_all(
+ const EAXVOCALMORPHERPROPERTIES& all)
+{
+ defer_phoneme_a(all.ulPhonemeA);
+ defer_phoneme_a_coarse_tuning(all.lPhonemeACoarseTuning);
+ defer_phoneme_b(all.ulPhonemeB);
+ defer_phoneme_b_coarse_tuning(all.lPhonemeBCoarseTuning);
+ defer_waveform(all.ulWaveform);
+ defer_rate(all.flRate);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_a(
+ const EaxEaxCall& eax_call)
+{
+ const auto& phoneme_a = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::ulPhonemeA)
+ >();
+
+ validate_phoneme_a(phoneme_a);
+ defer_phoneme_a(phoneme_a);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_a_coarse_tuning(
+ const EaxEaxCall& eax_call)
+{
+ const auto& phoneme_a_coarse_tuning = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::lPhonemeACoarseTuning)
+ >();
+
+ validate_phoneme_a_coarse_tuning(phoneme_a_coarse_tuning);
+ defer_phoneme_a_coarse_tuning(phoneme_a_coarse_tuning);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_b(
+ const EaxEaxCall& eax_call)
+{
+ const auto& phoneme_b = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::ulPhonemeB)
+ >();
+
+ validate_phoneme_b(phoneme_b);
+ defer_phoneme_b(phoneme_b);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_b_coarse_tuning(
+ const EaxEaxCall& eax_call)
+{
+ const auto& phoneme_b_coarse_tuning = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::lPhonemeBCoarseTuning)
+ >();
+
+ validate_phoneme_b_coarse_tuning(phoneme_b_coarse_tuning);
+ defer_phoneme_b_coarse_tuning(phoneme_b_coarse_tuning);
+}
+
+void EaxVocalMorpherEffect::defer_waveform(
+ const EaxEaxCall& eax_call)
+{
+ const auto& waveform = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::ulWaveform)
+ >();
+
+ validate_waveform(waveform);
+ defer_waveform(waveform);
+}
+
+void EaxVocalMorpherEffect::defer_rate(
+ const EaxEaxCall& eax_call)
+{
+ const auto& rate = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::flRate)
+ >();
+
+ validate_rate(rate);
+ defer_rate(rate);
+}
+
+void EaxVocalMorpherEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const EAXVOCALMORPHERPROPERTIES
+ >();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxVocalMorpherEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxVocalMorpherEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.ulPhonemeA)
+ {
+ set_efx_phoneme_a();
+ }
+
+ if (eax_dirty_flags_.lPhonemeACoarseTuning)
+ {
+ set_efx_phoneme_a_coarse_tuning();
+ }
+
+ if (eax_dirty_flags_.ulPhonemeB)
+ {
+ set_efx_phoneme_b();
+ }
+
+ if (eax_dirty_flags_.lPhonemeBCoarseTuning)
+ {
+ set_efx_phoneme_b_coarse_tuning();
+ }
+
+ if (eax_dirty_flags_.ulWaveform)
+ {
+ set_efx_waveform();
+ }
+
+ if (eax_dirty_flags_.flRate)
+ {
+ set_efx_rate();
+ }
+
+ eax_dirty_flags_ = EaxVocalMorpherEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxVocalMorpherEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXVOCALMORPHER_NONE:
+ break;
+
+ case EAXVOCALMORPHER_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEA:
+ defer_phoneme_a(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
+ defer_phoneme_a_coarse_tuning(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEB:
+ defer_phoneme_b(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
+ defer_phoneme_b_coarse_tuning(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_WAVEFORM:
+ defer_waveform(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_RATE:
+ defer_rate(eax_call);
+ break;
+
+ default:
+ throw EaxVocalMorpherEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_vocal_morpher_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxVocalMorpherEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX