#include "config.h" #include #include #include #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( EffectProps& al_effect_props); // [[nodiscard]] bool dispatch( const EaxEaxCall& eax_call) override; private: EffectProps& al_effect_props_; 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(); // [[nodiscard]] bool 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); // [[nodiscard]] bool apply_deferred(); // [[nodiscard]] bool 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( EffectProps& al_effect_props) : al_effect_props_{al_effect_props} { set_eax_defaults(); set_efx_defaults(); } // [[nodiscard]] bool EaxAutoWahEffect::dispatch( const EaxEaxCall& eax_call) { return 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(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(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(); } bool EaxAutoWahEffect::get( const EaxEaxCall& eax_call) { switch (eax_call.get_property_id()) { case EAXAUTOWAH_NONE: break; case EAXAUTOWAH_ALLPARAMETERS: eax_call.set_value(eax_); break; case EAXAUTOWAH_ATTACKTIME: eax_call.set_value(eax_.flAttackTime); break; case EAXAUTOWAH_RELEASETIME: eax_call.set_value(eax_.flReleaseTime); break; case EAXAUTOWAH_RESONANCE: eax_call.set_value(eax_.lResonance); break; case EAXAUTOWAH_PEAKLEVEL: eax_call.set_value(eax_.lPeakLevel); break; default: throw EaxAutoWahEffectException{"Unsupported property id."}; } return false; } void EaxAutoWahEffect::validate_attack_time( float flAttackTime) { eax_validate_range( "Attack Time", flAttackTime, EAXAUTOWAH_MINATTACKTIME, EAXAUTOWAH_MAXATTACKTIME); } void EaxAutoWahEffect::validate_release_time( float flReleaseTime) { eax_validate_range( "Release Time", flReleaseTime, EAXAUTOWAH_MINRELEASETIME, EAXAUTOWAH_MAXRELEASETIME); } void EaxAutoWahEffect::validate_resonance( long lResonance) { eax_validate_range( "Resonance", lResonance, EAXAUTOWAH_MINRESONANCE, EAXAUTOWAH_MAXRESONANCE); } void EaxAutoWahEffect::validate_peak_level( long lPeakLevel) { eax_validate_range( "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(); 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(); 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(); validate_resonance(resonance); defer_resonance(resonance); } void EaxAutoWahEffect::defer_peak_level( const EaxEaxCall& eax_call) { const auto& peak_level = eax_call.get_value(); 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(); 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; } // [[nodiscard]] bool 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."}; } if (!eax_call.is_deferred()) { return apply_deferred(); } return false; } } // namespace EaxEffectUPtr eax_create_eax_auto_wah_effect( EffectProps& al_effect_props) { return std::make_unique<::EaxAutoWahEffect>(al_effect_props); } #endif // ALSOFT_EAX