mirror of
https://github.com/love2d/megasource.git
synced 2026-08-20 04:30:45 +02:00
Update OpenAL-soft to 1.23.1-bc7cb17.
This commit is contained in:
+819
-1069
File diff suppressed because it is too large
Load Diff
+286
-178
@@ -1,33 +1,41 @@
|
||||
#ifndef AL_AUXEFFECTSLOT_H
|
||||
#define AL_AUXEFFECTSLOT_H
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/device.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "core/effectslot.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <memory>
|
||||
|
||||
#include "eax_eax_call.h"
|
||||
#include "eax_effect.h"
|
||||
#include "eax_fx_slot_index.h"
|
||||
#include "eax/api.h"
|
||||
#include "eax/call.h"
|
||||
#include "eax/effect.h"
|
||||
#include "eax/exception.h"
|
||||
#include "eax/fx_slot_index.h"
|
||||
#include "eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
struct ALbuffer;
|
||||
struct ALeffect;
|
||||
struct WetBuffer;
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
class EaxFxSlotException : public EaxException {
|
||||
public:
|
||||
explicit EaxFxSlotException(const char* message)
|
||||
: EaxException{"EAX_FX_SLOT", message}
|
||||
{}
|
||||
};
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
enum class SlotState : ALenum {
|
||||
Initial = AL_INITIAL,
|
||||
@@ -36,242 +44,342 @@ enum class SlotState : ALenum {
|
||||
};
|
||||
|
||||
struct ALeffectslot {
|
||||
ALuint EffectId{};
|
||||
float Gain{1.0f};
|
||||
bool AuxSendAuto{true};
|
||||
ALeffectslot *Target{nullptr};
|
||||
ALbuffer *Buffer{nullptr};
|
||||
|
||||
struct {
|
||||
struct EffectData {
|
||||
EffectSlotType Type{EffectSlotType::None};
|
||||
EffectProps Props{};
|
||||
|
||||
al::intrusive_ptr<EffectState> State;
|
||||
} Effect;
|
||||
};
|
||||
EffectData Effect;
|
||||
|
||||
bool mPropsDirty{true};
|
||||
|
||||
SlotState mState{SlotState::Initial};
|
||||
|
||||
RefCount ref{0u};
|
||||
std::atomic<ALuint> ref{0u};
|
||||
|
||||
EffectSlot mSlot;
|
||||
EffectSlot *mSlot{nullptr};
|
||||
|
||||
/* Self ID */
|
||||
ALuint id{};
|
||||
|
||||
ALeffectslot();
|
||||
ALeffectslot(ALCcontext *context);
|
||||
ALeffectslot(const ALeffectslot&) = delete;
|
||||
ALeffectslot& operator=(const ALeffectslot&) = delete;
|
||||
~ALeffectslot();
|
||||
|
||||
ALenum initEffect(ALenum effectType, const EffectProps &effectProps, ALCcontext *context);
|
||||
void updateProps(ALCcontext *context);
|
||||
ALenum initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
|
||||
ALCcontext *context);
|
||||
void updateProps(ALCcontext *context) const;
|
||||
|
||||
/* This can be new'd for the context's default effect slot. */
|
||||
DEF_NEWDEL(ALeffectslot)
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
public:
|
||||
void eax_initialize(
|
||||
ALCcontext& al_context,
|
||||
EaxFxSlotIndexValue index);
|
||||
void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index);
|
||||
|
||||
const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept;
|
||||
[[nodiscard]] auto eax_get_index() const noexcept -> EaxFxSlotIndexValue { return eax_fx_slot_index_; }
|
||||
[[nodiscard]] auto eax_get_eax_fx_slot() const noexcept -> const EAX50FXSLOTPROPERTIES&
|
||||
{ return eax_; }
|
||||
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
[[nodiscard]] auto eax_dispatch(const EaxCall& call) -> bool
|
||||
{ return call.is_get() ? eax_get(call) : eax_set(call); }
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_dispatch(const EaxEaxCall& eax_call)
|
||||
{ return eax_call.is_get() ? eax_get(eax_call) : eax_set(eax_call); }
|
||||
|
||||
|
||||
void eax_unlock_legacy() noexcept;
|
||||
|
||||
void eax_commit() { eax_apply_deferred(); }
|
||||
void eax_commit();
|
||||
|
||||
private:
|
||||
static constexpr auto eax_load_effect_dirty_bit = EaxDirtyFlags{1} << 0;
|
||||
static constexpr auto eax_volume_dirty_bit = EaxDirtyFlags{1} << 1;
|
||||
static constexpr auto eax_lock_dirty_bit = EaxDirtyFlags{1} << 2;
|
||||
static constexpr auto eax_flags_dirty_bit = EaxDirtyFlags{1} << 3;
|
||||
static constexpr auto eax_occlusion_dirty_bit = EaxDirtyFlags{1} << 4;
|
||||
static constexpr auto eax_occlusion_lf_ratio_dirty_bit = EaxDirtyFlags{1} << 5;
|
||||
|
||||
using Exception = EaxFxSlotException;
|
||||
|
||||
using Eax4Props = EAX40FXSLOTPROPERTIES;
|
||||
|
||||
struct Eax4State {
|
||||
Eax4Props i; // Immediate.
|
||||
};
|
||||
|
||||
using Eax5Props = EAX50FXSLOTPROPERTIES;
|
||||
|
||||
struct Eax5State {
|
||||
Eax5Props i; // Immediate.
|
||||
};
|
||||
|
||||
struct EaxRangeValidator {
|
||||
template<typename TValue>
|
||||
void operator()(
|
||||
const char* name,
|
||||
const TValue& value,
|
||||
const TValue& min_value,
|
||||
const TValue& max_value) const
|
||||
{
|
||||
eax_validate_range<Exception>(name, value, min_value, max_value);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax4GuidLoadEffectValidator {
|
||||
void operator()(const GUID& guidLoadEffect) const
|
||||
{
|
||||
if (guidLoadEffect != EAX_NULL_GUID &&
|
||||
guidLoadEffect != EAX_REVERB_EFFECT &&
|
||||
guidLoadEffect != EAX_AGCCOMPRESSOR_EFFECT &&
|
||||
guidLoadEffect != EAX_AUTOWAH_EFFECT &&
|
||||
guidLoadEffect != EAX_CHORUS_EFFECT &&
|
||||
guidLoadEffect != EAX_DISTORTION_EFFECT &&
|
||||
guidLoadEffect != EAX_ECHO_EFFECT &&
|
||||
guidLoadEffect != EAX_EQUALIZER_EFFECT &&
|
||||
guidLoadEffect != EAX_FLANGER_EFFECT &&
|
||||
guidLoadEffect != EAX_FREQUENCYSHIFTER_EFFECT &&
|
||||
guidLoadEffect != EAX_VOCALMORPHER_EFFECT &&
|
||||
guidLoadEffect != EAX_PITCHSHIFTER_EFFECT &&
|
||||
guidLoadEffect != EAX_RINGMODULATOR_EFFECT)
|
||||
{
|
||||
eax_fail_unknown_effect_id();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax4VolumeValidator {
|
||||
void operator()(long lVolume) const
|
||||
{
|
||||
EaxRangeValidator{}(
|
||||
"Volume",
|
||||
lVolume,
|
||||
EAXFXSLOT_MINVOLUME,
|
||||
EAXFXSLOT_MAXVOLUME);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax4LockValidator {
|
||||
void operator()(long lLock) const
|
||||
{
|
||||
EaxRangeValidator{}(
|
||||
"Lock",
|
||||
lLock,
|
||||
EAXFXSLOT_MINLOCK,
|
||||
EAXFXSLOT_MAXLOCK);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax4FlagsValidator {
|
||||
void operator()(unsigned long ulFlags) const
|
||||
{
|
||||
EaxRangeValidator{}(
|
||||
"Flags",
|
||||
ulFlags,
|
||||
0UL,
|
||||
~EAX40FXSLOTFLAGS_RESERVED);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax4AllValidator {
|
||||
void operator()(const EAX40FXSLOTPROPERTIES& all) const
|
||||
{
|
||||
Eax4GuidLoadEffectValidator{}(all.guidLoadEffect);
|
||||
Eax4VolumeValidator{}(all.lVolume);
|
||||
Eax4LockValidator{}(all.lLock);
|
||||
Eax4FlagsValidator{}(all.ulFlags);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax5OcclusionValidator {
|
||||
void operator()(long lOcclusion) const
|
||||
{
|
||||
EaxRangeValidator{}(
|
||||
"Occlusion",
|
||||
lOcclusion,
|
||||
EAXFXSLOT_MINOCCLUSION,
|
||||
EAXFXSLOT_MAXOCCLUSION);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax5OcclusionLfRatioValidator {
|
||||
void operator()(float flOcclusionLFRatio) const
|
||||
{
|
||||
EaxRangeValidator{}(
|
||||
"Occlusion LF Ratio",
|
||||
flOcclusionLFRatio,
|
||||
EAXFXSLOT_MINOCCLUSIONLFRATIO,
|
||||
EAXFXSLOT_MAXOCCLUSIONLFRATIO);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax5FlagsValidator {
|
||||
void operator()(unsigned long ulFlags) const
|
||||
{
|
||||
EaxRangeValidator{}(
|
||||
"Flags",
|
||||
ulFlags,
|
||||
0UL,
|
||||
~EAX50FXSLOTFLAGS_RESERVED);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax5AllValidator {
|
||||
void operator()(const EAX50FXSLOTPROPERTIES& all) const
|
||||
{
|
||||
Eax4AllValidator{}(static_cast<const EAX40FXSLOTPROPERTIES&>(all));
|
||||
Eax5OcclusionValidator{}(all.lOcclusion);
|
||||
Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio);
|
||||
}
|
||||
};
|
||||
|
||||
ALCcontext* eax_al_context_{};
|
||||
|
||||
EaxFxSlotIndexValue eax_fx_slot_index_{};
|
||||
|
||||
EAX50FXSLOTPROPERTIES eax_eax_fx_slot_{};
|
||||
|
||||
int eax_version_{}; // Current EAX version.
|
||||
EaxDirtyFlags eax_df_{}; // Dirty flags for the current EAX version.
|
||||
EaxEffectUPtr eax_effect_{};
|
||||
bool eax_is_locked_{};
|
||||
Eax5State eax123_{}; // EAX1/EAX2/EAX3 state.
|
||||
Eax4State eax4_{}; // EAX4 state.
|
||||
Eax5State eax5_{}; // EAX5 state.
|
||||
Eax5Props eax_{}; // Current EAX state.
|
||||
|
||||
[[noreturn]] static void eax_fail(const char* message);
|
||||
[[noreturn]] static void eax_fail_unknown_effect_id();
|
||||
[[noreturn]] static void eax_fail_unknown_property_id();
|
||||
[[noreturn]] static void eax_fail_unknown_version();
|
||||
|
||||
[[noreturn]]
|
||||
static void eax_fail(
|
||||
const char* message);
|
||||
// Gets a new value from EAX call,
|
||||
// validates it,
|
||||
// sets a dirty flag only if the new value differs form the old one,
|
||||
// and assigns the new value.
|
||||
template<typename TValidator, EaxDirtyFlags TDirtyBit, typename TProperties>
|
||||
static void eax_fx_slot_set(const EaxCall& call, TProperties& dst, EaxDirtyFlags& dirty_flags)
|
||||
{
|
||||
const auto& src = call.get_value<Exception, const TProperties>();
|
||||
TValidator{}(src);
|
||||
dirty_flags |= (dst != src ? TDirtyBit : EaxDirtyFlags{});
|
||||
dst = src;
|
||||
}
|
||||
|
||||
// Gets a new value from EAX call,
|
||||
// validates it,
|
||||
// sets a dirty flag without comparing the values,
|
||||
// and assigns the new value.
|
||||
template<typename TValidator, EaxDirtyFlags TDirtyBit, typename TProperties>
|
||||
static void eax_fx_slot_set_dirty(const EaxCall& call, TProperties& dst,
|
||||
EaxDirtyFlags& dirty_flags)
|
||||
{
|
||||
const auto& src = call.get_value<Exception, const TProperties>();
|
||||
TValidator{}(src);
|
||||
dirty_flags |= TDirtyBit;
|
||||
dst = src;
|
||||
}
|
||||
|
||||
GUID eax_get_eax_default_effect_guid() const noexcept;
|
||||
long eax_get_eax_default_lock() const noexcept;
|
||||
[[nodiscard]] constexpr auto eax4_fx_slot_is_legacy() const noexcept -> bool
|
||||
{ return eax_fx_slot_index_ < 2; }
|
||||
|
||||
void eax_set_eax_fx_slot_defaults();
|
||||
void eax4_fx_slot_ensure_unlocked() const;
|
||||
|
||||
void eax_initialize_eax();
|
||||
[[nodiscard]] static auto eax_get_efx_effect_type(const GUID& guid) -> ALenum;
|
||||
[[nodiscard]] auto eax_get_eax_default_effect_guid() const noexcept -> const GUID&;
|
||||
[[nodiscard]] auto eax_get_eax_default_lock() const noexcept -> long;
|
||||
|
||||
void eax_initialize_lock();
|
||||
void eax4_fx_slot_set_defaults(Eax4Props& props) noexcept;
|
||||
void eax5_fx_slot_set_defaults(Eax5Props& props) noexcept;
|
||||
void eax4_fx_slot_set_current_defaults(const Eax4Props& props) noexcept;
|
||||
void eax5_fx_slot_set_current_defaults(const Eax5Props& props) noexcept;
|
||||
void eax_fx_slot_set_current_defaults();
|
||||
void eax_fx_slot_set_defaults();
|
||||
|
||||
static void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props);
|
||||
static void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props);
|
||||
void eax_fx_slot_get(const EaxCall& call) const;
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax_get(const EaxCall& call);
|
||||
|
||||
void eax_initialize_effects();
|
||||
void eax_fx_slot_load_effect(int version, ALenum altype);
|
||||
void eax_fx_slot_set_volume();
|
||||
void eax_fx_slot_set_environment_flag();
|
||||
void eax_fx_slot_set_flags();
|
||||
|
||||
void eax4_fx_slot_set_all(const EaxCall& call);
|
||||
void eax5_fx_slot_set_all(const EaxCall& call);
|
||||
|
||||
void eax_get_fx_slot_all(
|
||||
const EaxEaxCall& eax_call) const;
|
||||
[[nodiscard]] auto eax_fx_slot_should_update_sources() const noexcept -> bool;
|
||||
|
||||
void eax_get_fx_slot(
|
||||
const EaxEaxCall& eax_call) const;
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax4_fx_slot_set(const EaxCall& call);
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax5_fx_slot_set(const EaxCall& call);
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax_fx_slot_set(const EaxCall& call);
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax_set(const EaxCall& call);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_get(
|
||||
const EaxEaxCall& eax_call);
|
||||
template<
|
||||
EaxDirtyFlags TDirtyBit,
|
||||
typename TMemberResult,
|
||||
typename TProps,
|
||||
typename TState>
|
||||
void eax_fx_slot_commit_property(TState& state, EaxDirtyFlags& dst_df,
|
||||
TMemberResult TProps::*member) noexcept
|
||||
{
|
||||
auto& src_i = state.i;
|
||||
auto& dst_i = eax_;
|
||||
|
||||
if((eax_df_ & TDirtyBit) != EaxDirtyFlags{})
|
||||
{
|
||||
dst_df |= TDirtyBit;
|
||||
dst_i.*member = src_i.*member;
|
||||
}
|
||||
}
|
||||
|
||||
void eax_set_fx_slot_effect(
|
||||
ALenum effect_type);
|
||||
|
||||
void eax_set_fx_slot_effect();
|
||||
|
||||
|
||||
void eax_set_efx_effect_slot_gain();
|
||||
|
||||
void eax_set_fx_slot_volume();
|
||||
|
||||
|
||||
void eax_set_effect_slot_send_auto();
|
||||
|
||||
void eax_set_fx_slot_flags();
|
||||
|
||||
|
||||
void eax_ensure_is_unlocked() const;
|
||||
|
||||
|
||||
void eax_validate_fx_slot_effect(
|
||||
const GUID& eax_effect_id);
|
||||
|
||||
void eax_validate_fx_slot_volume(
|
||||
long eax_volume);
|
||||
|
||||
void eax_validate_fx_slot_lock(
|
||||
long eax_lock);
|
||||
|
||||
void eax_validate_fx_slot_flags(
|
||||
unsigned long eax_flags,
|
||||
int eax_version);
|
||||
|
||||
void eax_validate_fx_slot_occlusion(
|
||||
long eax_occlusion);
|
||||
|
||||
void eax_validate_fx_slot_occlusion_lf_ratio(
|
||||
float eax_occlusion_lf_ratio);
|
||||
|
||||
void eax_validate_fx_slot_all(
|
||||
const EAX40FXSLOTPROPERTIES& fx_slot,
|
||||
int eax_version);
|
||||
|
||||
void eax_validate_fx_slot_all(
|
||||
const EAX50FXSLOTPROPERTIES& fx_slot,
|
||||
int eax_version);
|
||||
|
||||
|
||||
void eax_set_fx_slot_effect(
|
||||
const GUID& eax_effect_id);
|
||||
|
||||
void eax_set_fx_slot_volume(
|
||||
long eax_volume);
|
||||
|
||||
void eax_set_fx_slot_lock(
|
||||
long eax_lock);
|
||||
|
||||
void eax_set_fx_slot_flags(
|
||||
unsigned long eax_flags);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_occlusion(
|
||||
long eax_occlusion);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_occlusion_lf_ratio(
|
||||
float eax_occlusion_lf_ratio);
|
||||
|
||||
void eax_set_fx_slot_all(
|
||||
const EAX40FXSLOTPROPERTIES& eax_fx_slot);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_all(
|
||||
const EAX50FXSLOTPROPERTIES& eax_fx_slot);
|
||||
|
||||
|
||||
void eax_set_fx_slot_effect(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_set_fx_slot_volume(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_set_fx_slot_lock(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_set_fx_slot_flags(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_occlusion(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_occlusion_lf_ratio(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
bool eax_set_fx_slot(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_apply_deferred();
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
|
||||
void eax_dispatch_effect(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax4_fx_slot_commit(EaxDirtyFlags& dst_df);
|
||||
void eax5_fx_slot_commit(Eax5State& state, EaxDirtyFlags& dst_df);
|
||||
|
||||
// `alAuxiliaryEffectSloti(effect_slot, AL_EFFECTSLOT_EFFECT, effect)`
|
||||
void eax_set_effect_slot_effect(EaxEffect &effect);
|
||||
void eax_set_efx_slot_effect(EaxEffect &effect);
|
||||
|
||||
// `alAuxiliaryEffectSloti(effect_slot, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, value)`
|
||||
void eax_set_effect_slot_send_auto(bool is_send_auto);
|
||||
void eax_set_efx_slot_send_auto(bool is_send_auto);
|
||||
|
||||
// `alAuxiliaryEffectSlotf(effect_slot, AL_EFFECTSLOT_GAIN, gain)`
|
||||
void eax_set_effect_slot_gain(ALfloat gain);
|
||||
void eax_set_efx_slot_gain(ALfloat gain);
|
||||
|
||||
public:
|
||||
class EaxDeleter {
|
||||
public:
|
||||
void operator()(ALeffectslot *effect_slot);
|
||||
}; // EaxAlEffectSlotDeleter
|
||||
};
|
||||
#endif // ALSOFT_EAX
|
||||
};
|
||||
|
||||
void UpdateAllEffectSlotProps(ALCcontext *context);
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
|
||||
using EaxAlEffectSlotUPtr = std::unique_ptr<ALeffectslot, ALeffectslot::EaxDeleter>;
|
||||
|
||||
|
||||
EaxAlEffectSlotUPtr eax_create_al_effect_slot(
|
||||
ALCcontext& context);
|
||||
|
||||
void eax_delete_al_effect_slot(
|
||||
ALCcontext& context,
|
||||
ALeffectslot& effect_slot);
|
||||
EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context);
|
||||
void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot);
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
struct EffectSlotSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALeffectslot,64>*> EffectSlots{nullptr};
|
||||
|
||||
EffectSlotSubList() noexcept = default;
|
||||
EffectSlotSubList(const EffectSlotSubList&) = delete;
|
||||
EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
|
||||
: FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
|
||||
~EffectSlotSubList();
|
||||
|
||||
EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
|
||||
EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+1142
-1338
File diff suppressed because it is too large
Load Diff
@@ -1,57 +1,37 @@
|
||||
#ifndef AL_BUFFER_H
|
||||
#define AL_BUFFER_H
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/buffer_storage.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "eax_x_ram.h"
|
||||
enum class EaxStorage : uint8_t {
|
||||
Automatic,
|
||||
Accessible,
|
||||
Hardware
|
||||
};
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
/* User formats */
|
||||
enum UserFmtType : unsigned char {
|
||||
UserFmtUByte = FmtUByte,
|
||||
UserFmtShort = FmtShort,
|
||||
UserFmtFloat = FmtFloat,
|
||||
UserFmtMulaw = FmtMulaw,
|
||||
UserFmtAlaw = FmtAlaw,
|
||||
UserFmtDouble = FmtDouble,
|
||||
|
||||
UserFmtIMA4 = 128,
|
||||
UserFmtMSADPCM,
|
||||
};
|
||||
enum UserFmtChannels : unsigned char {
|
||||
UserFmtMono = FmtMono,
|
||||
UserFmtStereo = FmtStereo,
|
||||
UserFmtRear = FmtRear,
|
||||
UserFmtQuad = FmtQuad,
|
||||
UserFmtX51 = FmtX51,
|
||||
UserFmtX61 = FmtX61,
|
||||
UserFmtX71 = FmtX71,
|
||||
UserFmtBFormat2D = FmtBFormat2D,
|
||||
UserFmtBFormat3D = FmtBFormat3D,
|
||||
UserFmtUHJ2 = FmtUHJ2,
|
||||
UserFmtUHJ3 = FmtUHJ3,
|
||||
UserFmtUHJ4 = FmtUHJ4,
|
||||
};
|
||||
|
||||
|
||||
struct ALbuffer : public BufferStorage {
|
||||
ALbitfieldSOFT Access{0u};
|
||||
|
||||
al::vector<al::byte,16> mData;
|
||||
al::vector<std::byte,16> mDataStorage;
|
||||
|
||||
UserFmtType OriginalType{UserFmtShort};
|
||||
ALuint OriginalSize{0};
|
||||
ALuint OriginalAlign{0};
|
||||
|
||||
ALuint UnpackAlign{0};
|
||||
ALuint PackAlign{0};
|
||||
@@ -65,17 +45,34 @@ struct ALbuffer : public BufferStorage {
|
||||
ALuint mLoopEnd{0u};
|
||||
|
||||
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
|
||||
RefCount ref{0u};
|
||||
std::atomic<ALuint> ref{0u};
|
||||
|
||||
/* Self ID */
|
||||
ALuint id{0};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
ALenum eax_x_ram_mode{AL_STORAGE_AUTOMATIC};
|
||||
EaxStorage eax_x_ram_mode{EaxStorage::Automatic};
|
||||
bool eax_x_ram_is_hardware{};
|
||||
#endif // ALSOFT_EAX
|
||||
};
|
||||
|
||||
struct BufferSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALbuffer,64>*> Buffers{nullptr};
|
||||
|
||||
BufferSubList() noexcept = default;
|
||||
BufferSubList(const BufferSubList&) = delete;
|
||||
BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; }
|
||||
~BufferSubList();
|
||||
|
||||
BufferSubList& operator=(const BufferSubList&) = delete;
|
||||
BufferSubList& operator=(BufferSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,620 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "auxeffectslot.h"
|
||||
#include "buffer.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/voice.h"
|
||||
#include "direct_defs.h"
|
||||
#include "effect.h"
|
||||
#include "error.h"
|
||||
#include "filter.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "source.h"
|
||||
|
||||
|
||||
/* Declared here to prevent compilers from thinking it should be inlined, which
|
||||
* GCC warns about increasing code size.
|
||||
*/
|
||||
DebugGroup::~DebugGroup() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
static_assert(DebugSeverityBase+DebugSeverityCount <= 32, "Too many debug bits");
|
||||
|
||||
template<typename T, T ...Vals>
|
||||
constexpr auto make_array_sequence(std::integer_sequence<T, Vals...>)
|
||||
{ return std::array<T,sizeof...(Vals)>{Vals...}; }
|
||||
|
||||
template<typename T, size_t N>
|
||||
constexpr auto make_array_sequence()
|
||||
{ return make_array_sequence(std::make_integer_sequence<T,N>{}); }
|
||||
|
||||
|
||||
constexpr auto GetDebugSource(ALenum source) noexcept -> std::optional<DebugSource>
|
||||
{
|
||||
switch(source)
|
||||
{
|
||||
case AL_DEBUG_SOURCE_API_EXT: return DebugSource::API;
|
||||
case AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT: return DebugSource::System;
|
||||
case AL_DEBUG_SOURCE_THIRD_PARTY_EXT: return DebugSource::ThirdParty;
|
||||
case AL_DEBUG_SOURCE_APPLICATION_EXT: return DebugSource::Application;
|
||||
case AL_DEBUG_SOURCE_OTHER_EXT: return DebugSource::Other;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
constexpr auto GetDebugType(ALenum type) noexcept -> std::optional<DebugType>
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case AL_DEBUG_TYPE_ERROR_EXT: return DebugType::Error;
|
||||
case AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT: return DebugType::DeprecatedBehavior;
|
||||
case AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT: return DebugType::UndefinedBehavior;
|
||||
case AL_DEBUG_TYPE_PORTABILITY_EXT: return DebugType::Portability;
|
||||
case AL_DEBUG_TYPE_PERFORMANCE_EXT: return DebugType::Performance;
|
||||
case AL_DEBUG_TYPE_MARKER_EXT: return DebugType::Marker;
|
||||
case AL_DEBUG_TYPE_PUSH_GROUP_EXT: return DebugType::PushGroup;
|
||||
case AL_DEBUG_TYPE_POP_GROUP_EXT: return DebugType::PopGroup;
|
||||
case AL_DEBUG_TYPE_OTHER_EXT: return DebugType::Other;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
constexpr auto GetDebugSeverity(ALenum severity) noexcept -> std::optional<DebugSeverity>
|
||||
{
|
||||
switch(severity)
|
||||
{
|
||||
case AL_DEBUG_SEVERITY_HIGH_EXT: return DebugSeverity::High;
|
||||
case AL_DEBUG_SEVERITY_MEDIUM_EXT: return DebugSeverity::Medium;
|
||||
case AL_DEBUG_SEVERITY_LOW_EXT: return DebugSeverity::Low;
|
||||
case AL_DEBUG_SEVERITY_NOTIFICATION_EXT: return DebugSeverity::Notification;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
constexpr auto GetDebugSourceEnum(DebugSource source) -> ALenum
|
||||
{
|
||||
switch(source)
|
||||
{
|
||||
case DebugSource::API: return AL_DEBUG_SOURCE_API_EXT;
|
||||
case DebugSource::System: return AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT;
|
||||
case DebugSource::ThirdParty: return AL_DEBUG_SOURCE_THIRD_PARTY_EXT;
|
||||
case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT;
|
||||
case DebugSource::Other: return AL_DEBUG_SOURCE_OTHER_EXT;
|
||||
}
|
||||
throw std::runtime_error{"Unexpected debug source value "+std::to_string(al::to_underlying(source))};
|
||||
}
|
||||
|
||||
constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case DebugType::Error: return AL_DEBUG_TYPE_ERROR_EXT;
|
||||
case DebugType::DeprecatedBehavior: return AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT;
|
||||
case DebugType::UndefinedBehavior: return AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT;
|
||||
case DebugType::Portability: return AL_DEBUG_TYPE_PORTABILITY_EXT;
|
||||
case DebugType::Performance: return AL_DEBUG_TYPE_PERFORMANCE_EXT;
|
||||
case DebugType::Marker: return AL_DEBUG_TYPE_MARKER_EXT;
|
||||
case DebugType::PushGroup: return AL_DEBUG_TYPE_PUSH_GROUP_EXT;
|
||||
case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT;
|
||||
case DebugType::Other: return AL_DEBUG_TYPE_OTHER_EXT;
|
||||
}
|
||||
throw std::runtime_error{"Unexpected debug type value "+std::to_string(al::to_underlying(type))};
|
||||
}
|
||||
|
||||
constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum
|
||||
{
|
||||
switch(severity)
|
||||
{
|
||||
case DebugSeverity::High: return AL_DEBUG_SEVERITY_HIGH_EXT;
|
||||
case DebugSeverity::Medium: return AL_DEBUG_SEVERITY_MEDIUM_EXT;
|
||||
case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT;
|
||||
case DebugSeverity::Notification: return AL_DEBUG_SEVERITY_NOTIFICATION_EXT;
|
||||
}
|
||||
throw std::runtime_error{"Unexpected debug severity value "+std::to_string(al::to_underlying(severity))};
|
||||
}
|
||||
|
||||
|
||||
constexpr auto GetDebugSourceName(DebugSource source) noexcept -> const char*
|
||||
{
|
||||
switch(source)
|
||||
{
|
||||
case DebugSource::API: return "API";
|
||||
case DebugSource::System: return "Audio System";
|
||||
case DebugSource::ThirdParty: return "Third Party";
|
||||
case DebugSource::Application: return "Application";
|
||||
case DebugSource::Other: return "Other";
|
||||
}
|
||||
return "<invalid source>";
|
||||
}
|
||||
|
||||
constexpr auto GetDebugTypeName(DebugType type) noexcept -> const char*
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case DebugType::Error: return "Error";
|
||||
case DebugType::DeprecatedBehavior: return "Deprecated Behavior";
|
||||
case DebugType::UndefinedBehavior: return "Undefined Behavior";
|
||||
case DebugType::Portability: return "Portability";
|
||||
case DebugType::Performance: return "Performance";
|
||||
case DebugType::Marker: return "Marker";
|
||||
case DebugType::PushGroup: return "Push Group";
|
||||
case DebugType::PopGroup: return "Pop Group";
|
||||
case DebugType::Other: return "Other";
|
||||
}
|
||||
return "<invalid type>";
|
||||
}
|
||||
|
||||
constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> const char*
|
||||
{
|
||||
switch(severity)
|
||||
{
|
||||
case DebugSeverity::High: return "High";
|
||||
case DebugSeverity::Medium: return "Medium";
|
||||
case DebugSeverity::Low: return "Low";
|
||||
case DebugSeverity::Notification: return "Notification";
|
||||
}
|
||||
return "<invalid severity>";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void ALCcontext::sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
|
||||
DebugType type, ALuint id, DebugSeverity severity, std::string_view message)
|
||||
{
|
||||
if(!mDebugEnabled.load(std::memory_order_relaxed)) UNLIKELY
|
||||
return;
|
||||
|
||||
if(message.length() >= MaxDebugMessageLength) UNLIKELY
|
||||
{
|
||||
ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(),
|
||||
MaxDebugMessageLength, al::sizei(message), message.data());
|
||||
return;
|
||||
}
|
||||
|
||||
DebugGroup &debug = mDebugGroups.back();
|
||||
|
||||
const uint64_t idfilter{(1_u64 << (DebugSourceBase+al::to_underlying(source)))
|
||||
| (1_u64 << (DebugTypeBase+al::to_underlying(type)))
|
||||
| (uint64_t{id} << 32)};
|
||||
auto iditer = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(), idfilter);
|
||||
if(iditer != debug.mIdFilters.cend() && *iditer == idfilter)
|
||||
return;
|
||||
|
||||
const uint filter{(1u << (DebugSourceBase+al::to_underlying(source)))
|
||||
| (1u << (DebugTypeBase+al::to_underlying(type)))
|
||||
| (1u << (DebugSeverityBase+al::to_underlying(severity)))};
|
||||
auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
|
||||
if(iter != debug.mFilters.cend() && *iter == filter)
|
||||
return;
|
||||
|
||||
if(mDebugCb)
|
||||
{
|
||||
auto callback = mDebugCb;
|
||||
auto param = mDebugParam;
|
||||
debuglock.unlock();
|
||||
callback(GetDebugSourceEnum(source), GetDebugTypeEnum(type), id,
|
||||
GetDebugSeverityEnum(severity), static_cast<ALsizei>(message.length()), message.data(),
|
||||
param);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mDebugLog.size() < MaxDebugLoggedMessages)
|
||||
mDebugLog.emplace_back(source, type, id, severity, message);
|
||||
else UNLIKELY
|
||||
ERR("Debug message log overflow. Lost message:\n"
|
||||
" Source: %s\n"
|
||||
" Type: %s\n"
|
||||
" ID: %u\n"
|
||||
" Severity: %s\n"
|
||||
" Message: \"%.*s\"\n",
|
||||
GetDebugSourceName(source), GetDebugTypeName(type), id,
|
||||
GetDebugSeverityName(severity), al::sizei(message), message.data());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT,callback, void*,userParam)
|
||||
FORCE_ALIGN void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context,
|
||||
ALDEBUGPROCEXT callback, void *userParam) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
|
||||
context->mDebugCb = callback;
|
||||
context->mDebugParam = userParam;
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum,source, ALenum,type, ALuint,id, ALenum,severity, ALsizei,length, const ALchar*,message)
|
||||
FORCE_ALIGN void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source,
|
||||
ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) noexcept
|
||||
try {
|
||||
if(!context->mContextFlags.test(ContextFlags::DebugBit))
|
||||
return;
|
||||
|
||||
if(!message)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Null message pointer"};
|
||||
|
||||
auto msgview = (length < 0) ? std::string_view{message}
|
||||
: std::string_view{message, static_cast<uint>(length)};
|
||||
if(msgview.size() >= MaxDebugMessageLength)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)",
|
||||
msgview.size(), MaxDebugMessageLength};
|
||||
|
||||
auto dsource = GetDebugSource(source);
|
||||
if(!dsource)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
|
||||
if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
|
||||
|
||||
auto dtype = GetDebugType(type);
|
||||
if(!dtype)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
|
||||
|
||||
auto dseverity = GetDebugSeverity(severity);
|
||||
if(!dseverity)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
|
||||
|
||||
context->debugMessage(*dsource, *dtype, id, *dseverity, msgview);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum,source, ALenum,type, ALenum,severity, ALsizei,count, const ALuint*,ids, ALboolean,enable)
|
||||
FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source,
|
||||
ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) noexcept
|
||||
try {
|
||||
if(count > 0)
|
||||
{
|
||||
if(!ids)
|
||||
throw al::context_error{AL_INVALID_VALUE, "IDs is null with non-0 count"};
|
||||
if(source == AL_DONT_CARE_EXT)
|
||||
throw al::context_error{AL_INVALID_OPERATION,
|
||||
"Debug source cannot be AL_DONT_CARE_EXT with IDs"};
|
||||
if(type == AL_DONT_CARE_EXT)
|
||||
throw al::context_error{AL_INVALID_OPERATION,
|
||||
"Debug type cannot be AL_DONT_CARE_EXT with IDs"};
|
||||
if(severity != AL_DONT_CARE_EXT)
|
||||
throw al::context_error{AL_INVALID_OPERATION,
|
||||
"Debug severity must be AL_DONT_CARE_EXT with IDs"};
|
||||
}
|
||||
|
||||
if(enable != AL_TRUE && enable != AL_FALSE)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug enable %d", enable};
|
||||
|
||||
static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount};
|
||||
static constexpr auto Values = make_array_sequence<uint8_t,ElemCount>();
|
||||
|
||||
auto srcIndices = al::span{Values}.subspan(DebugSourceBase,DebugSourceCount);
|
||||
if(source != AL_DONT_CARE_EXT)
|
||||
{
|
||||
auto dsource = GetDebugSource(source);
|
||||
if(!dsource)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
|
||||
srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1);
|
||||
}
|
||||
|
||||
auto typeIndices = al::span{Values}.subspan(DebugTypeBase,DebugTypeCount);
|
||||
if(type != AL_DONT_CARE_EXT)
|
||||
{
|
||||
auto dtype = GetDebugType(type);
|
||||
if(!dtype)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
|
||||
typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1);
|
||||
}
|
||||
|
||||
auto svrIndices = al::span{Values}.subspan(DebugSeverityBase,DebugSeverityCount);
|
||||
if(severity != AL_DONT_CARE_EXT)
|
||||
{
|
||||
auto dseverity = GetDebugSeverity(severity);
|
||||
if(!dseverity)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
|
||||
svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
|
||||
DebugGroup &debug = context->mDebugGroups.back();
|
||||
if(count > 0)
|
||||
{
|
||||
const uint filterbase{(1u<<srcIndices[0]) | (1u<<typeIndices[0])};
|
||||
|
||||
for(const uint id : al::span{ids, static_cast<uint>(count)})
|
||||
{
|
||||
const uint64_t filter{filterbase | (uint64_t{id} << 32)};
|
||||
|
||||
auto iter = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(),
|
||||
filter);
|
||||
if(!enable && (iter == debug.mIdFilters.cend() || *iter != filter))
|
||||
debug.mIdFilters.insert(iter, filter);
|
||||
else if(enable && iter != debug.mIdFilters.cend() && *iter == filter)
|
||||
debug.mIdFilters.erase(iter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto apply_filter = [enable,&debug](const uint filter)
|
||||
{
|
||||
auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
|
||||
if(!enable && (iter == debug.mFilters.cend() || *iter != filter))
|
||||
debug.mFilters.insert(iter, filter);
|
||||
else if(enable && iter != debug.mFilters.cend() && *iter == filter)
|
||||
debug.mFilters.erase(iter);
|
||||
};
|
||||
auto apply_severity = [apply_filter,svrIndices](const uint filter)
|
||||
{
|
||||
std::for_each(svrIndices.cbegin(), svrIndices.cend(),
|
||||
[apply_filter,filter](const uint idx){ apply_filter(filter | (1<<idx)); });
|
||||
};
|
||||
auto apply_type = [apply_severity,typeIndices](const uint filter)
|
||||
{
|
||||
std::for_each(typeIndices.cbegin(), typeIndices.cend(),
|
||||
[apply_severity,filter](const uint idx){ apply_severity(filter | (1<<idx)); });
|
||||
};
|
||||
std::for_each(srcIndices.cbegin(), srcIndices.cend(),
|
||||
[apply_type](const uint idx){ apply_type(1<<idx); });
|
||||
}
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT4(void, alPushDebugGroup,EXT, ALenum,source, ALuint,id, ALsizei,length, const ALchar*,message)
|
||||
FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source,
|
||||
ALuint id, ALsizei length, const ALchar *message) noexcept
|
||||
try {
|
||||
if(length < 0)
|
||||
{
|
||||
size_t newlen{std::strlen(message)};
|
||||
if(newlen >= MaxDebugMessageLength)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", newlen,
|
||||
MaxDebugMessageLength};
|
||||
length = static_cast<ALsizei>(newlen);
|
||||
}
|
||||
else if(length >= MaxDebugMessageLength)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%d >= %d)", length,
|
||||
MaxDebugMessageLength};
|
||||
|
||||
auto dsource = GetDebugSource(source);
|
||||
if(!dsource)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
|
||||
if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
|
||||
|
||||
std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
|
||||
if(context->mDebugGroups.size() >= MaxDebugGroupDepth)
|
||||
throw al::context_error{AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"};
|
||||
|
||||
context->mDebugGroups.emplace_back(*dsource, id,
|
||||
std::string_view{message, static_cast<uint>(length)});
|
||||
auto &oldback = *(context->mDebugGroups.end()-2);
|
||||
auto &newback = context->mDebugGroups.back();
|
||||
|
||||
newback.mFilters = oldback.mFilters;
|
||||
newback.mIdFilters = oldback.mIdFilters;
|
||||
|
||||
if(context->mContextFlags.test(ContextFlags::DebugBit))
|
||||
context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId,
|
||||
DebugSeverity::Notification, newback.mMessage);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT)
|
||||
FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexcept
|
||||
try {
|
||||
std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
|
||||
if(context->mDebugGroups.size() <= 1)
|
||||
throw al::context_error{AL_STACK_UNDERFLOW_EXT,
|
||||
"Attempting to pop the default debug group"};
|
||||
|
||||
DebugGroup &debug = context->mDebugGroups.back();
|
||||
const auto source = debug.mSource;
|
||||
const auto id = debug.mId;
|
||||
std::string message{std::move(debug.mMessage)};
|
||||
|
||||
context->mDebugGroups.pop_back();
|
||||
if(context->mContextFlags.test(ContextFlags::DebugBit))
|
||||
context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id,
|
||||
DebugSeverity::Notification, message);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint,count, ALsizei,logBufSize, ALenum*,sources, ALenum*,types, ALuint*,ids, ALenum*,severities, ALsizei*,lengths, ALchar*,logBuf)
|
||||
FORCE_ALIGN ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count,
|
||||
ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities,
|
||||
ALsizei *lengths, ALchar *logBuf) noexcept
|
||||
try {
|
||||
if(logBufSize < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Negative debug log buffer size"};
|
||||
|
||||
auto sourcesOut = al::span{sources, sources ? count : 0u};
|
||||
auto typesOut = al::span{types, types ? count : 0u};
|
||||
auto idsOut = al::span{ids, ids ? count : 0u};
|
||||
auto severitiesOut = al::span{severities, severities ? count : 0u};
|
||||
auto lengthsOut = al::span{lengths, lengths ? count : 0u};
|
||||
auto logOut = al::span{logBuf, logBuf ? static_cast<ALuint>(logBufSize) : 0u};
|
||||
|
||||
std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
|
||||
for(ALuint i{0};i < count;++i)
|
||||
{
|
||||
if(context->mDebugLog.empty())
|
||||
return i;
|
||||
|
||||
auto &entry = context->mDebugLog.front();
|
||||
const size_t tocopy{entry.mMessage.size() + 1};
|
||||
if(logOut.data() != nullptr)
|
||||
{
|
||||
if(logOut.size() < tocopy)
|
||||
return i;
|
||||
auto oiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logOut.begin());
|
||||
*oiter = '\0';
|
||||
logOut = {oiter+1, logOut.end()};
|
||||
}
|
||||
|
||||
if(!sourcesOut.empty())
|
||||
{
|
||||
sourcesOut.front() = GetDebugSourceEnum(entry.mSource);
|
||||
sourcesOut = sourcesOut.subspan<1>();
|
||||
}
|
||||
if(!typesOut.empty())
|
||||
{
|
||||
typesOut.front() = GetDebugTypeEnum(entry.mType);
|
||||
typesOut = typesOut.subspan<1>();
|
||||
}
|
||||
if(!idsOut.empty())
|
||||
{
|
||||
idsOut.front() = entry.mId;
|
||||
idsOut = idsOut.subspan<1>();
|
||||
}
|
||||
if(!severitiesOut.empty())
|
||||
{
|
||||
severitiesOut.front() = GetDebugSeverityEnum(entry.mSeverity);
|
||||
severitiesOut = severitiesOut.subspan<1>();
|
||||
}
|
||||
if(!lengthsOut.empty())
|
||||
{
|
||||
lengthsOut.front() = static_cast<ALsizei>(tocopy);
|
||||
lengthsOut = lengthsOut.subspan<1>();
|
||||
}
|
||||
|
||||
context->mDebugLog.pop_front();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
return 0;
|
||||
}
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,length, const ALchar*,label)
|
||||
FORCE_ALIGN void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
|
||||
ALuint name, ALsizei length, const ALchar *label) noexcept
|
||||
try {
|
||||
if(!label && length != 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Null label pointer"};
|
||||
|
||||
auto objname = (length < 0) ? std::string_view{label}
|
||||
: std::string_view{label, static_cast<uint>(length)};
|
||||
if(objname.size() >= MaxObjectLabelLength)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Object label length too long (%zu >= %d)",
|
||||
objname.size(), MaxObjectLabelLength};
|
||||
|
||||
if(identifier == AL_SOURCE_EXT)
|
||||
return ALsource::SetName(context, name, objname);
|
||||
if(identifier == AL_BUFFER)
|
||||
return ALbuffer::SetName(context, name, objname);
|
||||
if(identifier == AL_FILTER_EXT)
|
||||
return ALfilter::SetName(context, name, objname);
|
||||
if(identifier == AL_EFFECT_EXT)
|
||||
return ALeffect::SetName(context, name, objname);
|
||||
if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
|
||||
return ALeffectslot::SetName(context, name, objname);
|
||||
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,bufSize, ALsizei*,length, ALchar*,label)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
|
||||
ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept
|
||||
try {
|
||||
if(bufSize < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Negative label bufSize"};
|
||||
|
||||
if(!label && !length)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Null length and label"};
|
||||
if(label && bufSize == 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Zero label bufSize"};
|
||||
|
||||
const auto labelOut = al::span{label, label ? static_cast<ALuint>(bufSize) : 0u};
|
||||
auto copy_name = [name,length,labelOut](std::unordered_map<ALuint,std::string> &names)
|
||||
{
|
||||
std::string_view objname;
|
||||
|
||||
auto iter = names.find(name);
|
||||
if(iter != names.end())
|
||||
objname = iter->second;
|
||||
|
||||
if(labelOut.empty())
|
||||
*length = static_cast<ALsizei>(objname.size());
|
||||
else
|
||||
{
|
||||
const size_t tocopy{std::min(objname.size(), labelOut.size()-1)};
|
||||
auto oiter = std::copy_n(objname.cbegin(), tocopy, labelOut.begin());
|
||||
*oiter = '\0';
|
||||
if(length)
|
||||
*length = static_cast<ALsizei>(tocopy);
|
||||
}
|
||||
};
|
||||
|
||||
if(identifier == AL_SOURCE_EXT)
|
||||
{
|
||||
std::lock_guard srclock{context->mSourceLock};
|
||||
copy_name(context->mSourceNames);
|
||||
}
|
||||
else if(identifier == AL_BUFFER)
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard buflock{device->BufferLock};
|
||||
copy_name(device->mBufferNames);
|
||||
}
|
||||
else if(identifier == AL_FILTER_EXT)
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard filterlock{device->FilterLock};
|
||||
copy_name(device->mFilterNames);
|
||||
}
|
||||
else if(identifier == AL_EFFECT_EXT)
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard effectlock{device->EffectLock};
|
||||
copy_name(device->mEffectNames);
|
||||
}
|
||||
else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
|
||||
{
|
||||
std::lock_guard slotlock{context->mEffectSlotLock};
|
||||
copy_name(context->mEffectSlotNames);
|
||||
}
|
||||
else
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef AL_DEBUG_H
|
||||
#define AL_DEBUG_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
|
||||
/* Somewhat arbitrary. Avoid letting it get out of control if the app enables
|
||||
* logging but never reads it.
|
||||
*/
|
||||
inline constexpr std::uint8_t MaxDebugLoggedMessages{64};
|
||||
inline constexpr std::uint16_t MaxDebugMessageLength{1024};
|
||||
inline constexpr std::uint8_t MaxDebugGroupDepth{64};
|
||||
inline constexpr std::uint16_t MaxObjectLabelLength{1024};
|
||||
|
||||
|
||||
inline constexpr uint DebugSourceBase{0};
|
||||
enum class DebugSource : std::uint8_t {
|
||||
API = 0,
|
||||
System,
|
||||
ThirdParty,
|
||||
Application,
|
||||
Other,
|
||||
};
|
||||
inline constexpr uint DebugSourceCount{5};
|
||||
|
||||
inline constexpr uint DebugTypeBase{DebugSourceBase + DebugSourceCount};
|
||||
enum class DebugType : std::uint8_t {
|
||||
Error = 0,
|
||||
DeprecatedBehavior,
|
||||
UndefinedBehavior,
|
||||
Portability,
|
||||
Performance,
|
||||
Marker,
|
||||
PushGroup,
|
||||
PopGroup,
|
||||
Other,
|
||||
};
|
||||
inline constexpr uint DebugTypeCount{9};
|
||||
|
||||
inline constexpr uint DebugSeverityBase{DebugTypeBase + DebugTypeCount};
|
||||
enum class DebugSeverity : std::uint8_t {
|
||||
High = 0,
|
||||
Medium,
|
||||
Low,
|
||||
Notification,
|
||||
};
|
||||
inline constexpr uint DebugSeverityCount{4};
|
||||
|
||||
struct DebugGroup {
|
||||
const uint mId;
|
||||
const DebugSource mSource;
|
||||
std::string mMessage;
|
||||
std::vector<uint> mFilters;
|
||||
std::vector<std::uint64_t> mIdFilters;
|
||||
|
||||
template<typename T>
|
||||
DebugGroup(DebugSource source, uint id, T&& message)
|
||||
: mId{id}, mSource{source}, mMessage{std::forward<T>(message)}
|
||||
{ }
|
||||
DebugGroup(const DebugGroup&) = default;
|
||||
DebugGroup(DebugGroup&&) = default;
|
||||
~DebugGroup();
|
||||
};
|
||||
|
||||
#endif /* AL_DEBUG_H */
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifndef AL_DIRECT_DEFS_H
|
||||
#define AL_DIRECT_DEFS_H
|
||||
|
||||
namespace detail_ {
|
||||
|
||||
template<typename T>
|
||||
constexpr T DefaultVal() noexcept { return T{}; }
|
||||
|
||||
template<>
|
||||
constexpr void DefaultVal() noexcept { }
|
||||
|
||||
} // namespace detail_
|
||||
|
||||
#define DECL_FUNC(R, Name) \
|
||||
auto AL_APIENTRY Name() noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get()); \
|
||||
}
|
||||
|
||||
#define DECL_FUNC1(R, Name, T1,n1) \
|
||||
auto AL_APIENTRY Name(T1 n1) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get(), n1); \
|
||||
}
|
||||
|
||||
#define DECL_FUNC2(R, Name, T1,n1, T2,n2) \
|
||||
auto AL_APIENTRY Name(T1 n1, T2 n2) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get(), n1, n2); \
|
||||
}
|
||||
|
||||
#define DECL_FUNC3(R, Name, T1,n1, T2,n2, T3,n3) \
|
||||
auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get(), n1, n2, n3); \
|
||||
}
|
||||
|
||||
#define DECL_FUNC4(R, Name, T1,n1, T2,n2, T3,n3, T4,n4) \
|
||||
auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get(), n1, n2, n3, n4); \
|
||||
}
|
||||
|
||||
#define DECL_FUNC5(R, Name, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5) \
|
||||
auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get(), n1, n2, n3, n4, n5); \
|
||||
}
|
||||
|
||||
|
||||
#define DECL_FUNCEXT(R, Name,Ext) \
|
||||
auto AL_APIENTRY Name##Ext() noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get()); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT1(R, Name,Ext, T1,n1) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT2(R, Name,Ext, T1,n1, T2,n2) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT3(R, Name,Ext, T1,n1, T2,n2, T3,n3) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2, n3); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT4(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2, n3, n4); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT5(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT6(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT8(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6, T7,n7, T8,n8) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6, T7 n7, T8 n8) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6, n7, n8); \
|
||||
}
|
||||
|
||||
#endif /* AL_DIRECT_DEFS_H */
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "al/eax_api.h"
|
||||
#include "api.h"
|
||||
|
||||
|
||||
const GUID DSPROPSETID_EAX_ReverbProperties =
|
||||
@@ -269,74 +269,15 @@ const GUID EAX_RINGMODULATOR_EFFECT =
|
||||
};
|
||||
|
||||
|
||||
bool operator==(
|
||||
const EAX40CONTEXTPROPERTIES& lhs,
|
||||
const EAX40CONTEXTPROPERTIES& rhs) noexcept
|
||||
{
|
||||
return
|
||||
lhs.guidPrimaryFXSlotID == rhs.guidPrimaryFXSlotID &&
|
||||
lhs.flDistanceFactor == rhs.flDistanceFactor &&
|
||||
lhs.flAirAbsorptionHF == rhs.flAirAbsorptionHF &&
|
||||
lhs.flHFReference == rhs.flHFReference;
|
||||
}
|
||||
const GUID EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID = EAXPROPERTYID_EAX40_FXSlot0;
|
||||
const GUID EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID = EAXPROPERTYID_EAX50_FXSlot0;
|
||||
|
||||
bool operator==(
|
||||
const EAX50CONTEXTPROPERTIES& lhs,
|
||||
const EAX50CONTEXTPROPERTIES& rhs) noexcept
|
||||
{
|
||||
return
|
||||
static_cast<const EAX40CONTEXTPROPERTIES&>(lhs) == static_cast<const EAX40CONTEXTPROPERTIES&>(rhs) &&
|
||||
lhs.flMacroFXFactor == rhs.flMacroFXFactor;
|
||||
}
|
||||
|
||||
|
||||
const GUID EAXCONTEXT_DEFAULTPRIMARYFXSLOTID = EAXPROPERTYID_EAX40_FXSlot0;
|
||||
|
||||
bool operator==(
|
||||
const EAX40FXSLOTPROPERTIES& lhs,
|
||||
const EAX40FXSLOTPROPERTIES& rhs) noexcept
|
||||
{
|
||||
return
|
||||
lhs.guidLoadEffect == rhs.guidLoadEffect &&
|
||||
lhs.lVolume == rhs.lVolume &&
|
||||
lhs.lLock == rhs.lLock &&
|
||||
lhs.ulFlags == rhs.ulFlags;
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
const EAX50FXSLOTPROPERTIES& lhs,
|
||||
const EAX50FXSLOTPROPERTIES& rhs) noexcept
|
||||
{
|
||||
return
|
||||
static_cast<const EAX40FXSLOTPROPERTIES&>(lhs) == static_cast<const EAX40FXSLOTPROPERTIES&>(rhs) &&
|
||||
lhs.lOcclusion == rhs.lOcclusion &&
|
||||
lhs.flOcclusionLFRatio == rhs.flOcclusionLFRatio;
|
||||
}
|
||||
|
||||
const EAX50ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS
|
||||
const EAX40ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID = EAX40ACTIVEFXSLOTS
|
||||
{{
|
||||
EAX_NULL_GUID,
|
||||
EAXPROPERTYID_EAX40_FXSlot0,
|
||||
}};
|
||||
|
||||
bool operator==(
|
||||
const EAX50ACTIVEFXSLOTS& lhs,
|
||||
const EAX50ACTIVEFXSLOTS& rhs) noexcept
|
||||
{
|
||||
return std::equal(
|
||||
std::cbegin(lhs.guidActiveFXSlots),
|
||||
std::cend(lhs.guidActiveFXSlots),
|
||||
std::begin(rhs.guidActiveFXSlots));
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
const EAX50ACTIVEFXSLOTS& lhs,
|
||||
const EAX50ACTIVEFXSLOTS& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOTS
|
||||
{{
|
||||
EAX_NULL_GUID,
|
||||
@@ -354,44 +295,569 @@ const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID = EAX50ACTIVEFXSLOT
|
||||
EAX_NULL_GUID,
|
||||
}};
|
||||
|
||||
bool operator==(
|
||||
const EAXREVERBPROPERTIES& lhs,
|
||||
const EAXREVERBPROPERTIES& rhs) noexcept
|
||||
{
|
||||
return
|
||||
lhs.ulEnvironment == rhs.ulEnvironment &&
|
||||
lhs.flEnvironmentSize == rhs.flEnvironmentSize &&
|
||||
lhs.flEnvironmentDiffusion == rhs.flEnvironmentDiffusion &&
|
||||
lhs.lRoom == rhs.lRoom &&
|
||||
lhs.lRoomHF == rhs.lRoomHF &&
|
||||
lhs.lRoomLF == rhs.lRoomLF &&
|
||||
lhs.flDecayTime == rhs.flDecayTime &&
|
||||
lhs.flDecayHFRatio == rhs.flDecayHFRatio &&
|
||||
lhs.flDecayLFRatio == rhs.flDecayLFRatio &&
|
||||
lhs.lReflections == rhs.lReflections &&
|
||||
lhs.flReflectionsDelay == rhs.flReflectionsDelay &&
|
||||
lhs.vReflectionsPan == rhs.vReflectionsPan &&
|
||||
lhs.lReverb == rhs.lReverb &&
|
||||
lhs.flReverbDelay == rhs.flReverbDelay &&
|
||||
lhs.vReverbPan == rhs.vReverbPan &&
|
||||
lhs.flEchoTime == rhs.flEchoTime &&
|
||||
lhs.flEchoDepth == rhs.flEchoDepth &&
|
||||
lhs.flModulationTime == rhs.flModulationTime &&
|
||||
lhs.flModulationDepth == rhs.flModulationDepth &&
|
||||
lhs.flAirAbsorptionHF == rhs.flAirAbsorptionHF &&
|
||||
lhs.flHFReference == rhs.flHFReference &&
|
||||
lhs.flLFReference == rhs.flLFReference &&
|
||||
lhs.flRoomRolloffFactor == rhs.flRoomRolloffFactor &&
|
||||
lhs.ulFlags == rhs.ulFlags;
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
const EAXREVERBPROPERTIES& lhs,
|
||||
const EAXREVERBPROPERTIES& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
// EAX1 =====================================================================
|
||||
|
||||
namespace {
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_GENERIC = {EAX_ENVIRONMENT_GENERIC, 0.5F, 1.493F, 0.5F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PADDEDCELL = {EAX_ENVIRONMENT_PADDEDCELL, 0.25F, 0.1F, 0.0F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ROOM = {EAX_ENVIRONMENT_ROOM, 0.417F, 0.4F, 0.666F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_BATHROOM = {EAX_ENVIRONMENT_BATHROOM, 0.653F, 1.499F, 0.166F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_LIVINGROOM = {EAX_ENVIRONMENT_LIVINGROOM, 0.208F, 0.478F, 0.0F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONEROOM = {EAX_ENVIRONMENT_STONEROOM, 0.5F, 2.309F, 0.888F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_AUDITORIUM = {EAX_ENVIRONMENT_AUDITORIUM, 0.403F, 4.279F, 0.5F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CONCERTHALL = {EAX_ENVIRONMENT_CONCERTHALL, 0.5F, 3.961F, 0.5F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CAVE = {EAX_ENVIRONMENT_CAVE, 0.5F, 2.886F, 1.304F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ARENA = {EAX_ENVIRONMENT_ARENA, 0.361F, 7.284F, 0.332F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HANGAR = {EAX_ENVIRONMENT_HANGAR, 0.5F, 10.0F, 0.3F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CARPETTEDHALLWAY = {EAX_ENVIRONMENT_CARPETEDHALLWAY, 0.153F, 0.259F, 2.0F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HALLWAY = {EAX_ENVIRONMENT_HALLWAY, 0.361F, 1.493F, 0.0F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONECORRIDOR = {EAX_ENVIRONMENT_STONECORRIDOR, 0.444F, 2.697F, 0.638F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ALLEY = {EAX_ENVIRONMENT_ALLEY, 0.25F, 1.752F, 0.776F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_FOREST = {EAX_ENVIRONMENT_FOREST, 0.111F, 3.145F, 0.472F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CITY = {EAX_ENVIRONMENT_CITY, 0.111F, 2.767F, 0.224F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_MOUNTAINS = {EAX_ENVIRONMENT_MOUNTAINS, 0.194F, 7.841F, 0.472F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_QUARRY = {EAX_ENVIRONMENT_QUARRY, 1.0F, 1.499F, 0.5F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PLAIN = {EAX_ENVIRONMENT_PLAIN, 0.097F, 2.767F, 0.224F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PARKINGLOT = {EAX_ENVIRONMENT_PARKINGLOT, 0.208F, 1.652F, 1.5F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_SEWERPIPE = {EAX_ENVIRONMENT_SEWERPIPE, 0.652F, 2.886F, 0.25F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_UNDERWATER = {EAX_ENVIRONMENT_UNDERWATER, 1.0F, 1.499F, 0.0F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DRUGGED = {EAX_ENVIRONMENT_DRUGGED, 0.875F, 8.392F, 1.388F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DIZZY = {EAX_ENVIRONMENT_DIZZY, 0.139F, 17.234F, 0.666F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PSYCHOTIC = {EAX_ENVIRONMENT_PSYCHOTIC, 0.486F, 7.563F, 0.806F};
|
||||
} // namespace
|
||||
|
||||
const Eax1ReverbPresets EAX1REVERB_PRESETS{{
|
||||
EAX1REVERB_PRESET_GENERIC,
|
||||
EAX1REVERB_PRESET_PADDEDCELL,
|
||||
EAX1REVERB_PRESET_ROOM,
|
||||
EAX1REVERB_PRESET_BATHROOM,
|
||||
EAX1REVERB_PRESET_LIVINGROOM,
|
||||
EAX1REVERB_PRESET_STONEROOM,
|
||||
EAX1REVERB_PRESET_AUDITORIUM,
|
||||
EAX1REVERB_PRESET_CONCERTHALL,
|
||||
EAX1REVERB_PRESET_CAVE,
|
||||
EAX1REVERB_PRESET_ARENA,
|
||||
EAX1REVERB_PRESET_HANGAR,
|
||||
EAX1REVERB_PRESET_CARPETTEDHALLWAY,
|
||||
EAX1REVERB_PRESET_HALLWAY,
|
||||
EAX1REVERB_PRESET_STONECORRIDOR,
|
||||
EAX1REVERB_PRESET_ALLEY,
|
||||
EAX1REVERB_PRESET_FOREST,
|
||||
EAX1REVERB_PRESET_CITY,
|
||||
EAX1REVERB_PRESET_MOUNTAINS,
|
||||
EAX1REVERB_PRESET_QUARRY,
|
||||
EAX1REVERB_PRESET_PLAIN,
|
||||
EAX1REVERB_PRESET_PARKINGLOT,
|
||||
EAX1REVERB_PRESET_SEWERPIPE,
|
||||
EAX1REVERB_PRESET_UNDERWATER,
|
||||
EAX1REVERB_PRESET_DRUGGED,
|
||||
EAX1REVERB_PRESET_DIZZY,
|
||||
EAX1REVERB_PRESET_PSYCHOTIC,
|
||||
}};
|
||||
|
||||
// EAX2 =====================================================================
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_GENERIC{
|
||||
EAX2LISTENER_DEFAULTROOM,
|
||||
EAX2LISTENER_DEFAULTROOMHF,
|
||||
EAX2LISTENER_DEFAULTROOMROLLOFFFACTOR,
|
||||
EAX2LISTENER_DEFAULTDECAYTIME,
|
||||
EAX2LISTENER_DEFAULTDECAYHFRATIO,
|
||||
EAX2LISTENER_DEFAULTREFLECTIONS,
|
||||
EAX2LISTENER_DEFAULTREFLECTIONSDELAY,
|
||||
EAX2LISTENER_DEFAULTREVERB,
|
||||
EAX2LISTENER_DEFAULTREVERBDELAY,
|
||||
EAX2LISTENER_DEFAULTENVIRONMENT,
|
||||
EAX2LISTENER_DEFAULTENVIRONMENTSIZE,
|
||||
EAX2LISTENER_DEFAULTENVIRONMENTDIFFUSION,
|
||||
EAX2LISTENER_DEFAULTAIRABSORPTIONHF,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_PADDEDCELL{
|
||||
-1'000L,
|
||||
-6'000L,
|
||||
0.0F,
|
||||
0.17F,
|
||||
0.1F,
|
||||
-1'204L,
|
||||
0.001F,
|
||||
207L,
|
||||
0.002F,
|
||||
EAX2_ENVIRONMENT_PADDEDCELL,
|
||||
1.4F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_ROOM{
|
||||
-1'000L,
|
||||
-454L,
|
||||
0.0F,
|
||||
0.4F,
|
||||
0.83F,
|
||||
-1'646L,
|
||||
0.002F,
|
||||
53L,
|
||||
0.003F,
|
||||
EAX2_ENVIRONMENT_ROOM,
|
||||
1.9F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_BATHROOM{
|
||||
-1'000L,
|
||||
-1'200L,
|
||||
0.0F,
|
||||
1.49F,
|
||||
0.54F,
|
||||
-370L,
|
||||
0.007F,
|
||||
1'030L,
|
||||
0.011F,
|
||||
EAX2_ENVIRONMENT_BATHROOM,
|
||||
1.4F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_LIVINGROOM{
|
||||
-1'000L,
|
||||
-6'000L,
|
||||
0.0F,
|
||||
0.5F,
|
||||
0.1F,
|
||||
-1'376L,
|
||||
0.003F,
|
||||
-1'104L,
|
||||
0.004F,
|
||||
EAX2_ENVIRONMENT_LIVINGROOM,
|
||||
2.5F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_STONEROOM{
|
||||
-1'000L,
|
||||
-300L,
|
||||
0.0F,
|
||||
2.31F,
|
||||
0.64F,
|
||||
-711L,
|
||||
0.012F,
|
||||
83L,
|
||||
0.017F,
|
||||
EAX2_ENVIRONMENT_STONEROOM,
|
||||
11.6F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_AUDITORIUM{
|
||||
-1'000L,
|
||||
-476L,
|
||||
0.0F,
|
||||
4.32F,
|
||||
0.59F,
|
||||
-789L,
|
||||
0.02F,
|
||||
-289L,
|
||||
0.03F,
|
||||
EAX2_ENVIRONMENT_AUDITORIUM,
|
||||
21.6F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_CONCERTHALL{
|
||||
-1'000L,
|
||||
-500L,
|
||||
0.0F,
|
||||
3.92F,
|
||||
0.7F,
|
||||
-1'230L,
|
||||
0.02F,
|
||||
-2L,
|
||||
0.029F,
|
||||
EAX2_ENVIRONMENT_CONCERTHALL,
|
||||
19.6F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_CAVE{
|
||||
-1'000L,
|
||||
0L,
|
||||
0.0F,
|
||||
2.91F,
|
||||
1.3F,
|
||||
-602L,
|
||||
0.015F,
|
||||
-302L,
|
||||
0.022F,
|
||||
EAX2_ENVIRONMENT_CAVE,
|
||||
14.6F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENERFLAGS_DECAYTIMESCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSSCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBDELAYSCALE,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_ARENA{
|
||||
-1'000L,
|
||||
-698L,
|
||||
0.0F,
|
||||
7.24F,
|
||||
0.33F,
|
||||
-1'166L,
|
||||
0.02F,
|
||||
16L,
|
||||
0.03F,
|
||||
EAX2_ENVIRONMENT_ARENA,
|
||||
36.2F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_HANGAR{
|
||||
-1'000L,
|
||||
-1'000L,
|
||||
0.0F,
|
||||
10.05F,
|
||||
0.23F,
|
||||
-602L,
|
||||
0.02F,
|
||||
198L,
|
||||
0.03F,
|
||||
EAX2_ENVIRONMENT_HANGAR,
|
||||
50.3F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_CARPETTEDHALLWAY{
|
||||
-1'000L,
|
||||
-4'000L,
|
||||
0.0F,
|
||||
0.3F,
|
||||
0.1F,
|
||||
-1'831L,
|
||||
0.002F,
|
||||
-1'630L,
|
||||
0.03F,
|
||||
EAX2_ENVIRONMENT_CARPETEDHALLWAY,
|
||||
1.9F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_HALLWAY{
|
||||
-1'000L,
|
||||
-300L,
|
||||
0.0F,
|
||||
1.49F,
|
||||
0.59F,
|
||||
-1'219L,
|
||||
0.007F,
|
||||
441L,
|
||||
0.011F,
|
||||
EAX2_ENVIRONMENT_HALLWAY,
|
||||
1.8F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_STONECORRIDOR{
|
||||
-1'000L,
|
||||
-237L,
|
||||
0.0F,
|
||||
2.7F,
|
||||
0.79F,
|
||||
-1'214L,
|
||||
0.013F,
|
||||
395L,
|
||||
0.02F,
|
||||
EAX2_ENVIRONMENT_STONECORRIDOR,
|
||||
13.5F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_ALLEY{
|
||||
-1'000L,
|
||||
-270L,
|
||||
0.0F,
|
||||
1.49F,
|
||||
0.86F,
|
||||
-1'204L,
|
||||
0.007F,
|
||||
-4L,
|
||||
0.011F,
|
||||
EAX2_ENVIRONMENT_ALLEY,
|
||||
7.5F,
|
||||
0.3F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_FOREST{
|
||||
-1'000L,
|
||||
-3'300L,
|
||||
0.0F,
|
||||
1.49F,
|
||||
0.54F,
|
||||
-2'560L,
|
||||
0.162F,
|
||||
-229L,
|
||||
0.088F,
|
||||
EAX2_ENVIRONMENT_FOREST,
|
||||
38.0F,
|
||||
0.3F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_CITY{
|
||||
-1'000L,
|
||||
-800L,
|
||||
0.0F,
|
||||
1.49F,
|
||||
0.67F,
|
||||
-2'273L,
|
||||
0.007F,
|
||||
-1'691L,
|
||||
0.011F,
|
||||
EAX2_ENVIRONMENT_CITY,
|
||||
7.5F,
|
||||
0.5F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_MOUNTAINS{
|
||||
-1'000L,
|
||||
-2'500L,
|
||||
0.0F,
|
||||
1.49F,
|
||||
0.21F,
|
||||
-2'780L,
|
||||
0.3F,
|
||||
-1'434L,
|
||||
0.1F,
|
||||
EAX2_ENVIRONMENT_MOUNTAINS,
|
||||
100.0F,
|
||||
0.27F,
|
||||
-5.0F,
|
||||
EAX2LISTENERFLAGS_DECAYTIMESCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSSCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBDELAYSCALE,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_QUARRY{
|
||||
-1'000L,
|
||||
-1'000L,
|
||||
0.0F,
|
||||
1.49F,
|
||||
0.83F,
|
||||
-10'000L,
|
||||
0.061F,
|
||||
500L,
|
||||
0.025F,
|
||||
EAX2_ENVIRONMENT_QUARRY,
|
||||
17.5F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_PLAIN{
|
||||
-1'000L,
|
||||
-2'000L,
|
||||
0.0F,
|
||||
1.49F,
|
||||
0.5F,
|
||||
-2'466L,
|
||||
0.179F,
|
||||
-1'926L,
|
||||
0.1F,
|
||||
EAX2_ENVIRONMENT_PLAIN,
|
||||
42.5F,
|
||||
0.21F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_PARKINGLOT{
|
||||
-1'000L,
|
||||
0L,
|
||||
0.0F,
|
||||
1.65F,
|
||||
1.5F,
|
||||
-1'363L,
|
||||
0.008F,
|
||||
-1'153L,
|
||||
0.012F,
|
||||
EAX2_ENVIRONMENT_PARKINGLOT,
|
||||
8.3F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENERFLAGS_DECAYTIMESCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSSCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBDELAYSCALE,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_SEWERPIPE{
|
||||
-1'000L,
|
||||
-1'000L,
|
||||
0.0F,
|
||||
2.81F,
|
||||
0.14F,
|
||||
429L,
|
||||
0.014F,
|
||||
1'023L,
|
||||
0.021F,
|
||||
EAX2_ENVIRONMENT_SEWERPIPE,
|
||||
1.7F,
|
||||
0.8F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_UNDERWATER{
|
||||
-1'000L,
|
||||
-4'000L,
|
||||
0.0F,
|
||||
1.49F,
|
||||
0.1F,
|
||||
-449L,
|
||||
0.007F,
|
||||
1'700L,
|
||||
0.011F,
|
||||
EAX2_ENVIRONMENT_UNDERWATER,
|
||||
1.8F,
|
||||
1.0F,
|
||||
-5.0F,
|
||||
EAX2LISTENER_DEFAULTFLAGS,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_DRUGGED{
|
||||
-1'000L,
|
||||
0L,
|
||||
0.0F,
|
||||
8.39F,
|
||||
1.39F,
|
||||
-115L,
|
||||
0.002F,
|
||||
985L,
|
||||
0.03F,
|
||||
EAX2_ENVIRONMENT_DRUGGED,
|
||||
1.9F,
|
||||
0.5F,
|
||||
-5.0F,
|
||||
EAX2LISTENERFLAGS_DECAYTIMESCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSSCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBDELAYSCALE,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_DIZZY{
|
||||
-1'000L,
|
||||
-400L,
|
||||
0.0F,
|
||||
17.23F,
|
||||
0.56F,
|
||||
-1'713L,
|
||||
0.02F,
|
||||
-613L,
|
||||
0.03F,
|
||||
EAX2_ENVIRONMENT_DIZZY,
|
||||
1.8F,
|
||||
0.6F,
|
||||
-5.0F,
|
||||
EAX2LISTENERFLAGS_DECAYTIMESCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSSCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBDELAYSCALE,
|
||||
};
|
||||
|
||||
constexpr EAX20LISTENERPROPERTIES EAX2REVERB_PRESET_PSYCHOTIC{
|
||||
-1'000L,
|
||||
-151L,
|
||||
0.0F,
|
||||
7.56F,
|
||||
0.91F,
|
||||
-626L,
|
||||
0.02F,
|
||||
774L,
|
||||
0.03F,
|
||||
EAX2_ENVIRONMENT_PSYCHOTIC,
|
||||
1.0F,
|
||||
0.5F,
|
||||
-5.0F,
|
||||
EAX2LISTENERFLAGS_DECAYTIMESCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSSCALE |
|
||||
EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBSCALE |
|
||||
EAX2LISTENERFLAGS_REVERBDELAYSCALE,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
const Eax2ReverbPresets EAX2REVERB_PRESETS{
|
||||
EAX2REVERB_PRESET_GENERIC,
|
||||
EAX2REVERB_PRESET_PADDEDCELL,
|
||||
EAX2REVERB_PRESET_ROOM,
|
||||
EAX2REVERB_PRESET_BATHROOM,
|
||||
EAX2REVERB_PRESET_LIVINGROOM,
|
||||
EAX2REVERB_PRESET_STONEROOM,
|
||||
EAX2REVERB_PRESET_AUDITORIUM,
|
||||
EAX2REVERB_PRESET_CONCERTHALL,
|
||||
EAX2REVERB_PRESET_CAVE,
|
||||
EAX2REVERB_PRESET_ARENA,
|
||||
EAX2REVERB_PRESET_HANGAR,
|
||||
EAX2REVERB_PRESET_CARPETTEDHALLWAY,
|
||||
EAX2REVERB_PRESET_HALLWAY,
|
||||
EAX2REVERB_PRESET_STONECORRIDOR,
|
||||
EAX2REVERB_PRESET_ALLEY,
|
||||
EAX2REVERB_PRESET_FOREST,
|
||||
EAX2REVERB_PRESET_CITY,
|
||||
EAX2REVERB_PRESET_MOUNTAINS,
|
||||
EAX2REVERB_PRESET_QUARRY,
|
||||
EAX2REVERB_PRESET_PLAIN,
|
||||
EAX2REVERB_PRESET_PARKINGLOT,
|
||||
EAX2REVERB_PRESET_SEWERPIPE,
|
||||
EAX2REVERB_PRESET_UNDERWATER,
|
||||
EAX2REVERB_PRESET_DRUGGED,
|
||||
EAX2REVERB_PRESET_DIZZY,
|
||||
EAX2REVERB_PRESET_PSYCHOTIC,
|
||||
};
|
||||
|
||||
// EAX3+ ====================================================================
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -1153,61 +1619,3 @@ const EaxReverbPresets EAXREVERB_PRESETS{{
|
||||
EAXREVERB_PRESET_DIZZY,
|
||||
EAXREVERB_PRESET_PSYCHOTIC,
|
||||
}};
|
||||
|
||||
namespace {
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_GENERIC = {EAX_ENVIRONMENT_GENERIC, 0.5F, 1.493F, 0.5F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PADDEDCELL = {EAX_ENVIRONMENT_PADDEDCELL, 0.25F, 0.1F, 0.0F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ROOM = {EAX_ENVIRONMENT_ROOM, 0.417F, 0.4F, 0.666F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_BATHROOM = {EAX_ENVIRONMENT_BATHROOM, 0.653F, 1.499F, 0.166F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_LIVINGROOM = {EAX_ENVIRONMENT_LIVINGROOM, 0.208F, 0.478F, 0.0F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONEROOM = {EAX_ENVIRONMENT_STONEROOM, 0.5F, 2.309F, 0.888F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_AUDITORIUM = {EAX_ENVIRONMENT_AUDITORIUM, 0.403F, 4.279F, 0.5F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CONCERTHALL = {EAX_ENVIRONMENT_CONCERTHALL, 0.5F, 3.961F, 0.5F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CAVE = {EAX_ENVIRONMENT_CAVE, 0.5F, 2.886F, 1.304F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ARENA = {EAX_ENVIRONMENT_ARENA, 0.361F, 7.284F, 0.332F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HANGAR = {EAX_ENVIRONMENT_HANGAR, 0.5F, 10.0F, 0.3F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CARPETTEDHALLWAY = {EAX_ENVIRONMENT_CARPETEDHALLWAY, 0.153F, 0.259F, 2.0F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_HALLWAY = {EAX_ENVIRONMENT_HALLWAY, 0.361F, 1.493F, 0.0F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_STONECORRIDOR = {EAX_ENVIRONMENT_STONECORRIDOR, 0.444F, 2.697F, 0.638F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_ALLEY = {EAX_ENVIRONMENT_ALLEY, 0.25F, 1.752F, 0.776F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_FOREST = {EAX_ENVIRONMENT_FOREST, 0.111F, 3.145F, 0.472F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_CITY = {EAX_ENVIRONMENT_CITY, 0.111F, 2.767F, 0.224F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_MOUNTAINS = {EAX_ENVIRONMENT_MOUNTAINS, 0.194F, 7.841F, 0.472F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_QUARRY = {EAX_ENVIRONMENT_QUARRY, 1.0F, 1.499F, 0.5F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PLAIN = {EAX_ENVIRONMENT_PLAIN, 0.097F, 2.767F, 0.224F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PARKINGLOT = {EAX_ENVIRONMENT_PARKINGLOT, 0.208F, 1.652F, 1.5F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_SEWERPIPE = {EAX_ENVIRONMENT_SEWERPIPE, 0.652F, 2.886F, 0.25F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_UNDERWATER = {EAX_ENVIRONMENT_UNDERWATER, 1.0F, 1.499F, 0.0F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DRUGGED = {EAX_ENVIRONMENT_DRUGGED, 0.875F, 8.392F, 1.388F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_DIZZY = {EAX_ENVIRONMENT_DIZZY, 0.139F, 17.234F, 0.666F};
|
||||
constexpr EAX_REVERBPROPERTIES EAX1REVERB_PRESET_PSYCHOTIC = {EAX_ENVIRONMENT_PSYCHOTIC, 0.486F, 7.563F, 0.806F};
|
||||
} // namespace
|
||||
|
||||
const Eax1ReverbPresets EAX1REVERB_PRESETS{{
|
||||
EAX1REVERB_PRESET_GENERIC,
|
||||
EAX1REVERB_PRESET_PADDEDCELL,
|
||||
EAX1REVERB_PRESET_ROOM,
|
||||
EAX1REVERB_PRESET_BATHROOM,
|
||||
EAX1REVERB_PRESET_LIVINGROOM,
|
||||
EAX1REVERB_PRESET_STONEROOM,
|
||||
EAX1REVERB_PRESET_AUDITORIUM,
|
||||
EAX1REVERB_PRESET_CONCERTHALL,
|
||||
EAX1REVERB_PRESET_CAVE,
|
||||
EAX1REVERB_PRESET_ARENA,
|
||||
EAX1REVERB_PRESET_HANGAR,
|
||||
EAX1REVERB_PRESET_CARPETTEDHALLWAY,
|
||||
EAX1REVERB_PRESET_HALLWAY,
|
||||
EAX1REVERB_PRESET_STONECORRIDOR,
|
||||
EAX1REVERB_PRESET_ALLEY,
|
||||
EAX1REVERB_PRESET_FOREST,
|
||||
EAX1REVERB_PRESET_CITY,
|
||||
EAX1REVERB_PRESET_MOUNTAINS,
|
||||
EAX1REVERB_PRESET_QUARRY,
|
||||
EAX1REVERB_PRESET_PLAIN,
|
||||
EAX1REVERB_PRESET_PARKINGLOT,
|
||||
EAX1REVERB_PRESET_SEWERPIPE,
|
||||
EAX1REVERB_PRESET_UNDERWATER,
|
||||
EAX1REVERB_PRESET_DRUGGED,
|
||||
EAX1REVERB_PRESET_DIZZY,
|
||||
EAX1REVERB_PRESET_PSYCHOTIC,
|
||||
}};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,218 @@
|
||||
#include "config.h"
|
||||
#include "call.h"
|
||||
#include "exception.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto deferred_flag = 0x80000000U;
|
||||
|
||||
class EaxCallException : public EaxException {
|
||||
public:
|
||||
explicit EaxCallException(const char* message)
|
||||
: EaxException{"EAX_CALL", message}
|
||||
{}
|
||||
}; // EaxCallException
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxCall::EaxCall(
|
||||
EaxCallType type,
|
||||
const GUID& property_set_guid,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size)
|
||||
: mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0}
|
||||
, mPropertyId{property_id & ~deferred_flag}, mPropertySourceId{property_source_id}
|
||||
, mPropertyBuffer{property_buffer}, mPropertyBufferSize{property_size}
|
||||
{
|
||||
switch(mCallType)
|
||||
{
|
||||
case EaxCallType::get:
|
||||
case EaxCallType::set:
|
||||
break;
|
||||
|
||||
default:
|
||||
fail("Invalid type.");
|
||||
}
|
||||
|
||||
if (false)
|
||||
{
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_Context)
|
||||
{
|
||||
mVersion = 4;
|
||||
mPropertySetId = EaxCallPropertySetId::context;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_Context)
|
||||
{
|
||||
mVersion = 5;
|
||||
mPropertySetId = EaxCallPropertySetId::context;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX20_ListenerProperties)
|
||||
{
|
||||
mVersion = 2;
|
||||
mFxSlotIndex = 0u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot_effect;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX30_ListenerProperties)
|
||||
{
|
||||
mVersion = 3;
|
||||
mFxSlotIndex = 0u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot_effect;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot0)
|
||||
{
|
||||
mVersion = 4;
|
||||
mFxSlotIndex = 0u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot0)
|
||||
{
|
||||
mVersion = 5;
|
||||
mFxSlotIndex = 0u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot1)
|
||||
{
|
||||
mVersion = 4;
|
||||
mFxSlotIndex = 1u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot1)
|
||||
{
|
||||
mVersion = 5;
|
||||
mFxSlotIndex = 1u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot2)
|
||||
{
|
||||
mVersion = 4;
|
||||
mFxSlotIndex = 2u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot2)
|
||||
{
|
||||
mVersion = 5;
|
||||
mFxSlotIndex = 2u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot3)
|
||||
{
|
||||
mVersion = 4;
|
||||
mFxSlotIndex = 3u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot3)
|
||||
{
|
||||
mVersion = 5;
|
||||
mFxSlotIndex = 3u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX20_BufferProperties)
|
||||
{
|
||||
mVersion = 2;
|
||||
mPropertySetId = EaxCallPropertySetId::source;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX30_BufferProperties)
|
||||
{
|
||||
mVersion = 3;
|
||||
mPropertySetId = EaxCallPropertySetId::source;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_Source)
|
||||
{
|
||||
mVersion = 4;
|
||||
mPropertySetId = EaxCallPropertySetId::source;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_Source)
|
||||
{
|
||||
mVersion = 5;
|
||||
mPropertySetId = EaxCallPropertySetId::source;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX_ReverbProperties)
|
||||
{
|
||||
mVersion = 1;
|
||||
mFxSlotIndex = 0u;
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot_effect;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAXBUFFER_ReverbProperties)
|
||||
{
|
||||
mVersion = 1;
|
||||
mPropertySetId = EaxCallPropertySetId::source;
|
||||
}
|
||||
else
|
||||
{
|
||||
fail("Unsupported property set id.");
|
||||
}
|
||||
|
||||
switch(mPropertyId)
|
||||
{
|
||||
case EAXCONTEXT_LASTERROR:
|
||||
case EAXCONTEXT_SPEAKERCONFIG:
|
||||
case EAXCONTEXT_EAXSESSION:
|
||||
case EAXFXSLOT_NONE:
|
||||
case EAXFXSLOT_ALLPARAMETERS:
|
||||
case EAXFXSLOT_LOADEFFECT:
|
||||
case EAXFXSLOT_VOLUME:
|
||||
case EAXFXSLOT_LOCK:
|
||||
case EAXFXSLOT_FLAGS:
|
||||
case EAXFXSLOT_OCCLUSION:
|
||||
case EAXFXSLOT_OCCLUSIONLFRATIO:
|
||||
// EAX allow to set "defer" flag on immediate-only properties.
|
||||
// If we don't clear our flag then "applyAllUpdates" in EAX context won't be called.
|
||||
mIsDeferred = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!mIsDeferred)
|
||||
{
|
||||
if(mPropertySetId != EaxCallPropertySetId::fx_slot && mPropertyId != 0)
|
||||
{
|
||||
if(mPropertyBuffer == nullptr)
|
||||
fail("Null property buffer.");
|
||||
|
||||
if(mPropertyBufferSize == 0)
|
||||
fail("Empty property.");
|
||||
}
|
||||
}
|
||||
|
||||
if(mPropertySetId == EaxCallPropertySetId::source && mPropertySourceId == 0)
|
||||
fail("Null AL source id.");
|
||||
|
||||
if(mPropertySetId == EaxCallPropertySetId::fx_slot)
|
||||
{
|
||||
if(mPropertyId < EAXFXSLOT_NONE)
|
||||
mPropertySetId = EaxCallPropertySetId::fx_slot_effect;
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void EaxCall::fail(const char* message)
|
||||
{
|
||||
throw EaxCallException{message};
|
||||
}
|
||||
|
||||
[[noreturn]] void EaxCall::fail_too_small()
|
||||
{
|
||||
fail("Property buffer too small.");
|
||||
}
|
||||
|
||||
EaxCall create_eax_call(
|
||||
EaxCallType type,
|
||||
const GUID* property_set_id,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size)
|
||||
{
|
||||
if(!property_set_id)
|
||||
throw EaxCallException{"Null property set ID."};
|
||||
|
||||
return EaxCall{
|
||||
type,
|
||||
*property_set_id,
|
||||
property_id,
|
||||
property_source_id,
|
||||
property_buffer,
|
||||
property_size
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#ifndef EAX_EAX_CALL_INCLUDED
|
||||
#define EAX_EAX_CALL_INCLUDED
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "api.h"
|
||||
#include "fx_slot_index.h"
|
||||
|
||||
enum class EaxCallType {
|
||||
none,
|
||||
get,
|
||||
set,
|
||||
}; // EaxCallType
|
||||
|
||||
enum class EaxCallPropertySetId {
|
||||
none,
|
||||
context,
|
||||
fx_slot,
|
||||
source,
|
||||
fx_slot_effect,
|
||||
}; // EaxCallPropertySetId
|
||||
|
||||
class EaxCall {
|
||||
public:
|
||||
EaxCall(
|
||||
EaxCallType type,
|
||||
const GUID& property_set_guid,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size);
|
||||
|
||||
[[nodiscard]] auto is_get() const noexcept -> bool { return mCallType == EaxCallType::get; }
|
||||
[[nodiscard]] auto is_deferred() const noexcept -> bool { return mIsDeferred; }
|
||||
[[nodiscard]] auto get_version() const noexcept -> int { return mVersion; }
|
||||
[[nodiscard]] auto get_property_set_id() const noexcept -> EaxCallPropertySetId { return mPropertySetId; }
|
||||
[[nodiscard]] auto get_property_id() const noexcept -> ALuint { return mPropertyId; }
|
||||
[[nodiscard]] auto get_property_al_name() const noexcept -> ALuint { return mPropertySourceId; }
|
||||
[[nodiscard]] auto get_fx_slot_index() const noexcept -> EaxFxSlotIndex { return mFxSlotIndex; }
|
||||
|
||||
template<typename TException, typename TValue>
|
||||
[[nodiscard]] auto get_value() const -> TValue&
|
||||
{
|
||||
if(mPropertyBufferSize < sizeof(TValue))
|
||||
fail_too_small();
|
||||
|
||||
return *static_cast<TValue*>(mPropertyBuffer);
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
[[nodiscard]] auto get_values(size_t max_count) const -> al::span<TValue>
|
||||
{
|
||||
if(max_count == 0 || mPropertyBufferSize < sizeof(TValue))
|
||||
fail_too_small();
|
||||
|
||||
const auto count = std::min(mPropertyBufferSize/sizeof(TValue), max_count);
|
||||
return {static_cast<TValue*>(mPropertyBuffer), count};
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
[[nodiscard]] auto get_values() const -> al::span<TValue>
|
||||
{
|
||||
return get_values<TValue>(~0_uz);
|
||||
}
|
||||
|
||||
template<typename TException, typename TValue>
|
||||
auto set_value(const TValue& value) const -> void
|
||||
{
|
||||
get_value<TException, TValue>() = value;
|
||||
}
|
||||
|
||||
private:
|
||||
const EaxCallType mCallType;
|
||||
int mVersion{};
|
||||
EaxFxSlotIndex mFxSlotIndex{};
|
||||
EaxCallPropertySetId mPropertySetId{EaxCallPropertySetId::none};
|
||||
bool mIsDeferred;
|
||||
|
||||
const ALuint mPropertyId;
|
||||
const ALuint mPropertySourceId;
|
||||
ALvoid*const mPropertyBuffer;
|
||||
const ALuint mPropertyBufferSize;
|
||||
|
||||
[[noreturn]] static void fail(const char* message);
|
||||
[[noreturn]] static void fail_too_small();
|
||||
}; // EaxCall
|
||||
|
||||
EaxCall create_eax_call(
|
||||
EaxCallType type,
|
||||
const GUID* property_set_id,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size);
|
||||
|
||||
#endif // !EAX_EAX_CALL_INCLUDED
|
||||
@@ -0,0 +1,457 @@
|
||||
#ifndef EAX_EFFECT_INCLUDED
|
||||
#define EAX_EFFECT_INCLUDED
|
||||
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alext.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "call.h"
|
||||
|
||||
struct EaxEffectErrorMessages {
|
||||
static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; }
|
||||
static constexpr auto unknown_version() noexcept { return "Unknown version."; }
|
||||
}; // EaxEffectErrorMessages
|
||||
|
||||
using EaxEffectProps = std::variant<std::monostate,
|
||||
EAXREVERBPROPERTIES,
|
||||
EAXCHORUSPROPERTIES,
|
||||
EAXAUTOWAHPROPERTIES,
|
||||
EAXAGCCOMPRESSORPROPERTIES,
|
||||
EAXDISTORTIONPROPERTIES,
|
||||
EAXECHOPROPERTIES,
|
||||
EAXEQUALIZERPROPERTIES,
|
||||
EAXFLANGERPROPERTIES,
|
||||
EAXFREQUENCYSHIFTERPROPERTIES,
|
||||
EAXRINGMODULATORPROPERTIES,
|
||||
EAXPITCHSHIFTERPROPERTIES,
|
||||
EAXVOCALMORPHERPROPERTIES>;
|
||||
|
||||
template<typename... Ts>
|
||||
struct overloaded : Ts... { using Ts::operator()...; };
|
||||
|
||||
template<typename... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props)
|
||||
{
|
||||
return std::visit(overloaded{
|
||||
[](const std::monostate&) noexcept { return AL_EFFECT_NULL; },
|
||||
[](const EAXREVERBPROPERTIES&) noexcept { return AL_EFFECT_EAXREVERB; },
|
||||
[](const EAXCHORUSPROPERTIES&) noexcept { return AL_EFFECT_CHORUS; },
|
||||
[](const EAXAUTOWAHPROPERTIES&) noexcept { return AL_EFFECT_AUTOWAH; },
|
||||
[](const EAXAGCCOMPRESSORPROPERTIES&) noexcept { return AL_EFFECT_COMPRESSOR; },
|
||||
[](const EAXDISTORTIONPROPERTIES&) noexcept { return AL_EFFECT_DISTORTION; },
|
||||
[](const EAXECHOPROPERTIES&) noexcept { return AL_EFFECT_ECHO; },
|
||||
[](const EAXEQUALIZERPROPERTIES&) noexcept { return AL_EFFECT_EQUALIZER; },
|
||||
[](const EAXFLANGERPROPERTIES&) noexcept { return AL_EFFECT_FLANGER; },
|
||||
[](const EAXFREQUENCYSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_FREQUENCY_SHIFTER; },
|
||||
[](const EAXRINGMODULATORPROPERTIES&) noexcept { return AL_EFFECT_RING_MODULATOR; },
|
||||
[](const EAXPITCHSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_PITCH_SHIFTER; },
|
||||
[](const EAXVOCALMORPHERPROPERTIES&) noexcept { return AL_EFFECT_VOCAL_MORPHER; }
|
||||
}, props);
|
||||
}
|
||||
|
||||
struct EaxReverbCommitter {
|
||||
struct Exception;
|
||||
|
||||
EaxReverbCommitter(EaxEffectProps &eaxprops, EffectProps &alprops)
|
||||
: mEaxProps{eaxprops}, mAlProps{alprops}
|
||||
{ }
|
||||
|
||||
EaxEffectProps &mEaxProps;
|
||||
EffectProps &mAlProps;
|
||||
|
||||
[[noreturn]] static void fail(const char* message);
|
||||
[[noreturn]] static void fail_unknown_property_id()
|
||||
{ fail(EaxEffectErrorMessages::unknown_property_id()); }
|
||||
|
||||
template<typename TValidator, typename TProperty>
|
||||
static void defer(const EaxCall& call, TProperty& property)
|
||||
{
|
||||
const auto& value = call.get_value<Exception, const TProperty>();
|
||||
TValidator{}(value);
|
||||
property = value;
|
||||
}
|
||||
|
||||
template<typename TValidator, typename TDeferrer, typename TProperties, typename TProperty>
|
||||
static void defer(const EaxCall& call, TProperties& properties, TProperty&)
|
||||
{
|
||||
const auto& value = call.get_value<Exception, const TProperty>();
|
||||
TValidator{}(value);
|
||||
TDeferrer{}(properties, value);
|
||||
}
|
||||
|
||||
template<typename TValidator, typename TProperty>
|
||||
static void defer3(const EaxCall& call, EAXREVERBPROPERTIES& properties, TProperty& property)
|
||||
{
|
||||
const auto& value = call.get_value<Exception, const TProperty>();
|
||||
TValidator{}(value);
|
||||
if (value == property)
|
||||
return;
|
||||
property = value;
|
||||
properties.ulEnvironment = EAX_ENVIRONMENT_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
bool commit(const EAX_REVERBPROPERTIES &props);
|
||||
bool commit(const EAX20LISTENERPROPERTIES &props);
|
||||
bool commit(const EAXREVERBPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EAX_REVERBPROPERTIES &props);
|
||||
static void SetDefaults(EAX20LISTENERPROPERTIES &props);
|
||||
static void SetDefaults(EAXREVERBPROPERTIES &props);
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
|
||||
static void Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props);
|
||||
static void Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props);
|
||||
static void Get(const EaxCall &call, const EAXREVERBPROPERTIES &props);
|
||||
|
||||
static void Set(const EaxCall &call, EAX_REVERBPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXREVERBPROPERTIES &props);
|
||||
|
||||
static void translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept;
|
||||
static void translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct EaxCommitter {
|
||||
struct Exception;
|
||||
|
||||
EaxCommitter(EaxEffectProps &eaxprops, EffectProps &alprops)
|
||||
: mEaxProps{eaxprops}, mAlProps{alprops}
|
||||
{ }
|
||||
|
||||
EaxEffectProps &mEaxProps;
|
||||
EffectProps &mAlProps;
|
||||
|
||||
template<typename TValidator, typename TProperty>
|
||||
static void defer(const EaxCall& call, TProperty& property)
|
||||
{
|
||||
const auto& value = call.get_value<Exception, const TProperty>();
|
||||
TValidator{}(value);
|
||||
property = value;
|
||||
}
|
||||
|
||||
[[noreturn]] static void fail(const char *message);
|
||||
[[noreturn]] static void fail_unknown_property_id()
|
||||
{ fail(EaxEffectErrorMessages::unknown_property_id()); }
|
||||
};
|
||||
|
||||
struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
|
||||
using EaxCommitter<EaxAutowahCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXAUTOWAHPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props);
|
||||
};
|
||||
struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
|
||||
using EaxCommitter<EaxChorusCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXCHORUSPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props);
|
||||
};
|
||||
struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
|
||||
using EaxCommitter<EaxCompressorCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXAGCCOMPRESSORPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props);
|
||||
};
|
||||
struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
|
||||
using EaxCommitter<EaxDistortionCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXDISTORTIONPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props);
|
||||
};
|
||||
struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
|
||||
using EaxCommitter<EaxEchoCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXECHOPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXECHOPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXECHOPROPERTIES &props);
|
||||
};
|
||||
struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
|
||||
using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXEQUALIZERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props);
|
||||
};
|
||||
struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
|
||||
using EaxCommitter<EaxFlangerCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXFLANGERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props);
|
||||
};
|
||||
struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> {
|
||||
using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||
};
|
||||
struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
|
||||
using EaxCommitter<EaxModulatorCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXRINGMODULATORPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props);
|
||||
};
|
||||
struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> {
|
||||
using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXPITCHSHIFTERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props);
|
||||
};
|
||||
struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> {
|
||||
using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXVOCALMORPHERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props);
|
||||
};
|
||||
struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> {
|
||||
using EaxCommitter<EaxNullCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const std::monostate &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const std::monostate &props);
|
||||
static void Set(const EaxCall &call, std::monostate &props);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct CommitterFromProps { };
|
||||
|
||||
template<> struct CommitterFromProps<std::monostate> { using type = EaxNullCommitter; };
|
||||
template<> struct CommitterFromProps<EAXREVERBPROPERTIES> { using type = EaxReverbCommitter; };
|
||||
template<> struct CommitterFromProps<EAXCHORUSPROPERTIES> { using type = EaxChorusCommitter; };
|
||||
template<> struct CommitterFromProps<EAXAGCCOMPRESSORPROPERTIES> { using type = EaxCompressorCommitter; };
|
||||
template<> struct CommitterFromProps<EAXAUTOWAHPROPERTIES> { using type = EaxAutowahCommitter; };
|
||||
template<> struct CommitterFromProps<EAXDISTORTIONPROPERTIES> { using type = EaxDistortionCommitter; };
|
||||
template<> struct CommitterFromProps<EAXECHOPROPERTIES> { using type = EaxEchoCommitter; };
|
||||
template<> struct CommitterFromProps<EAXEQUALIZERPROPERTIES> { using type = EaxEqualizerCommitter; };
|
||||
template<> struct CommitterFromProps<EAXFLANGERPROPERTIES> { using type = EaxFlangerCommitter; };
|
||||
template<> struct CommitterFromProps<EAXFREQUENCYSHIFTERPROPERTIES> { using type = EaxFrequencyShifterCommitter; };
|
||||
template<> struct CommitterFromProps<EAXRINGMODULATORPROPERTIES> { using type = EaxModulatorCommitter; };
|
||||
template<> struct CommitterFromProps<EAXPITCHSHIFTERPROPERTIES> { using type = EaxPitchShifterCommitter; };
|
||||
template<> struct CommitterFromProps<EAXVOCALMORPHERPROPERTIES> { using type = EaxVocalMorpherCommitter; };
|
||||
|
||||
template<typename T>
|
||||
using CommitterFor = typename CommitterFromProps<std::remove_cv_t<std::remove_reference_t<T>>>::type;
|
||||
|
||||
|
||||
class EaxEffect {
|
||||
public:
|
||||
EaxEffect() noexcept = default;
|
||||
~EaxEffect() = default;
|
||||
|
||||
ALenum al_effect_type_{AL_EFFECT_NULL};
|
||||
EffectProps al_effect_props_{};
|
||||
|
||||
using Props1 = EAX_REVERBPROPERTIES;
|
||||
using Props2 = EAX20LISTENERPROPERTIES;
|
||||
using Props3 = EAXREVERBPROPERTIES;
|
||||
using Props4 = EaxEffectProps;
|
||||
|
||||
struct State1 {
|
||||
Props1 i; // Immediate.
|
||||
Props1 d; // Deferred.
|
||||
};
|
||||
|
||||
struct State2 {
|
||||
Props2 i; // Immediate.
|
||||
Props2 d; // Deferred.
|
||||
};
|
||||
|
||||
struct State3 {
|
||||
Props3 i; // Immediate.
|
||||
Props3 d; // Deferred.
|
||||
};
|
||||
|
||||
struct State4 {
|
||||
Props4 i; // Immediate.
|
||||
Props4 d; // Deferred.
|
||||
};
|
||||
|
||||
int version_{};
|
||||
bool changed_{};
|
||||
Props4 props_{};
|
||||
State1 state1_{};
|
||||
State2 state2_{};
|
||||
State3 state3_{};
|
||||
State4 state4_{};
|
||||
State4 state5_{};
|
||||
|
||||
|
||||
static void call_set_defaults(const ALenum altype, EaxEffectProps &props)
|
||||
{
|
||||
switch(altype)
|
||||
{
|
||||
case AL_EFFECT_EAXREVERB: return EaxReverbCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_CHORUS: return EaxChorusCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_AUTOWAH: return EaxAutowahCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_COMPRESSOR: return EaxCompressorCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_DISTORTION: return EaxDistortionCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_ECHO: return EaxEchoCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_EQUALIZER: return EaxEqualizerCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_FLANGER: return EaxFlangerCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER: return EaxFrequencyShifterCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_RING_MODULATOR: return EaxModulatorCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_PITCH_SHIFTER: return EaxPitchShifterCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_VOCAL_MORPHER: return EaxVocalMorpherCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_NULL: break;
|
||||
}
|
||||
return EaxNullCommitter::SetDefaults(props);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void init()
|
||||
{
|
||||
EaxReverbCommitter::SetDefaults(state1_.d);
|
||||
state1_.i = state1_.d;
|
||||
EaxReverbCommitter::SetDefaults(state2_.d);
|
||||
state2_.i = state2_.d;
|
||||
EaxReverbCommitter::SetDefaults(state3_.d);
|
||||
state3_.i = state3_.d;
|
||||
T::SetDefaults(state4_.d);
|
||||
state4_.i = state4_.d;
|
||||
T::SetDefaults(state5_.d);
|
||||
state5_.i = state5_.d;
|
||||
}
|
||||
|
||||
void set_defaults(int eax_version, ALenum altype)
|
||||
{
|
||||
switch(eax_version)
|
||||
{
|
||||
case 1: EaxReverbCommitter::SetDefaults(state1_.d); break;
|
||||
case 2: EaxReverbCommitter::SetDefaults(state2_.d); break;
|
||||
case 3: EaxReverbCommitter::SetDefaults(state3_.d); break;
|
||||
case 4: call_set_defaults(altype, state4_.d); break;
|
||||
case 5: call_set_defaults(altype, state5_.d); break;
|
||||
}
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
|
||||
static void call_set(const EaxCall &call, EaxEffectProps &props)
|
||||
{
|
||||
return std::visit([&](auto &arg)
|
||||
{ return CommitterFor<decltype(arg)>::Set(call, arg); },
|
||||
props);
|
||||
}
|
||||
|
||||
void set(const EaxCall &call)
|
||||
{
|
||||
switch(call.get_version())
|
||||
{
|
||||
case 1: EaxReverbCommitter::Set(call, state1_.d); break;
|
||||
case 2: EaxReverbCommitter::Set(call, state2_.d); break;
|
||||
case 3: EaxReverbCommitter::Set(call, state3_.d); break;
|
||||
case 4: call_set(call, state4_.d); break;
|
||||
case 5: call_set(call, state5_.d); break;
|
||||
}
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
|
||||
static void call_get(const EaxCall &call, const EaxEffectProps &props)
|
||||
{
|
||||
return std::visit([&](auto &arg)
|
||||
{ return CommitterFor<decltype(arg)>::Get(call, arg); },
|
||||
props);
|
||||
}
|
||||
|
||||
void get(const EaxCall &call) const
|
||||
{
|
||||
switch(call.get_version())
|
||||
{
|
||||
case 1: EaxReverbCommitter::Get(call, state1_.d); break;
|
||||
case 2: EaxReverbCommitter::Get(call, state2_.d); break;
|
||||
case 3: EaxReverbCommitter::Get(call, state3_.d); break;
|
||||
case 4: call_get(call, state4_.d); break;
|
||||
case 5: call_get(call, state5_.d); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool call_commit(const EaxEffectProps &props)
|
||||
{
|
||||
return std::visit([&](auto &arg)
|
||||
{ return CommitterFor<decltype(arg)>{props_, al_effect_props_}.commit(arg); },
|
||||
props);
|
||||
}
|
||||
|
||||
bool commit(int eax_version)
|
||||
{
|
||||
changed_ |= version_ != eax_version;
|
||||
if(!changed_) return false;
|
||||
|
||||
bool ret{version_ != eax_version};
|
||||
version_ = eax_version;
|
||||
changed_ = false;
|
||||
|
||||
switch(eax_version)
|
||||
{
|
||||
case 1:
|
||||
state1_.i = state1_.d;
|
||||
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state1_.d);
|
||||
break;
|
||||
case 2:
|
||||
state2_.i = state2_.d;
|
||||
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state2_.d);
|
||||
break;
|
||||
case 3:
|
||||
state3_.i = state3_.d;
|
||||
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state3_.d);
|
||||
break;
|
||||
case 4:
|
||||
state4_.i = state4_.d;
|
||||
ret |= call_commit(state4_.d);
|
||||
break;
|
||||
case 5:
|
||||
state5_.i = state5_.d;
|
||||
ret |= call_commit(state5_.d);
|
||||
break;
|
||||
}
|
||||
al_effect_type_ = EnumFromEaxEffectType(props_);
|
||||
return ret;
|
||||
}
|
||||
#undef EAXCALL
|
||||
}; // EaxEffect
|
||||
|
||||
using EaxEffectUPtr = std::unique_ptr<EaxEffect>;
|
||||
|
||||
#endif // !EAX_EFFECT_INCLUDED
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "exception.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
|
||||
EaxException::EaxException(std::string_view context, std::string_view message)
|
||||
: std::runtime_error{make_message(context, message)}
|
||||
{
|
||||
}
|
||||
EaxException::~EaxException() = default;
|
||||
|
||||
|
||||
std::string EaxException::make_message(std::string_view context, std::string_view message)
|
||||
{
|
||||
auto what = std::string{};
|
||||
if(context.empty() && message.empty())
|
||||
return what;
|
||||
|
||||
what.reserve((!context.empty() ? context.size() + 3 : 0) + message.length() + 1);
|
||||
if(!context.empty())
|
||||
{
|
||||
what += "[";
|
||||
what += context;
|
||||
what += "] ";
|
||||
}
|
||||
what += message;
|
||||
|
||||
return what;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef EAX_EXCEPTION_INCLUDED
|
||||
#define EAX_EXCEPTION_INCLUDED
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
|
||||
class EaxException : public std::runtime_error {
|
||||
static std::string make_message(std::string_view context, std::string_view message);
|
||||
|
||||
public:
|
||||
EaxException(std::string_view context, std::string_view message);
|
||||
~EaxException() override;
|
||||
}; // EaxException
|
||||
|
||||
|
||||
#endif // !EAX_EXCEPTION_INCLUDED
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "eax_fx_slot_index.h"
|
||||
#include "fx_slot_index.h"
|
||||
|
||||
#include "eax_exception.h"
|
||||
#include "exception.h"
|
||||
|
||||
|
||||
namespace
|
||||
@@ -3,17 +3,16 @@
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
#include "aloptional.h"
|
||||
#include "eax_api.h"
|
||||
#include "api.h"
|
||||
|
||||
|
||||
using EaxFxSlotIndexValue = std::size_t;
|
||||
|
||||
class EaxFxSlotIndex : public al::optional<EaxFxSlotIndexValue>
|
||||
{
|
||||
class EaxFxSlotIndex : public std::optional<EaxFxSlotIndexValue> {
|
||||
public:
|
||||
using al::optional<EaxFxSlotIndexValue>::optional;
|
||||
using std::optional<EaxFxSlotIndexValue>::optional;
|
||||
|
||||
EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; }
|
||||
EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; }
|
||||
@@ -1,12 +1,11 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "eax_fx_slots.h"
|
||||
#include "fx_slots.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "eax_exception.h"
|
||||
|
||||
#include "eax_api.h"
|
||||
#include "api.h"
|
||||
#include "exception.h"
|
||||
|
||||
|
||||
namespace
|
||||
@@ -29,8 +28,7 @@ public:
|
||||
} // namespace
|
||||
|
||||
|
||||
void EaxFxSlots::initialize(
|
||||
ALCcontext& al_context)
|
||||
void EaxFxSlots::initialize(ALCcontext& al_context)
|
||||
{
|
||||
initialize_fx_slots(al_context);
|
||||
}
|
||||
@@ -57,12 +55,6 @@ ALeffectslot& EaxFxSlots::get(EaxFxSlotIndex index)
|
||||
return *fx_slots_[index.value()];
|
||||
}
|
||||
|
||||
void EaxFxSlots::unlock_legacy() noexcept
|
||||
{
|
||||
fx_slots_[0]->eax_unlock_legacy();
|
||||
fx_slots_[1]->eax_unlock_legacy();
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void EaxFxSlots::fail(
|
||||
const char* message)
|
||||
@@ -70,8 +62,7 @@ void EaxFxSlots::fail(
|
||||
throw EaxFxSlotsException{message};
|
||||
}
|
||||
|
||||
void EaxFxSlots::initialize_fx_slots(
|
||||
ALCcontext& al_context)
|
||||
void EaxFxSlots::initialize_fx_slots(ALCcontext& al_context)
|
||||
{
|
||||
auto fx_slot_index = EaxFxSlotIndexValue{};
|
||||
|
||||
@@ -6,16 +6,15 @@
|
||||
|
||||
#include "al/auxeffectslot.h"
|
||||
|
||||
#include "eax_api.h"
|
||||
|
||||
#include "eax_fx_slot_index.h"
|
||||
#include "api.h"
|
||||
#include "call.h"
|
||||
#include "fx_slot_index.h"
|
||||
|
||||
|
||||
class EaxFxSlots
|
||||
{
|
||||
public:
|
||||
void initialize(
|
||||
ALCcontext& al_context);
|
||||
void initialize(ALCcontext& al_context);
|
||||
|
||||
void uninitialize() noexcept;
|
||||
|
||||
@@ -26,14 +25,9 @@ public:
|
||||
}
|
||||
|
||||
|
||||
const ALeffectslot& get(
|
||||
EaxFxSlotIndex index) const;
|
||||
|
||||
ALeffectslot& get(
|
||||
EaxFxSlotIndex index);
|
||||
|
||||
void unlock_legacy() noexcept;
|
||||
[[nodiscard]] auto get(EaxFxSlotIndex index) const -> const ALeffectslot&;
|
||||
|
||||
[[nodiscard]] auto get(EaxFxSlotIndex index) -> ALeffectslot&;
|
||||
|
||||
private:
|
||||
using Items = std::array<EaxAlEffectSlotUPtr, EAX_MAX_FXSLOTS>;
|
||||
@@ -43,11 +37,9 @@ private:
|
||||
|
||||
|
||||
[[noreturn]]
|
||||
static void fail(
|
||||
const char* message);
|
||||
static void fail(const char* message);
|
||||
|
||||
void initialize_fx_slots(
|
||||
ALCcontext& al_context);
|
||||
void initialize_fx_slots(ALCcontext& al_context);
|
||||
}; // EaxFxSlots
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef EAX_GLOBALS_INCLUDED
|
||||
#define EAX_GLOBALS_INCLUDED
|
||||
|
||||
inline bool eax_g_is_enabled{true};
|
||||
|
||||
#endif /* EAX_GLOBALS_INCLUDED */
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
|
||||
#include "alstring.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
|
||||
void eax_log_exception(std::string_view message) noexcept
|
||||
{
|
||||
const auto exception_ptr = std::current_exception();
|
||||
assert(exception_ptr);
|
||||
|
||||
try {
|
||||
std::rethrow_exception(exception_ptr);
|
||||
}
|
||||
catch(const std::exception& ex) {
|
||||
ERR("%.*s %s\n", al::sizei(message), message.data(), ex.what());
|
||||
}
|
||||
catch(...) {
|
||||
ERR("%.*s %s\n", al::sizei(message), message.data(), "Generic exception.");
|
||||
}
|
||||
}
|
||||
@@ -4,34 +4,26 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include "opthelpers.h"
|
||||
|
||||
struct EaxAlLowPassParam
|
||||
{
|
||||
using EaxDirtyFlags = unsigned int;
|
||||
|
||||
struct EaxAlLowPassParam {
|
||||
float gain;
|
||||
float gain_hf;
|
||||
}; // EaxAlLowPassParam
|
||||
};
|
||||
|
||||
void eax_log_exception(std::string_view message) noexcept;
|
||||
|
||||
void eax_log_exception(
|
||||
const char* message = nullptr) noexcept;
|
||||
|
||||
|
||||
template<
|
||||
typename TException,
|
||||
typename TValue
|
||||
>
|
||||
void eax_validate_range(
|
||||
const char* value_name,
|
||||
const TValue& value,
|
||||
const TValue& min_value,
|
||||
template<typename TException, typename TValue>
|
||||
void eax_validate_range(std::string_view value_name, const TValue& value, const TValue& min_value,
|
||||
const TValue& max_value)
|
||||
{
|
||||
if (value >= min_value && value <= max_value)
|
||||
{
|
||||
if(value >= min_value && value <= max_value) LIKELY
|
||||
return;
|
||||
}
|
||||
|
||||
const auto message =
|
||||
std::string{value_name} +
|
||||
@@ -43,60 +35,38 @@ void eax_validate_range(
|
||||
throw TException{message.c_str()};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
|
||||
template<
|
||||
typename T
|
||||
>
|
||||
struct EaxIsBitFieldStruct
|
||||
{
|
||||
template<typename T>
|
||||
struct EaxIsBitFieldStruct {
|
||||
private:
|
||||
using yes = std::true_type;
|
||||
using no = std::false_type;
|
||||
|
||||
template<
|
||||
typename U
|
||||
>
|
||||
template<typename U>
|
||||
static auto test(int) -> decltype(std::declval<typename U::EaxIsBitFieldStruct>(), yes{});
|
||||
|
||||
template<
|
||||
typename
|
||||
>
|
||||
template<typename>
|
||||
static no test(...);
|
||||
|
||||
|
||||
public:
|
||||
static constexpr auto value = std::is_same<decltype(test<T>(0)), yes>::value;
|
||||
}; // EaxIsBitFieldStruct
|
||||
};
|
||||
|
||||
|
||||
template<
|
||||
typename T,
|
||||
typename TValue
|
||||
>
|
||||
inline bool eax_bit_fields_are_equal(
|
||||
const T& lhs,
|
||||
const T& rhs) noexcept
|
||||
template<typename T, typename TValue>
|
||||
inline bool eax_bit_fields_are_equal(const T& lhs, const T& rhs) noexcept
|
||||
{
|
||||
static_assert(sizeof(T) == sizeof(TValue), "Invalid type size.");
|
||||
|
||||
return reinterpret_cast<const TValue&>(lhs) == reinterpret_cast<const TValue&>(rhs);
|
||||
}
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
template<
|
||||
typename T,
|
||||
std::enable_if_t<detail::EaxIsBitFieldStruct<T>::value, int> = 0
|
||||
>
|
||||
inline bool operator==(
|
||||
const T& lhs,
|
||||
const T& rhs) noexcept
|
||||
inline bool operator==(const T& lhs, const T& rhs) noexcept
|
||||
{
|
||||
using Value = std::conditional_t<
|
||||
sizeof(T) == 1,
|
||||
@@ -107,13 +77,9 @@ inline bool operator==(
|
||||
std::conditional_t<
|
||||
sizeof(T) == 4,
|
||||
std::uint32_t,
|
||||
void
|
||||
>
|
||||
>
|
||||
>;
|
||||
void>>>;
|
||||
|
||||
static_assert(!std::is_same<Value, void>::value, "Unsupported type.");
|
||||
|
||||
return detail::eax_bit_fields_are_equal<T, Value>(lhs, rhs);
|
||||
}
|
||||
|
||||
@@ -121,12 +87,9 @@ template<
|
||||
typename T,
|
||||
std::enable_if_t<detail::EaxIsBitFieldStruct<T>::value, int> = 0
|
||||
>
|
||||
inline bool operator!=(
|
||||
const T& lhs,
|
||||
const T& rhs) noexcept
|
||||
inline bool operator!=(const T& lhs, const T& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
#endif // !EAX_UTILS_INCLUDED
|
||||
@@ -24,15 +24,12 @@ constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC";
|
||||
constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE";
|
||||
constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE";
|
||||
|
||||
|
||||
ALboolean AL_APIENTRY EAXSetBufferMode(
|
||||
ALsizei n,
|
||||
const ALuint* buffers,
|
||||
ALint value);
|
||||
|
||||
ALenum AL_APIENTRY EAXGetBufferMode(
|
||||
ALuint buffer,
|
||||
ALint* pReserved);
|
||||
|
||||
/* NOLINTBEGIN(readability-inconsistent-declaration-parameter-name)
|
||||
* These functions are defined using macros to forward them in a generic way to
|
||||
* implementation functions, which gives the parameters generic names.
|
||||
*/
|
||||
ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint *buffers, ALint value) noexcept;
|
||||
ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept;
|
||||
/* NOLINTEND(readability-inconsistent-declaration-parameter-name) */
|
||||
|
||||
#endif // !EAX_X_RAM_INCLUDED
|
||||
@@ -1,324 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "al/eax_eax_call.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto deferred_flag = 0x80000000U;
|
||||
|
||||
class EaxEaxCallException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxEaxCallException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_EAX_CALL", message}
|
||||
{
|
||||
}
|
||||
}; // EaxEaxCallException
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
EaxEaxCall::EaxEaxCall(
|
||||
bool is_get,
|
||||
const GUID& property_set_guid,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size)
|
||||
: is_get_{is_get}, version_{0}, property_set_id_{EaxEaxCallPropertySetId::none}
|
||||
, property_id_{property_id & ~deferred_flag}, property_source_id_{property_source_id}
|
||||
, property_buffer_{property_buffer}, property_size_{property_size}
|
||||
{
|
||||
if (false)
|
||||
{
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_Context)
|
||||
{
|
||||
version_ = 4;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::context;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_Context)
|
||||
{
|
||||
version_ = 5;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::context;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX20_ListenerProperties)
|
||||
{
|
||||
version_ = 2;
|
||||
fx_slot_index_ = 0u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect;
|
||||
property_id_ = convert_eax_v2_0_listener_property_id(property_id_);
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX30_ListenerProperties)
|
||||
{
|
||||
version_ = 3;
|
||||
fx_slot_index_ = 0u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot0)
|
||||
{
|
||||
version_ = 4;
|
||||
fx_slot_index_ = 0u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot0)
|
||||
{
|
||||
version_ = 5;
|
||||
fx_slot_index_ = 0u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot1)
|
||||
{
|
||||
version_ = 4;
|
||||
fx_slot_index_ = 1u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot1)
|
||||
{
|
||||
version_ = 5;
|
||||
fx_slot_index_ = 1u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot2)
|
||||
{
|
||||
version_ = 4;
|
||||
fx_slot_index_ = 2u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot2)
|
||||
{
|
||||
version_ = 5;
|
||||
fx_slot_index_ = 2u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot3)
|
||||
{
|
||||
version_ = 4;
|
||||
fx_slot_index_ = 3u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot3)
|
||||
{
|
||||
version_ = 5;
|
||||
fx_slot_index_ = 3u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX20_BufferProperties)
|
||||
{
|
||||
version_ = 2;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::source;
|
||||
property_id_ = convert_eax_v2_0_buffer_property_id(property_id_);
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX30_BufferProperties)
|
||||
{
|
||||
version_ = 3;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::source;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_Source)
|
||||
{
|
||||
version_ = 4;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::source;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_Source)
|
||||
{
|
||||
version_ = 5;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::source;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX_ReverbProperties)
|
||||
{
|
||||
version_ = 1;
|
||||
fx_slot_index_ = 0u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAXBUFFER_ReverbProperties)
|
||||
{
|
||||
version_ = 1;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::source;
|
||||
}
|
||||
else
|
||||
{
|
||||
fail("Unsupported property set id.");
|
||||
}
|
||||
|
||||
if (version_ < 1 || version_ > 5)
|
||||
{
|
||||
fail("EAX version out of range.");
|
||||
}
|
||||
|
||||
if(!(property_id&deferred_flag))
|
||||
{
|
||||
if(property_set_id_ != EaxEaxCallPropertySetId::fx_slot && property_id_ != 0)
|
||||
{
|
||||
if (!property_buffer)
|
||||
{
|
||||
fail("Null property buffer.");
|
||||
}
|
||||
|
||||
if (property_size == 0)
|
||||
{
|
||||
fail("Empty property.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(property_set_id_ == EaxEaxCallPropertySetId::source && property_source_id_ == 0)
|
||||
{
|
||||
fail("Null AL source id.");
|
||||
}
|
||||
|
||||
if (property_set_id_ == EaxEaxCallPropertySetId::fx_slot)
|
||||
{
|
||||
if (property_id_ < EAXFXSLOT_NONE)
|
||||
{
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void EaxEaxCall::fail(
|
||||
const char* message)
|
||||
{
|
||||
throw EaxEaxCallException{message};
|
||||
}
|
||||
|
||||
ALuint EaxEaxCall::convert_eax_v2_0_listener_property_id(
|
||||
ALuint property_id)
|
||||
{
|
||||
switch (property_id)
|
||||
{
|
||||
case DSPROPERTY_EAX20LISTENER_NONE:
|
||||
return EAXREVERB_NONE;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS:
|
||||
return EAXREVERB_ALLPARAMETERS;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ROOM:
|
||||
return EAXREVERB_ROOM;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ROOMHF:
|
||||
return EAXREVERB_ROOMHF;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR:
|
||||
return EAXREVERB_ROOMROLLOFFFACTOR;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_DECAYTIME:
|
||||
return EAXREVERB_DECAYTIME;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO:
|
||||
return EAXREVERB_DECAYHFRATIO;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_REFLECTIONS:
|
||||
return EAXREVERB_REFLECTIONS;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY:
|
||||
return EAXREVERB_REFLECTIONSDELAY;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_REVERB:
|
||||
return EAXREVERB_REVERB;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_REVERBDELAY:
|
||||
return EAXREVERB_REVERBDELAY;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ENVIRONMENT:
|
||||
return EAXREVERB_ENVIRONMENT;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE:
|
||||
return EAXREVERB_ENVIRONMENTSIZE;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION:
|
||||
return EAXREVERB_ENVIRONMENTDIFFUSION;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF:
|
||||
return EAXREVERB_AIRABSORPTIONHF;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_FLAGS:
|
||||
return EAXREVERB_FLAGS;
|
||||
|
||||
default:
|
||||
fail("Unsupported EAX 2.0 listener property id.");
|
||||
}
|
||||
}
|
||||
|
||||
ALuint EaxEaxCall::convert_eax_v2_0_buffer_property_id(
|
||||
ALuint property_id)
|
||||
{
|
||||
switch (property_id)
|
||||
{
|
||||
case DSPROPERTY_EAX20BUFFER_NONE:
|
||||
return EAXSOURCE_NONE;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS:
|
||||
return EAXSOURCE_ALLPARAMETERS;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_DIRECT:
|
||||
return EAXSOURCE_DIRECT;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_DIRECTHF:
|
||||
return EAXSOURCE_DIRECTHF;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_ROOM:
|
||||
return EAXSOURCE_ROOM;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_ROOMHF:
|
||||
return EAXSOURCE_ROOMHF;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR:
|
||||
return EAXSOURCE_ROOMROLLOFFFACTOR;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OBSTRUCTION:
|
||||
return EAXSOURCE_OBSTRUCTION;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO:
|
||||
return EAXSOURCE_OBSTRUCTIONLFRATIO;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OCCLUSION:
|
||||
return EAXSOURCE_OCCLUSION;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO:
|
||||
return EAXSOURCE_OCCLUSIONLFRATIO;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO:
|
||||
return EAXSOURCE_OCCLUSIONROOMRATIO;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF:
|
||||
return EAXSOURCE_OUTSIDEVOLUMEHF;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR:
|
||||
return EAXSOURCE_AIRABSORPTIONFACTOR;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_FLAGS:
|
||||
return EAXSOURCE_FLAGS;
|
||||
|
||||
default:
|
||||
fail("Unsupported EAX 2.0 buffer property id.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EaxEaxCall create_eax_call(
|
||||
bool is_get,
|
||||
const GUID* property_set_id,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size)
|
||||
{
|
||||
if(!property_set_id)
|
||||
throw EaxEaxCallException{"Null property set ID."};
|
||||
|
||||
return EaxEaxCall{
|
||||
is_get,
|
||||
*property_set_id,
|
||||
property_id,
|
||||
property_source_id,
|
||||
property_buffer,
|
||||
property_size
|
||||
};
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
#ifndef EAX_EAX_CALL_INCLUDED
|
||||
#define EAX_EAX_CALL_INCLUDED
|
||||
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "alspan.h"
|
||||
|
||||
#include "eax_api.h"
|
||||
#include "eax_fx_slot_index.h"
|
||||
|
||||
|
||||
enum class EaxEaxCallPropertySetId
|
||||
{
|
||||
none,
|
||||
|
||||
context,
|
||||
fx_slot,
|
||||
source,
|
||||
fx_slot_effect,
|
||||
}; // EaxEaxCallPropertySetId
|
||||
|
||||
|
||||
class EaxEaxCall
|
||||
{
|
||||
public:
|
||||
EaxEaxCall(
|
||||
bool is_get,
|
||||
const GUID& property_set_guid,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size);
|
||||
|
||||
bool is_get() const noexcept { return is_get_; }
|
||||
int get_version() const noexcept { return version_; }
|
||||
EaxEaxCallPropertySetId get_property_set_id() const noexcept { return property_set_id_; }
|
||||
ALuint get_property_id() const noexcept { return property_id_; }
|
||||
ALuint get_property_al_name() const noexcept { return property_source_id_; }
|
||||
EaxFxSlotIndex get_fx_slot_index() const noexcept { return fx_slot_index_; }
|
||||
|
||||
template<
|
||||
typename TException,
|
||||
typename TValue
|
||||
>
|
||||
TValue& get_value() const
|
||||
{
|
||||
if (property_size_ < static_cast<ALuint>(sizeof(TValue)))
|
||||
{
|
||||
throw TException{"Property buffer too small."};
|
||||
}
|
||||
|
||||
return *static_cast<TValue*>(property_buffer_);
|
||||
}
|
||||
|
||||
template<
|
||||
typename TException,
|
||||
typename TValue
|
||||
>
|
||||
al::span<TValue> get_values() const
|
||||
{
|
||||
if (property_size_ < static_cast<ALuint>(sizeof(TValue)))
|
||||
{
|
||||
throw TException{"Property buffer too small."};
|
||||
}
|
||||
|
||||
const auto count = property_size_ / sizeof(TValue);
|
||||
|
||||
return al::span<TValue>{static_cast<TValue*>(property_buffer_), count};
|
||||
}
|
||||
|
||||
template<
|
||||
typename TException,
|
||||
typename TValue
|
||||
>
|
||||
void set_value(
|
||||
const TValue& value) const
|
||||
{
|
||||
get_value<TException, TValue>() = value;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
const bool is_get_;
|
||||
int version_;
|
||||
EaxFxSlotIndex fx_slot_index_;
|
||||
EaxEaxCallPropertySetId property_set_id_;
|
||||
|
||||
ALuint property_id_;
|
||||
const ALuint property_source_id_;
|
||||
ALvoid*const property_buffer_;
|
||||
const ALuint property_size_;
|
||||
|
||||
|
||||
[[noreturn]]
|
||||
static void fail(
|
||||
const char* message);
|
||||
|
||||
|
||||
static ALuint convert_eax_v2_0_listener_property_id(
|
||||
ALuint property_id);
|
||||
|
||||
static ALuint convert_eax_v2_0_buffer_property_id(
|
||||
ALuint property_id);
|
||||
}; // EaxEaxCall
|
||||
|
||||
|
||||
EaxEaxCall create_eax_call(
|
||||
bool is_get,
|
||||
const GUID* property_set_id,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size);
|
||||
|
||||
|
||||
#endif // !EAX_EAX_CALL_INCLUDED
|
||||
@@ -1,3 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "eax_effect.h"
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifndef EAX_EFFECT_INCLUDED
|
||||
#define EAX_EFFECT_INCLUDED
|
||||
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "eax_eax_call.h"
|
||||
|
||||
class EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxEffect(ALenum type) : al_effect_type_{type} { }
|
||||
virtual ~EaxEffect() = default;
|
||||
|
||||
const ALenum al_effect_type_;
|
||||
EffectProps al_effect_props_{};
|
||||
|
||||
virtual void dispatch(const EaxEaxCall& eax_call) = 0;
|
||||
|
||||
// Returns "true" if any immediated property was changed.
|
||||
// [[nodiscard]]
|
||||
virtual bool apply_deferred() = 0;
|
||||
}; // EaxEffect
|
||||
|
||||
|
||||
using EaxEffectUPtr = std::unique_ptr<EaxEffect>;
|
||||
|
||||
EaxEffectUPtr eax_create_eax_null_effect();
|
||||
EaxEffectUPtr eax_create_eax_chorus_effect();
|
||||
EaxEffectUPtr eax_create_eax_distortion_effect();
|
||||
EaxEffectUPtr eax_create_eax_echo_effect();
|
||||
EaxEffectUPtr eax_create_eax_flanger_effect();
|
||||
EaxEffectUPtr eax_create_eax_frequency_shifter_effect();
|
||||
EaxEffectUPtr eax_create_eax_vocal_morpher_effect();
|
||||
EaxEffectUPtr eax_create_eax_pitch_shifter_effect();
|
||||
EaxEffectUPtr eax_create_eax_ring_modulator_effect();
|
||||
EaxEffectUPtr eax_create_eax_auto_wah_effect();
|
||||
EaxEffectUPtr eax_create_eax_compressor_effect();
|
||||
EaxEffectUPtr eax_create_eax_equalizer_effect();
|
||||
EaxEffectUPtr eax_create_eax_reverb_effect();
|
||||
|
||||
#endif // !EAX_EFFECT_INCLUDED
|
||||
@@ -1,63 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "eax_exception.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
EaxException::EaxException(
|
||||
const char* context,
|
||||
const char* message)
|
||||
:
|
||||
std::runtime_error{make_message(context, message)}
|
||||
{
|
||||
}
|
||||
|
||||
std::string EaxException::make_message(
|
||||
const char* context,
|
||||
const char* message)
|
||||
{
|
||||
const auto context_size = (context ? std::string::traits_type::length(context) : 0);
|
||||
const auto has_contex = (context_size > 0);
|
||||
|
||||
const auto message_size = (message ? std::string::traits_type::length(message) : 0);
|
||||
const auto has_message = (message_size > 0);
|
||||
|
||||
if (!has_contex && !has_message)
|
||||
{
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
static constexpr char left_prefix[] = "[";
|
||||
const auto left_prefix_size = std::string::traits_type::length(left_prefix);
|
||||
|
||||
static constexpr char right_prefix[] = "] ";
|
||||
const auto right_prefix_size = std::string::traits_type::length(right_prefix);
|
||||
|
||||
const auto what_size =
|
||||
(
|
||||
has_contex ?
|
||||
left_prefix_size + context_size + right_prefix_size :
|
||||
0) +
|
||||
message_size +
|
||||
1;
|
||||
|
||||
auto what = std::string{};
|
||||
what.reserve(what_size);
|
||||
|
||||
if (has_contex)
|
||||
{
|
||||
what.append(left_prefix, left_prefix_size);
|
||||
what.append(context, context_size);
|
||||
what.append(right_prefix, right_prefix_size);
|
||||
}
|
||||
|
||||
if (has_message)
|
||||
{
|
||||
what.append(message, message_size);
|
||||
}
|
||||
|
||||
return what;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef EAX_EXCEPTION_INCLUDED
|
||||
#define EAX_EXCEPTION_INCLUDED
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
|
||||
class EaxException :
|
||||
public std::runtime_error
|
||||
{
|
||||
public:
|
||||
EaxException(
|
||||
const char* context,
|
||||
const char* message);
|
||||
|
||||
|
||||
private:
|
||||
static std::string make_message(
|
||||
const char* context,
|
||||
const char* message);
|
||||
}; // EaxException
|
||||
|
||||
|
||||
#endif // !EAX_EXCEPTION_INCLUDED
|
||||
@@ -1,21 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "eax_globals.h"
|
||||
|
||||
|
||||
bool eax_g_is_enabled = true;
|
||||
|
||||
|
||||
const char eax1_ext_name[] = "EAX";
|
||||
const char eax2_ext_name[] = "EAX2.0";
|
||||
const char eax3_ext_name[] = "EAX3.0";
|
||||
const char eax4_ext_name[] = "EAX4.0";
|
||||
const char eax5_ext_name[] = "EAX5.0";
|
||||
|
||||
const char eax_x_ram_ext_name[] = "EAX-RAM";
|
||||
|
||||
const char eax_eax_set_func_name[] = "EAXSet";
|
||||
const char eax_eax_get_func_name[] = "EAXGet";
|
||||
|
||||
const char eax_eax_set_buffer_mode_func_name[] = "EAXSetBufferMode";
|
||||
const char eax_eax_get_buffer_mode_func_name[] = "EAXGetBufferMode";
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef EAX_GLOBALS_INCLUDED
|
||||
#define EAX_GLOBALS_INCLUDED
|
||||
|
||||
|
||||
extern bool eax_g_is_enabled;
|
||||
|
||||
|
||||
extern const char eax1_ext_name[];
|
||||
extern const char eax2_ext_name[];
|
||||
extern const char eax3_ext_name[];
|
||||
extern const char eax4_ext_name[];
|
||||
extern const char eax5_ext_name[];
|
||||
|
||||
extern const char eax_x_ram_ext_name[];
|
||||
|
||||
extern const char eax_eax_set_func_name[];
|
||||
extern const char eax_eax_get_func_name[];
|
||||
|
||||
extern const char eax_eax_set_buffer_mode_func_name[];
|
||||
extern const char eax_eax_get_buffer_mode_func_name[];
|
||||
|
||||
#endif // !EAX_GLOBALS_INCLUDED
|
||||
@@ -1,36 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "eax_utils.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
|
||||
#include "core/logging.h"
|
||||
|
||||
|
||||
void eax_log_exception(
|
||||
const char* message) noexcept
|
||||
{
|
||||
const auto exception_ptr = std::current_exception();
|
||||
|
||||
assert(exception_ptr);
|
||||
|
||||
if (message)
|
||||
{
|
||||
ERR("%s\n", message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::rethrow_exception(exception_ptr);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
const auto ex_message = ex.what();
|
||||
ERR("%s\n", ex_message);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ERR("%s\n", "Generic exception.");
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "eax_x_ram.h"
|
||||
+361
-385
@@ -28,9 +28,13 @@
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
@@ -38,26 +42,23 @@
|
||||
#include "AL/efx-presets.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "al/effects/effects.h"
|
||||
#include "albit.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "core/except.h"
|
||||
#include "core/logging.h"
|
||||
#include "direct_defs.h"
|
||||
#include "error.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
|
||||
#include "eax_exception.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
const EffectList gEffectList[16]{
|
||||
const std::array<EffectList,16> gEffectList{{
|
||||
{ "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
|
||||
{ "reverb", REVERB_EFFECT, AL_EFFECT_REVERB },
|
||||
{ "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH },
|
||||
@@ -73,117 +74,69 @@ const EffectList gEffectList[16]{
|
||||
{ "vmorpher", VMORPHER_EFFECT, AL_EFFECT_VOCAL_MORPHER },
|
||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
|
||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE },
|
||||
{ "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_REVERB_SOFT },
|
||||
};
|
||||
{ "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT },
|
||||
}};
|
||||
|
||||
bool DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
|
||||
effect_exception::effect_exception(ALenum code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
std::va_list args;
|
||||
va_start(args, msg);
|
||||
setMessage(msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct EffectPropsItem {
|
||||
ALenum Type;
|
||||
const EffectProps &DefaultProps;
|
||||
const EffectVtable &Vtable;
|
||||
};
|
||||
constexpr EffectPropsItem EffectPropsList[] = {
|
||||
{ AL_EFFECT_NULL, NullEffectProps, NullEffectVtable },
|
||||
{ AL_EFFECT_EAXREVERB, ReverbEffectProps, ReverbEffectVtable },
|
||||
{ AL_EFFECT_REVERB, StdReverbEffectProps, StdReverbEffectVtable },
|
||||
{ AL_EFFECT_AUTOWAH, AutowahEffectProps, AutowahEffectVtable },
|
||||
{ AL_EFFECT_CHORUS, ChorusEffectProps, ChorusEffectVtable },
|
||||
{ AL_EFFECT_COMPRESSOR, CompressorEffectProps, CompressorEffectVtable },
|
||||
{ AL_EFFECT_DISTORTION, DistortionEffectProps, DistortionEffectVtable },
|
||||
{ AL_EFFECT_ECHO, EchoEffectProps, EchoEffectVtable },
|
||||
{ AL_EFFECT_EQUALIZER, EqualizerEffectProps, EqualizerEffectVtable },
|
||||
{ AL_EFFECT_FLANGER, FlangerEffectProps, FlangerEffectVtable },
|
||||
{ AL_EFFECT_FREQUENCY_SHIFTER, FshifterEffectProps, FshifterEffectVtable },
|
||||
{ AL_EFFECT_RING_MODULATOR, ModulatorEffectProps, ModulatorEffectVtable },
|
||||
{ AL_EFFECT_PITCH_SHIFTER, PshifterEffectProps, PshifterEffectVtable },
|
||||
{ AL_EFFECT_VOCAL_MORPHER, VmorpherEffectProps, VmorpherEffectVtable },
|
||||
{ AL_EFFECT_DEDICATED_DIALOGUE, DedicatedEffectProps, DedicatedEffectVtable },
|
||||
{ AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedEffectProps, DedicatedEffectVtable },
|
||||
{ AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable },
|
||||
};
|
||||
using SubListAllocator = al::allocator<std::array<ALeffect,64>>;
|
||||
|
||||
|
||||
void ALeffect_setParami(ALeffect *effect, ALenum param, int value)
|
||||
{ effect->vtab->setParami(&effect->Props, param, value); }
|
||||
void ALeffect_setParamiv(ALeffect *effect, ALenum param, const int *values)
|
||||
{ effect->vtab->setParamiv(&effect->Props, param, values); }
|
||||
void ALeffect_setParamf(ALeffect *effect, ALenum param, float value)
|
||||
{ effect->vtab->setParamf(&effect->Props, param, value); }
|
||||
void ALeffect_setParamfv(ALeffect *effect, ALenum param, const float *values)
|
||||
{ effect->vtab->setParamfv(&effect->Props, param, values); }
|
||||
|
||||
void ALeffect_getParami(const ALeffect *effect, ALenum param, int *value)
|
||||
{ effect->vtab->getParami(&effect->Props, param, value); }
|
||||
void ALeffect_getParamiv(const ALeffect *effect, ALenum param, int *values)
|
||||
{ effect->vtab->getParamiv(&effect->Props, param, values); }
|
||||
void ALeffect_getParamf(const ALeffect *effect, ALenum param, float *value)
|
||||
{ effect->vtab->getParamf(&effect->Props, param, value); }
|
||||
void ALeffect_getParamfv(const ALeffect *effect, ALenum param, float *values)
|
||||
{ effect->vtab->getParamfv(&effect->Props, param, values); }
|
||||
|
||||
|
||||
const EffectPropsItem *getEffectPropsItemByType(ALenum type)
|
||||
constexpr auto GetDefaultProps(ALenum type) noexcept -> const EffectProps&
|
||||
{
|
||||
auto iter = std::find_if(std::begin(EffectPropsList), std::end(EffectPropsList),
|
||||
[type](const EffectPropsItem &item) noexcept -> bool
|
||||
{ return item.Type == type; });
|
||||
return (iter != std::end(EffectPropsList)) ? std::addressof(*iter) : nullptr;
|
||||
switch(type)
|
||||
{
|
||||
case AL_EFFECT_NULL: return NullEffectProps;
|
||||
case AL_EFFECT_EAXREVERB: return ReverbEffectProps;
|
||||
case AL_EFFECT_REVERB: return StdReverbEffectProps;
|
||||
case AL_EFFECT_AUTOWAH: return AutowahEffectProps;
|
||||
case AL_EFFECT_CHORUS: return ChorusEffectProps;
|
||||
case AL_EFFECT_COMPRESSOR: return CompressorEffectProps;
|
||||
case AL_EFFECT_DISTORTION: return DistortionEffectProps;
|
||||
case AL_EFFECT_ECHO: return EchoEffectProps;
|
||||
case AL_EFFECT_EQUALIZER: return EqualizerEffectProps;
|
||||
case AL_EFFECT_FLANGER: return FlangerEffectProps;
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER: return FshifterEffectProps;
|
||||
case AL_EFFECT_RING_MODULATOR: return ModulatorEffectProps;
|
||||
case AL_EFFECT_PITCH_SHIFTER: return PshifterEffectProps;
|
||||
case AL_EFFECT_VOCAL_MORPHER: return VmorpherEffectProps;
|
||||
case AL_EFFECT_DEDICATED_DIALOGUE: return DedicatedDialogEffectProps;
|
||||
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return DedicatedLfeEffectProps;
|
||||
case AL_EFFECT_CONVOLUTION_SOFT: return ConvolutionEffectProps;
|
||||
}
|
||||
return NullEffectProps;
|
||||
}
|
||||
|
||||
void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
void InitEffectParams(ALeffect *effect, ALenum type) noexcept
|
||||
{
|
||||
const EffectPropsItem *item{getEffectPropsItemByType(type)};
|
||||
if(item)
|
||||
{
|
||||
effect->Props = item->DefaultProps;
|
||||
effect->vtab = &item->Vtable;
|
||||
}
|
||||
else
|
||||
{
|
||||
effect->Props = EffectProps{};
|
||||
effect->vtab = &NullEffectVtable;
|
||||
}
|
||||
effect->Props = GetDefaultProps(type);
|
||||
effect->type = type;
|
||||
}
|
||||
|
||||
bool EnsureEffects(ALCdevice *device, size_t needed)
|
||||
{
|
||||
size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), size_t{0},
|
||||
auto EnsureEffects(ALCdevice *device, size_t needed) noexcept -> bool
|
||||
try {
|
||||
size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz,
|
||||
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
|
||||
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
|
||||
|
||||
while(needed > count)
|
||||
{
|
||||
if UNLIKELY(device->EffectList.size() >= 1<<25)
|
||||
if(device->EffectList.size() >= 1<<25) UNLIKELY
|
||||
return false;
|
||||
|
||||
device->EffectList.emplace_back();
|
||||
auto sublist = device->EffectList.end() - 1;
|
||||
sublist->FreeMask = ~0_u64;
|
||||
sublist->Effects = static_cast<ALeffect*>(al_calloc(alignof(ALeffect), sizeof(ALeffect)*64));
|
||||
if UNLIKELY(!sublist->Effects)
|
||||
{
|
||||
device->EffectList.pop_back();
|
||||
return false;
|
||||
}
|
||||
count += 64;
|
||||
EffectSubList sublist{};
|
||||
sublist.FreeMask = ~0_u64;
|
||||
sublist.Effects = SubListAllocator{}.allocate(1);
|
||||
device->EffectList.emplace_back(std::move(sublist));
|
||||
count += std::tuple_size_v<SubListAllocator::value_type>;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch(...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ALeffect *AllocEffect(ALCdevice *device)
|
||||
ALeffect *AllocEffect(ALCdevice *device) noexcept
|
||||
{
|
||||
auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
|
||||
[](const EffectSubList &entry) noexcept -> bool
|
||||
@@ -192,7 +145,7 @@ ALeffect *AllocEffect(ALCdevice *device)
|
||||
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
|
||||
ASSUME(slidx < 64);
|
||||
|
||||
ALeffect *effect{al::construct_at(sublist->Effects + slidx)};
|
||||
ALeffect *effect{al::construct_at(al::to_address(sublist->Effects->begin() + slidx))};
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
|
||||
/* Add 1 to avoid effect ID 0. */
|
||||
@@ -205,339 +158,347 @@ ALeffect *AllocEffect(ALCdevice *device)
|
||||
|
||||
void FreeEffect(ALCdevice *device, ALeffect *effect)
|
||||
{
|
||||
device->mEffectNames.erase(effect->id);
|
||||
|
||||
const ALuint id{effect->id - 1};
|
||||
const size_t lidx{id >> 6};
|
||||
const ALuint slidx{id & 0x3f};
|
||||
|
||||
al::destroy_at(effect);
|
||||
std::destroy_at(effect);
|
||||
|
||||
device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
|
||||
}
|
||||
|
||||
inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
||||
inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
|
||||
{
|
||||
const size_t lidx{(id-1) >> 6};
|
||||
const ALuint slidx{(id-1) & 0x3f};
|
||||
|
||||
if UNLIKELY(lidx >= device->EffectList.size())
|
||||
if(lidx >= device->EffectList.size()) UNLIKELY
|
||||
return nullptr;
|
||||
EffectSubList &sublist = device->EffectList[lidx];
|
||||
if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
|
||||
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
|
||||
return nullptr;
|
||||
return sublist.Effects + slidx;
|
||||
return al::to_address(sublist.Effects->begin() + slidx);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
if UNLIKELY(n < 0)
|
||||
context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects)
|
||||
FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
|
||||
try {
|
||||
if(n < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Generating %d effects", n};
|
||||
if(n <= 0) UNLIKELY return;
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
if(!EnsureEffects(device, static_cast<ALuint>(n)))
|
||||
{
|
||||
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n, (n==1)?"":"s");
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
if LIKELY(n == 1)
|
||||
{
|
||||
/* Special handling for the easy and normal case. */
|
||||
ALeffect *effect{AllocEffect(device)};
|
||||
effects[0] = effect->id;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Store the allocated buffer IDs in a separate local list, to avoid
|
||||
* modifying the user storage in case of failure.
|
||||
*/
|
||||
al::vector<ALuint> ids;
|
||||
ids.reserve(static_cast<ALuint>(n));
|
||||
do {
|
||||
ALeffect *effect{AllocEffect(device)};
|
||||
ids.emplace_back(effect->id);
|
||||
} while(--n);
|
||||
std::copy(ids.cbegin(), ids.cend(), effects);
|
||||
}
|
||||
const al::span eids{effects, static_cast<ALuint>(n)};
|
||||
if(!EnsureEffects(device, eids.size()))
|
||||
throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n,
|
||||
(n == 1) ? "" : "s"};
|
||||
|
||||
std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; });
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
if UNLIKELY(n < 0)
|
||||
context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects)
|
||||
FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n,
|
||||
const ALuint *effects) noexcept
|
||||
try {
|
||||
if(n < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Deleting %d effects", n};
|
||||
if(n <= 0) UNLIKELY return;
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
/* First try to find any effects that are invalid. */
|
||||
auto validate_effect = [device](const ALuint eid) -> bool
|
||||
{ return !eid || LookupEffect(device, eid) != nullptr; };
|
||||
|
||||
const ALuint *effects_end = effects + n;
|
||||
auto inveffect = std::find_if_not(effects, effects_end, validate_effect);
|
||||
if UNLIKELY(inveffect != effects_end)
|
||||
{
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", *inveffect);
|
||||
return;
|
||||
}
|
||||
const al::span eids{effects, static_cast<ALuint>(n)};
|
||||
auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect);
|
||||
if(inveffect != eids.end())
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", *inveffect};
|
||||
|
||||
/* All good. Delete non-0 effect IDs. */
|
||||
auto delete_effect = [device](ALuint eid) -> void
|
||||
{
|
||||
ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr};
|
||||
if(effect) FreeEffect(device, effect);
|
||||
if(ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr})
|
||||
FreeEffect(device, effect);
|
||||
};
|
||||
std::for_each(effects, effects_end, delete_effect);
|
||||
std::for_each(eids.begin(), eids.end(), delete_effect);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
|
||||
START_API_FUNC
|
||||
AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect)
|
||||
FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if LIKELY(context)
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
if(!effect || LookupEffect(device, effect))
|
||||
return AL_TRUE;
|
||||
}
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
if(!effect || LookupEffect(device, effect))
|
||||
return AL_TRUE;
|
||||
return AL_FALSE;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alEffecti, ALuint,effect, ALenum,param, ALint,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALint value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if UNLIKELY(!aleffect)
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else if(param == AL_EFFECT_TYPE)
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
bool isOk{value == AL_EFFECT_NULL};
|
||||
if(!isOk)
|
||||
case AL_EFFECT_TYPE:
|
||||
if(value != AL_EFFECT_NULL)
|
||||
{
|
||||
for(const EffectList &effectitem : gEffectList)
|
||||
{
|
||||
if(value == effectitem.val && !DisabledEffects[effectitem.type])
|
||||
{
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto check_effect = [value](const EffectList &item) -> bool
|
||||
{ return value == item.val && !DisabledEffects.test(item.type); };
|
||||
if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect))
|
||||
throw al::context_error{AL_INVALID_VALUE, "Effect type 0x%04x not supported",
|
||||
value};
|
||||
}
|
||||
|
||||
if(isOk)
|
||||
InitEffectParams(aleffect, value);
|
||||
else
|
||||
context->setError(AL_INVALID_VALUE, "Effect type 0x%04x not supported", value);
|
||||
}
|
||||
else try
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_setParami(aleffect, param, value);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_TYPE:
|
||||
alEffecti(effect, param, values[0]);
|
||||
InitEffectParams(aleffect, value);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
if constexpr(std::is_same_v<Type,ReverbProps>)
|
||||
{
|
||||
if(aleffect->type == AL_EFFECT_REVERB)
|
||||
return EffectHandler::StdReverbSetParami(arg, param, value);
|
||||
}
|
||||
return EffectHandler::SetParami(arg, param, value);
|
||||
}, aleffect->Props);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC3(void, alEffectiv, ALuint,effect, ALenum,param, const ALint*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
const ALint *values) noexcept
|
||||
try {
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_TYPE:
|
||||
alEffectiDirect(context, effect, param, *values);
|
||||
return;
|
||||
}
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if UNLIKELY(!aleffect)
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_setParamiv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
if constexpr(std::is_same_v<Type,ReverbProps>)
|
||||
{
|
||||
if(aleffect->type == AL_EFFECT_REVERB)
|
||||
return EffectHandler::StdReverbSetParamiv(arg, param, values);
|
||||
}
|
||||
return EffectHandler::SetParamiv(arg, param, values);
|
||||
}, aleffect->Props);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alEffectf, ALuint,effect, ALenum,param, ALfloat,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALfloat value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if UNLIKELY(!aleffect)
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect) UNLIKELY
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_setParamf(aleffect, param, value);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
if constexpr(std::is_same_v<Type,ReverbProps>)
|
||||
{
|
||||
if(aleffect->type == AL_EFFECT_REVERB)
|
||||
return EffectHandler::StdReverbSetParamf(arg, param, value);
|
||||
}
|
||||
return EffectHandler::SetParamf(arg, param, value);
|
||||
}, aleffect->Props);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alEffectfv, ALuint,effect, ALenum,param, const ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
const ALfloat *values) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if UNLIKELY(!aleffect)
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_setParamfv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
if constexpr(std::is_same_v<Type,ReverbProps>)
|
||||
{
|
||||
if(aleffect->type == AL_EFFECT_REVERB)
|
||||
return EffectHandler::StdReverbSetParamfv(arg, param, values);
|
||||
}
|
||||
return EffectHandler::SetParamfv(arg, param, values);
|
||||
}, aleffect->Props);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALint *value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if UNLIKELY(!aleffect)
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else if(param == AL_EFFECT_TYPE)
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_TYPE:
|
||||
*value = aleffect->type;
|
||||
else try
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParami(aleffect, param, value);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_TYPE:
|
||||
alGetEffecti(effect, param, values);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
if constexpr(std::is_same_v<Type,ReverbProps>)
|
||||
{
|
||||
if(aleffect->type == AL_EFFECT_REVERB)
|
||||
return EffectHandler::StdReverbGetParami(arg, param, value);
|
||||
}
|
||||
return EffectHandler::GetParami(arg, param, value);
|
||||
}, aleffect->Props);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,effect, ALenum,param, ALint*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALint *values) noexcept
|
||||
try {
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_TYPE:
|
||||
alGetEffectiDirect(context, effect, param, values);
|
||||
return;
|
||||
}
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if UNLIKELY(!aleffect)
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParamiv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
if constexpr(std::is_same_v<Type,ReverbProps>)
|
||||
{
|
||||
if(aleffect->type == AL_EFFECT_REVERB)
|
||||
return EffectHandler::StdReverbGetParamiv(arg, param, values);
|
||||
}
|
||||
return EffectHandler::GetParamiv(arg, param, values);
|
||||
}, aleffect->Props);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALfloat *value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if UNLIKELY(!aleffect)
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParamf(aleffect, param, value);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
if constexpr(std::is_same_v<Type,ReverbProps>)
|
||||
{
|
||||
if(aleffect->type == AL_EFFECT_REVERB)
|
||||
return EffectHandler::StdReverbGetParamf(arg, param, value);
|
||||
}
|
||||
return EffectHandler::GetParamf(arg, param, value);
|
||||
}, aleffect->Props);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALfloat *values) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if UNLIKELY(!aleffect)
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParamfv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
if constexpr(std::is_same_v<Type,ReverbProps>)
|
||||
{
|
||||
if(aleffect->type == AL_EFFECT_REVERB)
|
||||
return EffectHandler::StdReverbGetParamfv(arg, param, values);
|
||||
}
|
||||
return EffectHandler::GetParamfv(arg, param, values);
|
||||
}, aleffect->Props);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
void InitEffect(ALeffect *effect)
|
||||
@@ -545,26 +506,43 @@ void InitEffect(ALeffect *effect)
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
}
|
||||
|
||||
void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name)
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
auto effect = LookupEffect(device, id);
|
||||
if(!effect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", id};
|
||||
|
||||
device->mEffectNames.insert_or_assign(id, name);
|
||||
}
|
||||
|
||||
|
||||
EffectSubList::~EffectSubList()
|
||||
{
|
||||
if(!Effects)
|
||||
return;
|
||||
|
||||
uint64_t usemask{~FreeMask};
|
||||
while(usemask)
|
||||
{
|
||||
const int idx{al::countr_zero(usemask)};
|
||||
al::destroy_at(Effects+idx);
|
||||
std::destroy_at(al::to_address(Effects->begin()+idx));
|
||||
usemask &= ~(1_u64 << idx);
|
||||
}
|
||||
FreeMask = ~usemask;
|
||||
al_free(Effects);
|
||||
SubListAllocator{}.deallocate(Effects, 1);
|
||||
Effects = nullptr;
|
||||
}
|
||||
|
||||
|
||||
#define DECL(x) { #x, EFX_REVERB_PRESET_##x }
|
||||
static const struct {
|
||||
const char name[32];
|
||||
struct EffectPreset {
|
||||
const char name[32]; /* NOLINT(*-avoid-c-arrays) */
|
||||
EFXEAXREVERBPROPERTIES props;
|
||||
} reverblist[] = {
|
||||
};
|
||||
#define DECL(x) EffectPreset{#x, EFX_REVERB_PRESET_##x}
|
||||
static constexpr std::array reverblist{
|
||||
DECL(GENERIC),
|
||||
DECL(PADDEDCELL),
|
||||
DECL(ROOM),
|
||||
@@ -694,61 +672,62 @@ static const struct {
|
||||
};
|
||||
#undef DECL
|
||||
|
||||
void LoadReverbPreset(const char *name, ALeffect *effect)
|
||||
void LoadReverbPreset(const std::string_view name, ALeffect *effect)
|
||||
{
|
||||
if(al::strcasecmp(name, "NONE") == 0)
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
if(al::case_compare(name, "NONE"sv) == 0)
|
||||
{
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
TRACE("Loading reverb '%s'\n", "NONE");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!DisabledEffects[EAXREVERB_EFFECT])
|
||||
if(!DisabledEffects.test(EAXREVERB_EFFECT))
|
||||
InitEffectParams(effect, AL_EFFECT_EAXREVERB);
|
||||
else if(!DisabledEffects[REVERB_EFFECT])
|
||||
else if(!DisabledEffects.test(REVERB_EFFECT))
|
||||
InitEffectParams(effect, AL_EFFECT_REVERB);
|
||||
else
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
for(const auto &reverbitem : reverblist)
|
||||
{
|
||||
const EFXEAXREVERBPROPERTIES *props;
|
||||
|
||||
if(al::strcasecmp(name, reverbitem.name) != 0)
|
||||
if(al::case_compare(name, std::data(reverbitem.name)) != 0)
|
||||
continue;
|
||||
|
||||
TRACE("Loading reverb '%s'\n", reverbitem.name);
|
||||
props = &reverbitem.props;
|
||||
effect->Props.Reverb.Density = props->flDensity;
|
||||
effect->Props.Reverb.Diffusion = props->flDiffusion;
|
||||
effect->Props.Reverb.Gain = props->flGain;
|
||||
effect->Props.Reverb.GainHF = props->flGainHF;
|
||||
effect->Props.Reverb.GainLF = props->flGainLF;
|
||||
effect->Props.Reverb.DecayTime = props->flDecayTime;
|
||||
effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio;
|
||||
effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio;
|
||||
effect->Props.Reverb.ReflectionsGain = props->flReflectionsGain;
|
||||
effect->Props.Reverb.ReflectionsDelay = props->flReflectionsDelay;
|
||||
effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0];
|
||||
effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1];
|
||||
effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2];
|
||||
effect->Props.Reverb.LateReverbGain = props->flLateReverbGain;
|
||||
effect->Props.Reverb.LateReverbDelay = props->flLateReverbDelay;
|
||||
effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0];
|
||||
effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1];
|
||||
effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2];
|
||||
effect->Props.Reverb.EchoTime = props->flEchoTime;
|
||||
effect->Props.Reverb.EchoDepth = props->flEchoDepth;
|
||||
effect->Props.Reverb.ModulationTime = props->flModulationTime;
|
||||
effect->Props.Reverb.ModulationDepth = props->flModulationDepth;
|
||||
effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF;
|
||||
effect->Props.Reverb.HFReference = props->flHFReference;
|
||||
effect->Props.Reverb.LFReference = props->flLFReference;
|
||||
effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor;
|
||||
effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit ? AL_TRUE : AL_FALSE;
|
||||
TRACE("Loading reverb '%s'\n", std::data(reverbitem.name));
|
||||
const auto &props = reverbitem.props;
|
||||
auto &dst = std::get<ReverbProps>(effect->Props);
|
||||
dst.Density = props.flDensity;
|
||||
dst.Diffusion = props.flDiffusion;
|
||||
dst.Gain = props.flGain;
|
||||
dst.GainHF = props.flGainHF;
|
||||
dst.GainLF = props.flGainLF;
|
||||
dst.DecayTime = props.flDecayTime;
|
||||
dst.DecayHFRatio = props.flDecayHFRatio;
|
||||
dst.DecayLFRatio = props.flDecayLFRatio;
|
||||
dst.ReflectionsGain = props.flReflectionsGain;
|
||||
dst.ReflectionsDelay = props.flReflectionsDelay;
|
||||
dst.ReflectionsPan[0] = props.flReflectionsPan[0];
|
||||
dst.ReflectionsPan[1] = props.flReflectionsPan[1];
|
||||
dst.ReflectionsPan[2] = props.flReflectionsPan[2];
|
||||
dst.LateReverbGain = props.flLateReverbGain;
|
||||
dst.LateReverbDelay = props.flLateReverbDelay;
|
||||
dst.LateReverbPan[0] = props.flLateReverbPan[0];
|
||||
dst.LateReverbPan[1] = props.flLateReverbPan[1];
|
||||
dst.LateReverbPan[2] = props.flLateReverbPan[2];
|
||||
dst.EchoTime = props.flEchoTime;
|
||||
dst.EchoDepth = props.flEchoDepth;
|
||||
dst.ModulationTime = props.flModulationTime;
|
||||
dst.ModulationDepth = props.flModulationDepth;
|
||||
dst.AirAbsorptionGainHF = props.flAirAbsorptionGainHF;
|
||||
dst.HFReference = props.flHFReference;
|
||||
dst.LFReference = props.flLFReference;
|
||||
dst.RoomRolloffFactor = props.flRoomRolloffFactor;
|
||||
dst.DecayHFLimit = props.iDecayHFLimit ? AL_TRUE : AL_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
WARN("Reverb preset '%s' not found\n", name);
|
||||
WARN("Reverb preset '%.*s' not found\n", al::sizei(name), name.data());
|
||||
}
|
||||
|
||||
bool IsValidEffectType(ALenum type) noexcept
|
||||
@@ -756,10 +735,7 @@ bool IsValidEffectType(ALenum type) noexcept
|
||||
if(type == AL_EFFECT_NULL)
|
||||
return true;
|
||||
|
||||
for(const auto &effect_item : gEffectList)
|
||||
{
|
||||
if(type == effect_item.val && !DisabledEffects[effect_item.type])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
auto check_effect = [type](const EffectList &item) noexcept -> bool
|
||||
{ return type == item.val && !DisabledEffects.test(item.type); };
|
||||
return std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
#ifndef AL_EFFECT_H
|
||||
#define AL_EFFECT_H
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "al/effects/effects.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/effects/base.h"
|
||||
|
||||
|
||||
enum {
|
||||
@@ -27,16 +35,14 @@ enum {
|
||||
|
||||
MAX_EFFECTS
|
||||
};
|
||||
extern bool DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
extern float ReverbBoost;
|
||||
inline std::bitset<MAX_EFFECTS> DisabledEffects;
|
||||
|
||||
struct EffectList {
|
||||
const char name[16];
|
||||
int type;
|
||||
const char name[16]; /* NOLINT(*-avoid-c-arrays) */
|
||||
ALuint type;
|
||||
ALenum val;
|
||||
};
|
||||
extern const EffectList gEffectList[16];
|
||||
extern const std::array<EffectList,16> gEffectList;
|
||||
|
||||
|
||||
struct ALeffect {
|
||||
@@ -45,18 +51,33 @@ struct ALeffect {
|
||||
|
||||
EffectProps Props{};
|
||||
|
||||
const EffectVtable *vtab{nullptr};
|
||||
|
||||
/* Self ID */
|
||||
ALuint id{0u};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC
|
||||
};
|
||||
|
||||
void InitEffect(ALeffect *effect);
|
||||
|
||||
void LoadReverbPreset(const char *name, ALeffect *effect);
|
||||
void LoadReverbPreset(const std::string_view name, ALeffect *effect);
|
||||
|
||||
bool IsValidEffectType(ALenum type) noexcept;
|
||||
|
||||
struct EffectSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALeffect,64>*> Effects{nullptr}; /* 64 */
|
||||
|
||||
EffectSubList() noexcept = default;
|
||||
EffectSubList(const EffectSubList&) = delete;
|
||||
EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; }
|
||||
~EffectSubList();
|
||||
|
||||
EffectSubList& operator=(const EffectSubList&) = delete;
|
||||
EffectSubList& operator=(EffectSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,536 +13,228 @@
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Autowah_setParamf(EffectProps *props, ALenum param, float val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
AutowahProps props{};
|
||||
props.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
|
||||
props.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
|
||||
props.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
|
||||
props.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps AutowahEffectProps{genDefaultProps()};
|
||||
|
||||
void EffectHandler::SetParami(AutowahProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
||||
void EffectHandler::SetParamiv(AutowahProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void EffectHandler::SetParamf(AutowahProps &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;
|
||||
props.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;
|
||||
props.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;
|
||||
props.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;
|
||||
props.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 EffectHandler::SetParamfv(AutowahProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Autowah_setParami(EffectProps*, ALenum param, int)
|
||||
void EffectHandler::GetParami(const AutowahProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
||||
void Autowah_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void EffectHandler::GetParamiv(const AutowahProps&, ALenum param, 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)
|
||||
void EffectHandler::GetParamf(const AutowahProps &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;
|
||||
case AL_AUTOWAH_ATTACK_TIME: *val = props.AttackTime; break;
|
||||
case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; break;
|
||||
case AL_AUTOWAH_RESONANCE: *val = props.Resonance; break;
|
||||
case AL_AUTOWAH_PEAK_GAIN: *val = props.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()};
|
||||
void EffectHandler::GetParamfv(const AutowahProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxAutoWahEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
using AutowahCommitter = EaxCommitter<EaxAutowahCommitter>;
|
||||
|
||||
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();
|
||||
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
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();
|
||||
|
||||
|
||||
void 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);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxAutoWahEffect
|
||||
|
||||
|
||||
class EaxAutoWahEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxAutoWahEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_AUTO_WAH_EFFECT", message}
|
||||
struct AttackTimeValidator {
|
||||
void operator()(float flAttackTime) const
|
||||
{
|
||||
eax_validate_range<AutowahCommitter::Exception>(
|
||||
"Attack Time",
|
||||
flAttackTime,
|
||||
EAXAUTOWAH_MINATTACKTIME,
|
||||
EAXAUTOWAH_MAXATTACKTIME);
|
||||
}
|
||||
}; // EaxAutoWahEffectException
|
||||
}; // AttackTimeValidator
|
||||
|
||||
|
||||
EaxAutoWahEffect::EaxAutoWahEffect()
|
||||
: EaxEffect{AL_EFFECT_AUTOWAH}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
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<float>(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<float>(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();
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch (eax_call.get_property_id())
|
||||
struct ReleaseTimeValidator {
|
||||
void operator()(float flReleaseTime) const
|
||||
{
|
||||
case EAXAUTOWAH_NONE:
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxAutoWahEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_ATTACKTIME:
|
||||
eax_call.set_value<EaxAutoWahEffectException>(eax_.flAttackTime);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_RELEASETIME:
|
||||
eax_call.set_value<EaxAutoWahEffectException>(eax_.flReleaseTime);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_RESONANCE:
|
||||
eax_call.set_value<EaxAutoWahEffectException>(eax_.lResonance);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_PEAKLEVEL:
|
||||
eax_call.set_value<EaxAutoWahEffectException>(eax_.lPeakLevel);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxAutoWahEffectException{"Unsupported property id."};
|
||||
eax_validate_range<AutowahCommitter::Exception>(
|
||||
"Release Time",
|
||||
flReleaseTime,
|
||||
EAXAUTOWAH_MINRELEASETIME,
|
||||
EAXAUTOWAH_MAXRELEASETIME);
|
||||
}
|
||||
}
|
||||
}; // ReleaseTimeValidator
|
||||
|
||||
void EaxAutoWahEffect::validate_attack_time(
|
||||
float flAttackTime)
|
||||
{
|
||||
eax_validate_range<EaxAutoWahEffectException>(
|
||||
"Attack Time",
|
||||
flAttackTime,
|
||||
EAXAUTOWAH_MINATTACKTIME,
|
||||
EAXAUTOWAH_MAXATTACKTIME);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::validate_release_time(
|
||||
float flReleaseTime)
|
||||
{
|
||||
eax_validate_range<EaxAutoWahEffectException>(
|
||||
"Release Time",
|
||||
flReleaseTime,
|
||||
EAXAUTOWAH_MINRELEASETIME,
|
||||
EAXAUTOWAH_MAXRELEASETIME);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::validate_resonance(
|
||||
long lResonance)
|
||||
{
|
||||
eax_validate_range<EaxAutoWahEffectException>(
|
||||
"Resonance",
|
||||
lResonance,
|
||||
EAXAUTOWAH_MINRESONANCE,
|
||||
EAXAUTOWAH_MAXRESONANCE);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::validate_peak_level(
|
||||
long lPeakLevel)
|
||||
{
|
||||
eax_validate_range<EaxAutoWahEffectException>(
|
||||
"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<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::flAttackTime)>();
|
||||
|
||||
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<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::flReleaseTime)>();
|
||||
|
||||
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<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::lResonance)>();
|
||||
|
||||
validate_resonance(resonance);
|
||||
defer_resonance(resonance);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_peak_level(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& peak_level =
|
||||
eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::lPeakLevel)>();
|
||||
|
||||
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<EaxAutoWahEffectException, const EAXAUTOWAHPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxAutoWahEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxAutoWahEffectDirtyFlags{})
|
||||
struct ResonanceValidator {
|
||||
void operator()(long lResonance) const
|
||||
{
|
||||
eax_validate_range<AutowahCommitter::Exception>(
|
||||
"Resonance",
|
||||
lResonance,
|
||||
EAXAUTOWAH_MINRESONANCE,
|
||||
EAXAUTOWAH_MAXRESONANCE);
|
||||
}
|
||||
}; // ResonanceValidator
|
||||
|
||||
struct PeakLevelValidator {
|
||||
void operator()(long lPeakLevel) const
|
||||
{
|
||||
eax_validate_range<AutowahCommitter::Exception>(
|
||||
"Peak Level",
|
||||
lPeakLevel,
|
||||
EAXAUTOWAH_MINPEAKLEVEL,
|
||||
EAXAUTOWAH_MAXPEAKLEVEL);
|
||||
}
|
||||
}; // PeakLevelValidator
|
||||
|
||||
struct AllValidator {
|
||||
void operator()(const EAXAUTOWAHPROPERTIES& all) const
|
||||
{
|
||||
AttackTimeValidator{}(all.flAttackTime);
|
||||
ReleaseTimeValidator{}(all.flReleaseTime);
|
||||
ResonanceValidator{}(all.lResonance);
|
||||
PeakLevelValidator{}(all.lPeakLevel);
|
||||
}
|
||||
}; // AllValidator
|
||||
|
||||
} // namespace
|
||||
|
||||
template<>
|
||||
struct AutowahCommitter::Exception : public EaxException
|
||||
{
|
||||
explicit Exception(const char *message) : EaxException{"EAX_AUTOWAH_EFFECT", message}
|
||||
{ }
|
||||
};
|
||||
|
||||
template<>
|
||||
[[noreturn]] void AutowahCommitter::fail(const char *message)
|
||||
{
|
||||
throw Exception{message};
|
||||
}
|
||||
|
||||
bool EaxAutowahCommitter::commit(const EAXAUTOWAHPROPERTIES &props)
|
||||
{
|
||||
if(auto *cur = std::get_if<EAXAUTOWAHPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
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{};
|
||||
mEaxProps = props;
|
||||
mAlProps = [&]{
|
||||
AutowahProps ret{};
|
||||
ret.AttackTime = props.flAttackTime;
|
||||
ret.ReleaseTime = props.flReleaseTime;
|
||||
ret.Resonance = level_mb_to_gain(static_cast<float>(props.lResonance));
|
||||
ret.PeakGain = level_mb_to_gain(static_cast<float>(props.lPeakLevel));
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::set(const EaxEaxCall& eax_call)
|
||||
void EaxAutowahCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
switch (eax_call.get_property_id())
|
||||
static constexpr EAXAUTOWAHPROPERTIES defprops{[]
|
||||
{
|
||||
case EAXAUTOWAH_NONE:
|
||||
break;
|
||||
EAXAUTOWAHPROPERTIES ret{};
|
||||
ret.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
|
||||
ret.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
|
||||
ret.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
|
||||
ret.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
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."};
|
||||
void EaxAutowahCommitter::Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAUTOWAH_NONE: break;
|
||||
case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.flAttackTime); break;
|
||||
case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.flReleaseTime); break;
|
||||
case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.lResonance); break;
|
||||
case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.lPeakLevel); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_auto_wah_effect()
|
||||
void EaxAutowahCommitter::Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props)
|
||||
{
|
||||
return std::make_unique<::EaxAutoWahEffect>();
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAUTOWAH_NONE: break;
|
||||
case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.flAttackTime); break;
|
||||
case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.flReleaseTime); break;
|
||||
case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.lResonance); break;
|
||||
case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.lPeakLevel); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
+406
-1111
File diff suppressed because it is too large
Load Diff
@@ -9,22 +9,33 @@
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Compressor_setParami(EffectProps *props, ALenum param, int val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
CompressorProps props{};
|
||||
props.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps CompressorEffectProps{genDefaultProps()};
|
||||
|
||||
void EffectHandler::SetParami(CompressorProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"};
|
||||
props->Compressor.OnOff = (val != AL_FALSE);
|
||||
props.OnOff = (val != AL_FALSE);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -32,22 +43,22 @@ void Compressor_setParami(EffectProps *props, ALenum param, int val)
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Compressor_setParami(props, param, vals[0]); }
|
||||
void Compressor_setParamf(EffectProps*, ALenum param, float)
|
||||
void EffectHandler::SetParamiv(CompressorProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
void EffectHandler::SetParamf(CompressorProps&, ALenum param, float)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
||||
void Compressor_setParamfv(EffectProps*, ALenum param, const float*)
|
||||
void EffectHandler::SetParamfv(CompressorProps&, ALenum param, const float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void Compressor_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void EffectHandler::GetParami(const CompressorProps &props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
*val = props->Compressor.OnOff;
|
||||
*val = props.OnOff;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -55,242 +66,91 @@ void Compressor_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Compressor_getParami(props, param, vals); }
|
||||
void Compressor_getParamf(const EffectProps*, ALenum param, float*)
|
||||
void EffectHandler::GetParamiv(const CompressorProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
void EffectHandler::GetParamf(const CompressorProps&, ALenum param, float*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
||||
void Compressor_getParamfv(const EffectProps*, ALenum param, float*)
|
||||
void EffectHandler::GetParamfv(const CompressorProps&, ALenum param, float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Compressor);
|
||||
|
||||
const EffectProps CompressorEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxCompressorEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
using CompressorCommitter = EaxCommitter<EaxCompressorCommitter>;
|
||||
|
||||
struct EaxCompressorEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxCompressorEffectDirtyFlagsValue ulOnOff : 1;
|
||||
}; // EaxCompressorEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxCompressorEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxCompressorEffect();
|
||||
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXAGCCOMPRESSORPROPERTIES eax_{};
|
||||
EAXAGCCOMPRESSORPROPERTIES eax_d_{};
|
||||
EaxCompressorEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_on_off();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_on_off(unsigned long ulOnOff);
|
||||
void validate_all(const EAXAGCCOMPRESSORPROPERTIES& eax_all);
|
||||
|
||||
void defer_on_off(unsigned long ulOnOff);
|
||||
void defer_all(const EAXAGCCOMPRESSORPROPERTIES& eax_all);
|
||||
|
||||
void defer_on_off(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxCompressorEffect
|
||||
|
||||
|
||||
class EaxCompressorEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxCompressorEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_COMPRESSOR_EFFECT", message}
|
||||
struct OnOffValidator {
|
||||
void operator()(unsigned long ulOnOff) const
|
||||
{
|
||||
eax_validate_range<CompressorCommitter::Exception>(
|
||||
"On-Off",
|
||||
ulOnOff,
|
||||
EAXAGCCOMPRESSOR_MINONOFF,
|
||||
EAXAGCCOMPRESSOR_MAXONOFF);
|
||||
}
|
||||
}; // EaxCompressorEffectException
|
||||
}; // OnOffValidator
|
||||
|
||||
|
||||
EaxCompressorEffect::EaxCompressorEffect()
|
||||
: EaxEffect{AL_EFFECT_COMPRESSOR}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
void EaxCompressorEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::set_efx_on_off()
|
||||
{
|
||||
const auto on_off = clamp(
|
||||
static_cast<ALint>(eax_.ulOnOff),
|
||||
AL_COMPRESSOR_MIN_ONOFF,
|
||||
AL_COMPRESSOR_MAX_ONOFF);
|
||||
|
||||
al_effect_props_.Compressor.OnOff = (on_off != AL_FALSE);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_on_off();
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
struct AllValidator {
|
||||
void operator()(const EAXAGCCOMPRESSORPROPERTIES& all) const
|
||||
{
|
||||
case EAXAGCCOMPRESSOR_NONE:
|
||||
break;
|
||||
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxCompressorEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXAGCCOMPRESSOR_ONOFF:
|
||||
eax_call.set_value<EaxCompressorEffectException>(eax_.ulOnOff);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxCompressorEffectException{"Unsupported property id."};
|
||||
OnOffValidator{}(all.ulOnOff);
|
||||
}
|
||||
}; // AllValidator
|
||||
|
||||
} // namespace
|
||||
|
||||
template<>
|
||||
struct CompressorCommitter::Exception : public EaxException
|
||||
{
|
||||
explicit Exception(const char *message) : EaxException{"EAX_CHORUS_EFFECT", message}
|
||||
{ }
|
||||
};
|
||||
|
||||
template<>
|
||||
[[noreturn]] void CompressorCommitter::fail(const char *message)
|
||||
{
|
||||
throw Exception{message};
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::validate_on_off(
|
||||
unsigned long ulOnOff)
|
||||
bool EaxCompressorCommitter::commit(const EAXAGCCOMPRESSORPROPERTIES &props)
|
||||
{
|
||||
eax_validate_range<EaxCompressorEffectException>(
|
||||
"On-Off",
|
||||
ulOnOff,
|
||||
EAXAGCCOMPRESSOR_MINONOFF,
|
||||
EAXAGCCOMPRESSOR_MAXONOFF);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::validate_all(
|
||||
const EAXAGCCOMPRESSORPROPERTIES& eax_all)
|
||||
{
|
||||
validate_on_off(eax_all.ulOnOff);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::defer_on_off(
|
||||
unsigned long ulOnOff)
|
||||
{
|
||||
eax_d_.ulOnOff = ulOnOff;
|
||||
eax_dirty_flags_.ulOnOff = (eax_.ulOnOff != eax_d_.ulOnOff);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::defer_all(
|
||||
const EAXAGCCOMPRESSORPROPERTIES& eax_all)
|
||||
{
|
||||
defer_on_off(eax_all.ulOnOff);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::defer_on_off(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& on_off =
|
||||
eax_call.get_value<EaxCompressorEffectException, const decltype(EAXAGCCOMPRESSORPROPERTIES::ulOnOff)>();
|
||||
|
||||
validate_on_off(on_off);
|
||||
defer_on_off(on_off);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<EaxCompressorEffectException, const EAXAGCCOMPRESSORPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxCompressorEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxCompressorEffectDirtyFlags{})
|
||||
{
|
||||
if(auto *cur = std::get_if<EAXAGCCOMPRESSORPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.ulOnOff)
|
||||
{
|
||||
set_efx_on_off();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxCompressorEffectDirtyFlags{};
|
||||
mEaxProps = props;
|
||||
mAlProps = CompressorProps{props.ulOnOff != 0};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::set(const EaxEaxCall& eax_call)
|
||||
void EaxCompressorCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
props = EAXAGCCOMPRESSORPROPERTIES{EAXAGCCOMPRESSOR_DEFAULTONOFF};
|
||||
}
|
||||
|
||||
void EaxCompressorCommitter::Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAGCCOMPRESSOR_NONE:
|
||||
break;
|
||||
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXAGCCOMPRESSOR_ONOFF:
|
||||
defer_on_off(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxCompressorEffectException{"Unsupported property id."};
|
||||
case EAXAGCCOMPRESSOR_NONE: break;
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.ulOnOff); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_compressor_effect()
|
||||
void EaxCompressorCommitter::Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props)
|
||||
{
|
||||
return std::make_unique<EaxCompressorEffect>();
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAGCCOMPRESSOR_NONE: break;
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.ulOnOff); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
@@ -1,93 +1,117 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "alc/inprogext.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "effects.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Convolution_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Convolution_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Convolution_setParami(props, param, vals[0]);
|
||||
}
|
||||
}
|
||||
void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Convolution_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Convolution_setParamf(props, param, vals[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void Convolution_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Convolution_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Convolution_getParami(props, param, vals);
|
||||
}
|
||||
}
|
||||
void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Convolution_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Convolution_getParamf(props, param, vals);
|
||||
}
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
ConvolutionProps props{};
|
||||
props.OrientAt = {0.0f, 0.0f, -1.0f};
|
||||
props.OrientUp = {0.0f, 1.0f, 0.0f};
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Convolution);
|
||||
|
||||
const EffectProps ConvolutionEffectProps{genDefaultProps()};
|
||||
|
||||
void EffectHandler::SetParami(ConvolutionProps& /*props*/, ALenum param, int /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::SetParamiv(ConvolutionProps &props, ALenum param, const int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SetParami(props, param, *vals);
|
||||
}
|
||||
}
|
||||
void EffectHandler::SetParamf(ConvolutionProps& /*props*/, ALenum param, float /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::SetParamfv(ConvolutionProps &props, ALenum param, const float *values)
|
||||
{
|
||||
static constexpr auto finite_checker = [](float val) -> bool { return std::isfinite(val); };
|
||||
al::span<const float> vals;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CONVOLUTION_ORIENTATION_SOFT:
|
||||
vals = {values, 6_uz};
|
||||
if(!std::all_of(vals.cbegin(), vals.cend(), finite_checker))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Property 0x%04x value out of range", param};
|
||||
|
||||
std::copy_n(vals.cbegin(), props.OrientAt.size(), props.OrientAt.begin());
|
||||
std::copy_n(vals.cbegin()+3, props.OrientUp.size(), props.OrientUp.begin());
|
||||
break;
|
||||
|
||||
default:
|
||||
SetParamf(props, param, *values);
|
||||
}
|
||||
}
|
||||
|
||||
void EffectHandler::GetParami(const ConvolutionProps& /*props*/, ALenum param, int* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamiv(const ConvolutionProps &props, ALenum param, int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
GetParami(props, param, vals);
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamf(const ConvolutionProps& /*props*/, ALenum param, float* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamfv(const ConvolutionProps &props, ALenum param, float *values)
|
||||
{
|
||||
al::span<float> vals;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CONVOLUTION_ORIENTATION_SOFT:
|
||||
vals = {values, 6_uz};
|
||||
std::copy(props.OrientAt.cbegin(), props.OrientAt.cend(), vals.begin());
|
||||
std::copy(props.OrientUp.cbegin(), props.OrientUp.cend(), vals.begin()+3);
|
||||
break;
|
||||
|
||||
default:
|
||||
GetParamf(props, param, values);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,61 +12,109 @@
|
||||
|
||||
namespace {
|
||||
|
||||
void Dedicated_setParami(EffectProps*, ALenum param, int)
|
||||
constexpr EffectProps genDefaultDialogProps() noexcept
|
||||
{
|
||||
DedicatedDialogProps props{};
|
||||
props.Gain = 1.0f;
|
||||
return props;
|
||||
}
|
||||
|
||||
constexpr EffectProps genDefaultLfeProps() noexcept
|
||||
{
|
||||
DedicatedLfeProps props{};
|
||||
props.Gain = 1.0f;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()};
|
||||
|
||||
void EffectHandler::SetParami(DedicatedDialogProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void Dedicated_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void EffectHandler::SetParamiv(DedicatedDialogProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Dedicated_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void EffectHandler::SetParamf(DedicatedDialogProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
if(!(val >= 0.0f && std::isfinite(val)))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
|
||||
props->Dedicated.Gain = val;
|
||||
props.Gain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Dedicated_setParamf(props, param, vals[0]); }
|
||||
void EffectHandler::SetParamfv(DedicatedDialogProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Dedicated_getParami(const EffectProps*, ALenum param, int*)
|
||||
void EffectHandler::GetParami(const DedicatedDialogProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void Dedicated_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void EffectHandler::GetParamiv(const DedicatedDialogProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void EffectHandler::GetParamf(const DedicatedDialogProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN: *val = props.Gain; break;
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamfv(const DedicatedDialogProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
|
||||
const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()};
|
||||
|
||||
void EffectHandler::SetParami(DedicatedLfeProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void EffectHandler::SetParamiv(DedicatedLfeProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void EffectHandler::SetParamf(DedicatedLfeProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
*val = props->Dedicated.Gain;
|
||||
if(!(val >= 0.0f && std::isfinite(val)))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
|
||||
props.Gain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Dedicated_getParamf(props, param, vals); }
|
||||
void EffectHandler::SetParamfv(DedicatedLfeProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
void EffectHandler::GetParami(const DedicatedLfeProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void EffectHandler::GetParamiv(const DedicatedLfeProps&, ALenum param, int*)
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Dedicated.Gain = 1.0f;
|
||||
return props;
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Dedicated);
|
||||
|
||||
const EffectProps DedicatedEffectProps{genDefaultProps()};
|
||||
void EffectHandler::GetParamf(const DedicatedLfeProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN: *val = props.Gain; break;
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamfv(const DedicatedLfeProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
@@ -9,563 +9,249 @@
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Distortion_setParami(EffectProps*, ALenum param, int)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
DistortionProps props{};
|
||||
props.Edge = AL_DISTORTION_DEFAULT_EDGE;
|
||||
props.Gain = AL_DISTORTION_DEFAULT_GAIN;
|
||||
props.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
|
||||
props.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
|
||||
props.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps DistortionEffectProps{genDefaultProps()};
|
||||
|
||||
void EffectHandler::SetParami(DistortionProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
||||
void Distortion_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void EffectHandler::SetParamiv(DistortionProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Distortion_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void EffectHandler::SetParamf(DistortionProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"};
|
||||
props->Distortion.Edge = val;
|
||||
props.Edge = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"};
|
||||
props->Distortion.Gain = val;
|
||||
props.Gain = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"};
|
||||
props->Distortion.LowpassCutoff = val;
|
||||
props.LowpassCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"};
|
||||
props->Distortion.EQCenter = val;
|
||||
props.EQCenter = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"};
|
||||
props->Distortion.EQBandwidth = val;
|
||||
props.EQBandwidth = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Distortion_setParamf(props, param, vals[0]); }
|
||||
void EffectHandler::SetParamfv(DistortionProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Distortion_getParami(const EffectProps*, ALenum param, int*)
|
||||
void EffectHandler::GetParami(const DistortionProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
||||
void Distortion_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void EffectHandler::GetParamiv(const DistortionProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Distortion_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void EffectHandler::GetParamf(const DistortionProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
*val = props->Distortion.Edge;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
*val = props->Distortion.Gain;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
*val = props->Distortion.LowpassCutoff;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
*val = props->Distortion.EQCenter;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
*val = props->Distortion.EQBandwidth;
|
||||
break;
|
||||
case AL_DISTORTION_EDGE: *val = props.Edge; break;
|
||||
case AL_DISTORTION_GAIN: *val = props.Gain; break;
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; break;
|
||||
case AL_DISTORTION_EQCENTER: *val = props.EQCenter; break;
|
||||
case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Distortion_getParamf(props, param, vals); }
|
||||
void EffectHandler::GetParamfv(const DistortionProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
|
||||
props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
|
||||
props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
|
||||
props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
|
||||
props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Distortion);
|
||||
|
||||
const EffectProps DistortionEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxDistortionEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
using DistortionCommitter = EaxCommitter<EaxDistortionCommitter>;
|
||||
|
||||
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();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
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();
|
||||
|
||||
void 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);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxDistortionEffect
|
||||
|
||||
|
||||
class EaxDistortionEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxDistortionEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_DISTORTION_EFFECT", message}
|
||||
struct EdgeValidator {
|
||||
void operator()(float flEdge) const
|
||||
{
|
||||
eax_validate_range<DistortionCommitter::Exception>(
|
||||
"Edge",
|
||||
flEdge,
|
||||
EAXDISTORTION_MINEDGE,
|
||||
EAXDISTORTION_MAXEDGE);
|
||||
}
|
||||
}; // EaxDistortionEffectException
|
||||
}; // EdgeValidator
|
||||
|
||||
|
||||
EaxDistortionEffect::EaxDistortionEffect()
|
||||
: EaxEffect{AL_EFFECT_DISTORTION}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
struct GainValidator {
|
||||
void operator()(long lGain) const
|
||||
{
|
||||
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."};
|
||||
eax_validate_range<DistortionCommitter::Exception>(
|
||||
"Gain",
|
||||
lGain,
|
||||
EAXDISTORTION_MINGAIN,
|
||||
EAXDISTORTION_MAXGAIN);
|
||||
}
|
||||
}
|
||||
}; // GainValidator
|
||||
|
||||
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{})
|
||||
struct LowPassCutOffValidator {
|
||||
void operator()(float flLowPassCutOff) const
|
||||
{
|
||||
eax_validate_range<DistortionCommitter::Exception>(
|
||||
"Low-pass Cut-off",
|
||||
flLowPassCutOff,
|
||||
EAXDISTORTION_MINLOWPASSCUTOFF,
|
||||
EAXDISTORTION_MAXLOWPASSCUTOFF);
|
||||
}
|
||||
}; // LowPassCutOffValidator
|
||||
|
||||
struct EqCenterValidator {
|
||||
void operator()(float flEQCenter) const
|
||||
{
|
||||
eax_validate_range<DistortionCommitter::Exception>(
|
||||
"EQ Center",
|
||||
flEQCenter,
|
||||
EAXDISTORTION_MINEQCENTER,
|
||||
EAXDISTORTION_MAXEQCENTER);
|
||||
}
|
||||
}; // EqCenterValidator
|
||||
|
||||
struct EqBandwidthValidator {
|
||||
void operator()(float flEQBandwidth) const
|
||||
{
|
||||
eax_validate_range<DistortionCommitter::Exception>(
|
||||
"EQ Bandwidth",
|
||||
flEQBandwidth,
|
||||
EAXDISTORTION_MINEQBANDWIDTH,
|
||||
EAXDISTORTION_MAXEQBANDWIDTH);
|
||||
}
|
||||
}; // EqBandwidthValidator
|
||||
|
||||
struct AllValidator {
|
||||
void operator()(const EAXDISTORTIONPROPERTIES& all) const
|
||||
{
|
||||
EdgeValidator{}(all.flEdge);
|
||||
GainValidator{}(all.lGain);
|
||||
LowPassCutOffValidator{}(all.flLowPassCutOff);
|
||||
EqCenterValidator{}(all.flEQCenter);
|
||||
EqBandwidthValidator{}(all.flEQBandwidth);
|
||||
}
|
||||
}; // AllValidator
|
||||
|
||||
} // namespace
|
||||
|
||||
template<>
|
||||
struct DistortionCommitter::Exception : public EaxException {
|
||||
explicit Exception(const char *message) : EaxException{"EAX_DISTORTION_EFFECT", message}
|
||||
{ }
|
||||
};
|
||||
|
||||
template<>
|
||||
[[noreturn]] void DistortionCommitter::fail(const char *message)
|
||||
{
|
||||
throw Exception{message};
|
||||
}
|
||||
|
||||
bool EaxDistortionCommitter::commit(const EAXDISTORTIONPROPERTIES &props)
|
||||
{
|
||||
if(auto *cur = std::get_if<EAXDISTORTIONPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
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{};
|
||||
mEaxProps = props;
|
||||
mAlProps = [&]{
|
||||
DistortionProps ret{};
|
||||
ret.Edge = props.flEdge;
|
||||
ret.Gain = level_mb_to_gain(static_cast<float>(props.lGain));
|
||||
ret.LowpassCutoff = props.flLowPassCutOff;
|
||||
ret.EQCenter = props.flEQCenter;
|
||||
ret.EQBandwidth = props.flEdge;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::set(const EaxEaxCall& eax_call)
|
||||
void EaxDistortionCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
static constexpr EAXDISTORTIONPROPERTIES defprops{[]
|
||||
{
|
||||
case EAXDISTORTION_NONE:
|
||||
break;
|
||||
EAXDISTORTIONPROPERTIES ret{};
|
||||
ret.flEdge = EAXDISTORTION_DEFAULTEDGE;
|
||||
ret.lGain = EAXDISTORTION_DEFAULTGAIN;
|
||||
ret.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
|
||||
ret.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
|
||||
ret.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
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."};
|
||||
void EaxDistortionCommitter::Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXDISTORTION_NONE: break;
|
||||
case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXDISTORTION_EDGE: call.set_value<Exception>(props.flEdge); break;
|
||||
case EAXDISTORTION_GAIN: call.set_value<Exception>(props.lGain); break;
|
||||
case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.flLowPassCutOff); break;
|
||||
case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.flEQCenter); break;
|
||||
case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.flEQBandwidth); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_distortion_effect()
|
||||
void EaxDistortionCommitter::Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props)
|
||||
{
|
||||
return std::make_unique<EaxDistortionEffect>();
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXDISTORTION_NONE: break;
|
||||
case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.flEdge); break;
|
||||
case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.lGain); break;
|
||||
case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.flLowPassCutOff); break;
|
||||
case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.flEQCenter); break;
|
||||
case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.flEQBandwidth); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
@@ -20,550 +20,235 @@ 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)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EchoProps props{};
|
||||
props.Delay = AL_ECHO_DEFAULT_DELAY;
|
||||
props.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
|
||||
props.Damping = AL_ECHO_DEFAULT_DAMPING;
|
||||
props.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
|
||||
props.Spread = AL_ECHO_DEFAULT_SPREAD;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps EchoEffectProps{genDefaultProps()};
|
||||
|
||||
void EffectHandler::SetParami(EchoProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
||||
void Echo_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void EffectHandler::SetParamiv(EchoProps&, 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)
|
||||
void EffectHandler::SetParamf(EchoProps &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;
|
||||
props.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;
|
||||
props.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;
|
||||
props.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;
|
||||
props.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;
|
||||
props.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 EffectHandler::SetParamfv(EchoProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Echo_getParami(const EffectProps*, ALenum param, int*)
|
||||
void EffectHandler::GetParami(const EchoProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
||||
void Echo_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void EffectHandler::GetParamiv(const EchoProps&, 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)
|
||||
void EffectHandler::GetParamf(const EchoProps &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;
|
||||
case AL_ECHO_DELAY: *val = props.Delay; break;
|
||||
case AL_ECHO_LRDELAY: *val = props.LRDelay; break;
|
||||
case AL_ECHO_DAMPING: *val = props.Damping; break;
|
||||
case AL_ECHO_FEEDBACK: *val = props.Feedback; break;
|
||||
case AL_ECHO_SPREAD: *val = props.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); }
|
||||
void EffectHandler::GetParamfv(const EchoProps &props, ALenum param, float *vals)
|
||||
{ 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;
|
||||
using EchoCommitter = EaxCommitter<EaxEchoCommitter>;
|
||||
|
||||
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}
|
||||
struct DelayValidator {
|
||||
void operator()(float flDelay) const
|
||||
{
|
||||
eax_validate_range<EchoCommitter::Exception>(
|
||||
"Delay",
|
||||
flDelay,
|
||||
EAXECHO_MINDELAY,
|
||||
EAXECHO_MAXDELAY);
|
||||
}
|
||||
}; // EaxEchoEffectException
|
||||
}; // DelayValidator
|
||||
|
||||
|
||||
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())
|
||||
struct LrDelayValidator {
|
||||
void operator()(float flLRDelay) const
|
||||
{
|
||||
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."};
|
||||
eax_validate_range<EchoCommitter::Exception>(
|
||||
"LR Delay",
|
||||
flLRDelay,
|
||||
EAXECHO_MINLRDELAY,
|
||||
EAXECHO_MAXLRDELAY);
|
||||
}
|
||||
}
|
||||
}; // LrDelayValidator
|
||||
|
||||
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{})
|
||||
struct DampingValidator {
|
||||
void operator()(float flDamping) const
|
||||
{
|
||||
eax_validate_range<EchoCommitter::Exception>(
|
||||
"Damping",
|
||||
flDamping,
|
||||
EAXECHO_MINDAMPING,
|
||||
EAXECHO_MAXDAMPING);
|
||||
}
|
||||
}; // DampingValidator
|
||||
|
||||
struct FeedbackValidator {
|
||||
void operator()(float flFeedback) const
|
||||
{
|
||||
eax_validate_range<EchoCommitter::Exception>(
|
||||
"Feedback",
|
||||
flFeedback,
|
||||
EAXECHO_MINFEEDBACK,
|
||||
EAXECHO_MAXFEEDBACK);
|
||||
}
|
||||
}; // FeedbackValidator
|
||||
|
||||
struct SpreadValidator {
|
||||
void operator()(float flSpread) const
|
||||
{
|
||||
eax_validate_range<EchoCommitter::Exception>(
|
||||
"Spread",
|
||||
flSpread,
|
||||
EAXECHO_MINSPREAD,
|
||||
EAXECHO_MAXSPREAD);
|
||||
}
|
||||
}; // SpreadValidator
|
||||
|
||||
struct AllValidator {
|
||||
void operator()(const EAXECHOPROPERTIES& all) const
|
||||
{
|
||||
DelayValidator{}(all.flDelay);
|
||||
LrDelayValidator{}(all.flLRDelay);
|
||||
DampingValidator{}(all.flDamping);
|
||||
FeedbackValidator{}(all.flFeedback);
|
||||
SpreadValidator{}(all.flSpread);
|
||||
}
|
||||
}; // AllValidator
|
||||
|
||||
} // namespace
|
||||
|
||||
template<>
|
||||
struct EchoCommitter::Exception : public EaxException {
|
||||
explicit Exception(const char* message) : EaxException{"EAX_ECHO_EFFECT", message}
|
||||
{ }
|
||||
};
|
||||
|
||||
template<>
|
||||
[[noreturn]] void EchoCommitter::fail(const char *message)
|
||||
{
|
||||
throw Exception{message};
|
||||
}
|
||||
|
||||
bool EaxEchoCommitter::commit(const EAXECHOPROPERTIES &props)
|
||||
{
|
||||
if(auto *cur = std::get_if<EAXECHOPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
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{};
|
||||
mEaxProps = props;
|
||||
mAlProps = [&]{
|
||||
EchoProps ret{};
|
||||
ret.Delay = props.flDelay;
|
||||
ret.LRDelay = props.flLRDelay;
|
||||
ret.Damping = props.flDamping;
|
||||
ret.Feedback = props.flFeedback;
|
||||
ret.Spread = props.flSpread;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxEchoEffect::set(const EaxEaxCall& eax_call)
|
||||
void EaxEchoCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
static constexpr EAXECHOPROPERTIES defprops{[]
|
||||
{
|
||||
case EAXECHO_NONE:
|
||||
break;
|
||||
EAXECHOPROPERTIES ret{};
|
||||
ret.flDelay = EAXECHO_DEFAULTDELAY;
|
||||
ret.flLRDelay = EAXECHO_DEFAULTLRDELAY;
|
||||
ret.flDamping = EAXECHO_DEFAULTDAMPING;
|
||||
ret.flFeedback = EAXECHO_DEFAULTFEEDBACK;
|
||||
ret.flSpread = EAXECHO_DEFAULTSPREAD;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
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."};
|
||||
void EaxEchoCommitter::Get(const EaxCall &call, const EAXECHOPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXECHO_NONE: break;
|
||||
case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXECHO_DELAY: call.set_value<Exception>(props.flDelay); break;
|
||||
case EAXECHO_LRDELAY: call.set_value<Exception>(props.flLRDelay); break;
|
||||
case EAXECHO_DAMPING: call.set_value<Exception>(props.flDamping); break;
|
||||
case EAXECHO_FEEDBACK: call.set_value<Exception>(props.flFeedback); break;
|
||||
case EAXECHO_SPREAD: call.set_value<Exception>(props.flSpread); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_echo_effect()
|
||||
void EaxEchoCommitter::Set(const EaxCall &call, EAXECHOPROPERTIES &props)
|
||||
{
|
||||
return std::make_unique<EaxEchoEffect>();
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXECHO_NONE: break;
|
||||
case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXECHO_DELAY: defer<DelayValidator>(call, props.flDelay); break;
|
||||
case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.flLRDelay); break;
|
||||
case EAXECHO_DAMPING: defer<DampingValidator>(call, props.flDamping); break;
|
||||
case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.flFeedback); break;
|
||||
case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.flSpread); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
@@ -1,66 +1,3 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
|
||||
#include "effects.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "AL/efx.h"
|
||||
|
||||
|
||||
EaxEffectUPtr eax_create_eax_effect(ALenum al_effect_type)
|
||||
{
|
||||
#define EAX_PREFIX "[EAX_MAKE_EAX_EFFECT] "
|
||||
|
||||
switch (al_effect_type)
|
||||
{
|
||||
case AL_EFFECT_NULL:
|
||||
return eax_create_eax_null_effect();
|
||||
|
||||
case AL_EFFECT_CHORUS:
|
||||
return eax_create_eax_chorus_effect();
|
||||
|
||||
case AL_EFFECT_DISTORTION:
|
||||
return eax_create_eax_distortion_effect();
|
||||
|
||||
case AL_EFFECT_ECHO:
|
||||
return eax_create_eax_echo_effect();
|
||||
|
||||
case AL_EFFECT_FLANGER:
|
||||
return eax_create_eax_flanger_effect();
|
||||
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER:
|
||||
return eax_create_eax_frequency_shifter_effect();
|
||||
|
||||
case AL_EFFECT_VOCAL_MORPHER:
|
||||
return eax_create_eax_vocal_morpher_effect();
|
||||
|
||||
case AL_EFFECT_PITCH_SHIFTER:
|
||||
return eax_create_eax_pitch_shifter_effect();
|
||||
|
||||
case AL_EFFECT_RING_MODULATOR:
|
||||
return eax_create_eax_ring_modulator_effect();
|
||||
|
||||
case AL_EFFECT_AUTOWAH:
|
||||
return eax_create_eax_auto_wah_effect();
|
||||
|
||||
case AL_EFFECT_COMPRESSOR:
|
||||
return eax_create_eax_compressor_effect();
|
||||
|
||||
case AL_EFFECT_EQUALIZER:
|
||||
return eax_create_eax_equalizer_effect();
|
||||
|
||||
case AL_EFFECT_EAXREVERB:
|
||||
return eax_create_eax_reverb_effect();
|
||||
|
||||
default:
|
||||
assert(false && "Unsupported AL effect type.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#undef EAX_PREFIX
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
@@ -1,51 +1,54 @@
|
||||
#ifndef AL_EFFECTS_EFFECTS_H
|
||||
#define AL_EFFECTS_EFFECTS_H
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "core/except.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "al/eax_effect.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
union EffectProps;
|
||||
#include "al/error.h"
|
||||
#include "core/effects/base.h"
|
||||
|
||||
|
||||
class effect_exception final : public al::base_exception {
|
||||
ALenum mErrorCode;
|
||||
struct EffectHandler {
|
||||
#define DECL_HANDLER(T) \
|
||||
static void SetParami(T &props, ALenum param, int val); \
|
||||
static void SetParamiv(T &props, ALenum param, const int *vals); \
|
||||
static void SetParamf(T &props, ALenum param, float val); \
|
||||
static void SetParamfv(T &props, ALenum param, const float *vals); \
|
||||
static void GetParami(const T &props, ALenum param, int *val); \
|
||||
static void GetParamiv(const T &props, ALenum param, int *vals); \
|
||||
static void GetParamf(const T &props, ALenum param, float *val); \
|
||||
static void GetParamfv(const T &props, ALenum param, float *vals);
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
effect_exception(ALenum code, const char *msg, ...);
|
||||
DECL_HANDLER(std::monostate)
|
||||
DECL_HANDLER(ReverbProps)
|
||||
DECL_HANDLER(ChorusProps)
|
||||
DECL_HANDLER(AutowahProps)
|
||||
DECL_HANDLER(CompressorProps)
|
||||
DECL_HANDLER(ConvolutionProps)
|
||||
DECL_HANDLER(DedicatedDialogProps)
|
||||
DECL_HANDLER(DedicatedLfeProps)
|
||||
DECL_HANDLER(DistortionProps)
|
||||
DECL_HANDLER(EchoProps)
|
||||
DECL_HANDLER(EqualizerProps)
|
||||
DECL_HANDLER(FlangerProps)
|
||||
DECL_HANDLER(FshifterProps)
|
||||
DECL_HANDLER(ModulatorProps)
|
||||
DECL_HANDLER(PshifterProps)
|
||||
DECL_HANDLER(VmorpherProps)
|
||||
#undef DECL_HANDLER
|
||||
|
||||
ALenum errorCode() const noexcept { return mErrorCode; }
|
||||
static void StdReverbSetParami(ReverbProps &props, ALenum param, int val);
|
||||
static void StdReverbSetParamiv(ReverbProps &props, ALenum param, const int *vals);
|
||||
static void StdReverbSetParamf(ReverbProps &props, ALenum param, float val);
|
||||
static void StdReverbSetParamfv(ReverbProps &props, ALenum param, const float *vals);
|
||||
static void StdReverbGetParami(const ReverbProps &props, ALenum param, int *val);
|
||||
static void StdReverbGetParamiv(const ReverbProps &props, ALenum param, int *vals);
|
||||
static void StdReverbGetParamf(const ReverbProps &props, ALenum param, float *val);
|
||||
static void StdReverbGetParamfv(const ReverbProps &props, ALenum param, float *vals);
|
||||
};
|
||||
|
||||
|
||||
struct EffectVtable {
|
||||
void (*const setParami)(EffectProps *props, ALenum param, int val);
|
||||
void (*const setParamiv)(EffectProps *props, ALenum param, const int *vals);
|
||||
void (*const setParamf)(EffectProps *props, ALenum param, float val);
|
||||
void (*const setParamfv)(EffectProps *props, ALenum param, const float *vals);
|
||||
|
||||
void (*const getParami)(const EffectProps *props, ALenum param, int *val);
|
||||
void (*const getParamiv)(const EffectProps *props, ALenum param, int *vals);
|
||||
void (*const getParamf)(const EffectProps *props, ALenum param, float *val);
|
||||
void (*const getParamfv)(const EffectProps *props, ALenum param, float *vals);
|
||||
};
|
||||
|
||||
#define DEFINE_ALEFFECT_VTABLE(T) \
|
||||
const EffectVtable T##EffectVtable = { \
|
||||
T##_setParami, T##_setParamiv, \
|
||||
T##_setParamf, T##_setParamfv, \
|
||||
T##_getParami, T##_getParamiv, \
|
||||
T##_getParamf, T##_getParamfv, \
|
||||
}
|
||||
using effect_exception = al::context_error;
|
||||
|
||||
|
||||
/* Default properties for the given effect types. */
|
||||
@@ -63,30 +66,8 @@ extern const EffectProps FshifterEffectProps;
|
||||
extern const EffectProps ModulatorEffectProps;
|
||||
extern const EffectProps PshifterEffectProps;
|
||||
extern const EffectProps VmorpherEffectProps;
|
||||
extern const EffectProps DedicatedEffectProps;
|
||||
extern const EffectProps DedicatedDialogEffectProps;
|
||||
extern const EffectProps DedicatedLfeEffectProps;
|
||||
extern const EffectProps ConvolutionEffectProps;
|
||||
|
||||
/* Vtables to get/set properties for the given effect types. */
|
||||
extern const EffectVtable NullEffectVtable;
|
||||
extern const EffectVtable ReverbEffectVtable;
|
||||
extern const EffectVtable StdReverbEffectVtable;
|
||||
extern const EffectVtable AutowahEffectVtable;
|
||||
extern const EffectVtable ChorusEffectVtable;
|
||||
extern const EffectVtable CompressorEffectVtable;
|
||||
extern const EffectVtable DistortionEffectVtable;
|
||||
extern const EffectVtable EchoEffectVtable;
|
||||
extern const EffectVtable EqualizerEffectVtable;
|
||||
extern const EffectVtable FlangerEffectVtable;
|
||||
extern const EffectVtable FshifterEffectVtable;
|
||||
extern const EffectVtable ModulatorEffectVtable;
|
||||
extern const EffectVtable PshifterEffectVtable;
|
||||
extern const EffectVtable VmorpherEffectVtable;
|
||||
extern const EffectVtable DedicatedEffectVtable;
|
||||
extern const EffectVtable ConvolutionEffectVtable;
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
EaxEffectUPtr eax_create_eax_effect(ALenum al_effect_type);
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
#endif /* AL_EFFECTS_EFFECTS_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,38 +1,37 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
|
||||
constexpr std::optional<FShifterDirection> DirectionFromEmum(ALenum value) noexcept
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN: return al::make_optional(FShifterDirection::Down);
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_UP: return al::make_optional(FShifterDirection::Up);
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_OFF: return al::make_optional(FShifterDirection::Off);
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN: return FShifterDirection::Down;
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_UP: return FShifterDirection::Up;
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_OFF: return FShifterDirection::Off;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
ALenum EnumFromDirection(FShifterDirection dir)
|
||||
constexpr ALenum EnumFromDirection(FShifterDirection dir)
|
||||
{
|
||||
switch(dir)
|
||||
{
|
||||
@@ -43,31 +42,26 @@ ALenum EnumFromDirection(FShifterDirection dir)
|
||||
throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast<int>(dir))};
|
||||
}
|
||||
|
||||
void Fshifter_setParamf(EffectProps *props, ALenum param, float val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"};
|
||||
props->Fshifter.Frequency = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
FshifterProps props{};
|
||||
props.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
|
||||
props.LeftDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION).value();
|
||||
props.RightDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION).value();
|
||||
return props;
|
||||
}
|
||||
void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Fshifter_setParamf(props, param, vals[0]); }
|
||||
|
||||
void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||
} // namespace
|
||||
|
||||
const EffectProps FshifterEffectProps{genDefaultProps()};
|
||||
|
||||
void EffectHandler::SetParami(FshifterProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||
if(auto diropt = DirectionFromEmum(val))
|
||||
props->Fshifter.LeftDirection = *diropt;
|
||||
props.LeftDirection = *diropt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE,
|
||||
"Unsupported frequency shifter left direction: 0x%04x", val};
|
||||
@@ -75,7 +69,7 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||
|
||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||
if(auto diropt = DirectionFromEmum(val))
|
||||
props->Fshifter.RightDirection = *diropt;
|
||||
props.RightDirection = *diropt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE,
|
||||
"Unsupported frequency shifter right direction: 0x%04x", val};
|
||||
@@ -86,33 +80,17 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||
"Invalid frequency shifter integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Fshifter_setParami(props, param, vals[0]); }
|
||||
void EffectHandler::SetParamiv(FshifterProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
|
||||
void Fshifter_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||
*val = EnumFromDirection(props->Fshifter.LeftDirection);
|
||||
break;
|
||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||
*val = EnumFromDirection(props->Fshifter.RightDirection);
|
||||
break;
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM,
|
||||
"Invalid frequency shifter integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Fshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Fshifter_getParami(props, param, vals); }
|
||||
|
||||
void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void EffectHandler::SetParamf(FshifterProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||
*val = props->Fshifter.Frequency;
|
||||
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"};
|
||||
props.Frequency = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -120,360 +98,170 @@ void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Fshifter_getParamf(props, param, vals); }
|
||||
void EffectHandler::SetParamfv(FshifterProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
void EffectHandler::GetParami(const FshifterProps &props, ALenum param, int *val)
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
|
||||
props.Fshifter.LeftDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION);
|
||||
props.Fshifter.RightDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION);
|
||||
return props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||
*val = EnumFromDirection(props.LeftDirection);
|
||||
break;
|
||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||
*val = EnumFromDirection(props.RightDirection);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM,
|
||||
"Invalid frequency shifter integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamiv(const FshifterProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
|
||||
} // namespace
|
||||
void EffectHandler::GetParamf(const FshifterProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||
*val = props.Frequency;
|
||||
break;
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Fshifter);
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamfv(const FshifterProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
const EffectProps FshifterEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxFrequencyShifterEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
using FrequencyShifterCommitter = EaxCommitter<EaxFrequencyShifterCommitter>;
|
||||
|
||||
struct EaxFrequencyShifterEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxFrequencyShifterEffectDirtyFlagsValue flFrequency : 1;
|
||||
EaxFrequencyShifterEffectDirtyFlagsValue ulLeftDirection : 1;
|
||||
EaxFrequencyShifterEffectDirtyFlagsValue ulRightDirection : 1;
|
||||
}; // EaxFrequencyShifterEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxFrequencyShifterEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxFrequencyShifterEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXFREQUENCYSHIFTERPROPERTIES eax_{};
|
||||
EAXFREQUENCYSHIFTERPROPERTIES eax_d_{};
|
||||
EaxFrequencyShifterEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_frequency();
|
||||
void set_efx_left_direction();
|
||||
void set_efx_right_direction();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_frequency(float flFrequency);
|
||||
void validate_left_direction(unsigned long ulLeftDirection);
|
||||
void validate_right_direction(unsigned long ulRightDirection);
|
||||
void validate_all(const EAXFREQUENCYSHIFTERPROPERTIES& all);
|
||||
|
||||
void defer_frequency(float flFrequency);
|
||||
void defer_left_direction(unsigned long ulLeftDirection);
|
||||
void defer_right_direction(unsigned long ulRightDirection);
|
||||
void defer_all(const EAXFREQUENCYSHIFTERPROPERTIES& all);
|
||||
|
||||
void defer_frequency(const EaxEaxCall& eax_call);
|
||||
void defer_left_direction(const EaxEaxCall& eax_call);
|
||||
void defer_right_direction(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxFrequencyShifterEffect
|
||||
|
||||
|
||||
class EaxFrequencyShifterEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxFrequencyShifterEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_FREQUENCY_SHIFTER_EFFECT", message}
|
||||
struct FrequencyValidator {
|
||||
void operator()(float flFrequency) const
|
||||
{
|
||||
eax_validate_range<FrequencyShifterCommitter::Exception>(
|
||||
"Frequency",
|
||||
flFrequency,
|
||||
EAXFREQUENCYSHIFTER_MINFREQUENCY,
|
||||
EAXFREQUENCYSHIFTER_MAXFREQUENCY);
|
||||
}
|
||||
}; // EaxFrequencyShifterEffectException
|
||||
}; // FrequencyValidator
|
||||
|
||||
|
||||
EaxFrequencyShifterEffect::EaxFrequencyShifterEffect()
|
||||
: EaxEffect{AL_EFFECT_FREQUENCY_SHIFTER}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
|
||||
eax_.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
|
||||
eax_.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set_efx_frequency()
|
||||
{
|
||||
const auto frequency = clamp(
|
||||
eax_.flFrequency,
|
||||
AL_FREQUENCY_SHIFTER_MIN_FREQUENCY,
|
||||
AL_FREQUENCY_SHIFTER_MAX_FREQUENCY);
|
||||
|
||||
al_effect_props_.Fshifter.Frequency = frequency;
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set_efx_left_direction()
|
||||
{
|
||||
const auto left_direction = clamp(
|
||||
static_cast<ALint>(eax_.ulLeftDirection),
|
||||
AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION,
|
||||
AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION);
|
||||
|
||||
const auto efx_left_direction = DirectionFromEmum(left_direction);
|
||||
assert(efx_left_direction.has_value());
|
||||
al_effect_props_.Fshifter.LeftDirection = *efx_left_direction;
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set_efx_right_direction()
|
||||
{
|
||||
const auto right_direction = clamp(
|
||||
static_cast<ALint>(eax_.ulRightDirection),
|
||||
AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION,
|
||||
AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION);
|
||||
|
||||
const auto efx_right_direction = DirectionFromEmum(right_direction);
|
||||
assert(efx_right_direction.has_value());
|
||||
al_effect_props_.Fshifter.RightDirection = *efx_right_direction;
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_frequency();
|
||||
set_efx_left_direction();
|
||||
set_efx_right_direction();
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
struct LeftDirectionValidator {
|
||||
void operator()(unsigned long ulLeftDirection) const
|
||||
{
|
||||
case EAXFREQUENCYSHIFTER_NONE:
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxFrequencyShifterEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY:
|
||||
eax_call.set_value<EaxFrequencyShifterEffectException>(eax_.flFrequency);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION:
|
||||
eax_call.set_value<EaxFrequencyShifterEffectException>(eax_.ulLeftDirection);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION:
|
||||
eax_call.set_value<EaxFrequencyShifterEffectException>(eax_.ulRightDirection);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxFrequencyShifterEffectException{"Unsupported property id."};
|
||||
eax_validate_range<FrequencyShifterCommitter::Exception>(
|
||||
"Left Direction",
|
||||
ulLeftDirection,
|
||||
EAXFREQUENCYSHIFTER_MINLEFTDIRECTION,
|
||||
EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION);
|
||||
}
|
||||
}
|
||||
}; // LeftDirectionValidator
|
||||
|
||||
void EaxFrequencyShifterEffect::validate_frequency(
|
||||
float flFrequency)
|
||||
{
|
||||
eax_validate_range<EaxFrequencyShifterEffectException>(
|
||||
"Frequency",
|
||||
flFrequency,
|
||||
EAXFREQUENCYSHIFTER_MINFREQUENCY,
|
||||
EAXFREQUENCYSHIFTER_MAXFREQUENCY);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::validate_left_direction(
|
||||
unsigned long ulLeftDirection)
|
||||
{
|
||||
eax_validate_range<EaxFrequencyShifterEffectException>(
|
||||
"Left Direction",
|
||||
ulLeftDirection,
|
||||
EAXFREQUENCYSHIFTER_MINLEFTDIRECTION,
|
||||
EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::validate_right_direction(
|
||||
unsigned long ulRightDirection)
|
||||
{
|
||||
eax_validate_range<EaxFrequencyShifterEffectException>(
|
||||
"Right Direction",
|
||||
ulRightDirection,
|
||||
EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION,
|
||||
EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::validate_all(
|
||||
const EAXFREQUENCYSHIFTERPROPERTIES& all)
|
||||
{
|
||||
validate_frequency(all.flFrequency);
|
||||
validate_left_direction(all.ulLeftDirection);
|
||||
validate_right_direction(all.ulRightDirection);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_frequency(
|
||||
float flFrequency)
|
||||
{
|
||||
eax_d_.flFrequency = flFrequency;
|
||||
eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_left_direction(
|
||||
unsigned long ulLeftDirection)
|
||||
{
|
||||
eax_d_.ulLeftDirection = ulLeftDirection;
|
||||
eax_dirty_flags_.ulLeftDirection = (eax_.ulLeftDirection != eax_d_.ulLeftDirection);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_right_direction(
|
||||
unsigned long ulRightDirection)
|
||||
{
|
||||
eax_d_.ulRightDirection = ulRightDirection;
|
||||
eax_dirty_flags_.ulRightDirection = (eax_.ulRightDirection != eax_d_.ulRightDirection);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_all(
|
||||
const EAXFREQUENCYSHIFTERPROPERTIES& all)
|
||||
{
|
||||
defer_frequency(all.flFrequency);
|
||||
defer_left_direction(all.ulLeftDirection);
|
||||
defer_right_direction(all.ulRightDirection);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_frequency(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& frequency =
|
||||
eax_call.get_value<
|
||||
EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::flFrequency)>();
|
||||
|
||||
validate_frequency(frequency);
|
||||
defer_frequency(frequency);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_left_direction(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& left_direction =
|
||||
eax_call.get_value<
|
||||
EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::ulLeftDirection)>();
|
||||
|
||||
validate_left_direction(left_direction);
|
||||
defer_left_direction(left_direction);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_right_direction(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& right_direction =
|
||||
eax_call.get_value<
|
||||
EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::ulRightDirection)>();
|
||||
|
||||
validate_right_direction(right_direction);
|
||||
defer_right_direction(right_direction);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<
|
||||
EaxFrequencyShifterEffectException, const EAXFREQUENCYSHIFTERPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxFrequencyShifterEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxFrequencyShifterEffectDirtyFlags{})
|
||||
struct RightDirectionValidator {
|
||||
void operator()(unsigned long ulRightDirection) const
|
||||
{
|
||||
eax_validate_range<FrequencyShifterCommitter::Exception>(
|
||||
"Right Direction",
|
||||
ulRightDirection,
|
||||
EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION,
|
||||
EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION);
|
||||
}
|
||||
}; // RightDirectionValidator
|
||||
|
||||
struct AllValidator {
|
||||
void operator()(const EAXFREQUENCYSHIFTERPROPERTIES& all) const
|
||||
{
|
||||
FrequencyValidator{}(all.flFrequency);
|
||||
LeftDirectionValidator{}(all.ulLeftDirection);
|
||||
RightDirectionValidator{}(all.ulRightDirection);
|
||||
}
|
||||
}; // AllValidator
|
||||
|
||||
} // namespace
|
||||
|
||||
template<>
|
||||
struct FrequencyShifterCommitter::Exception : public EaxException {
|
||||
explicit Exception(const char *message) : EaxException{"EAX_FREQUENCY_SHIFTER_EFFECT", message}
|
||||
{ }
|
||||
};
|
||||
|
||||
template<>
|
||||
[[noreturn]] void FrequencyShifterCommitter::fail(const char *message)
|
||||
{
|
||||
throw Exception{message};
|
||||
}
|
||||
|
||||
bool EaxFrequencyShifterCommitter::commit(const EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||
{
|
||||
if(auto *cur = std::get_if<EAXFREQUENCYSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
mEaxProps = props;
|
||||
|
||||
if (eax_dirty_flags_.flFrequency)
|
||||
auto get_direction = [](unsigned long dir) noexcept
|
||||
{
|
||||
set_efx_frequency();
|
||||
}
|
||||
if(dir == EAX_FREQUENCYSHIFTER_DOWN)
|
||||
return FShifterDirection::Down;
|
||||
if(dir == EAX_FREQUENCYSHIFTER_UP)
|
||||
return FShifterDirection::Up;
|
||||
return FShifterDirection::Off;
|
||||
};
|
||||
|
||||
if (eax_dirty_flags_.ulLeftDirection)
|
||||
{
|
||||
set_efx_left_direction();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.ulRightDirection)
|
||||
{
|
||||
set_efx_right_direction();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxFrequencyShifterEffectDirtyFlags{};
|
||||
mAlProps = [&]{
|
||||
FshifterProps ret{};
|
||||
ret.Frequency = props.flFrequency;
|
||||
ret.LeftDirection = get_direction(props.ulLeftDirection);
|
||||
ret.RightDirection = get_direction(props.ulRightDirection);
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set(const EaxEaxCall& eax_call)
|
||||
void EaxFrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
static constexpr EAXFREQUENCYSHIFTERPROPERTIES defprops{[]
|
||||
{
|
||||
case EAXFREQUENCYSHIFTER_NONE:
|
||||
break;
|
||||
EAXFREQUENCYSHIFTERPROPERTIES ret{};
|
||||
ret.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
|
||||
ret.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
|
||||
ret.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY:
|
||||
defer_frequency(eax_call);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION:
|
||||
defer_left_direction(eax_call);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION:
|
||||
defer_right_direction(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxFrequencyShifterEffectException{"Unsupported property id."};
|
||||
void EaxFrequencyShifterCommitter::Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXFREQUENCYSHIFTER_NONE: break;
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.ulLeftDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.ulRightDirection); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_frequency_shifter_effect()
|
||||
void EaxFrequencyShifterCommitter::Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||
{
|
||||
return std::make_unique<EaxFrequencyShifterEffect>();
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXFREQUENCYSHIFTER_NONE: break;
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.ulLeftDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.ulRightDirection); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
|
||||
constexpr std::optional<ModulatorWaveform> WaveformFromEmum(ALenum value) noexcept
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case AL_RING_MODULATOR_SINUSOID: return al::make_optional(ModulatorWaveform::Sinusoid);
|
||||
case AL_RING_MODULATOR_SAWTOOTH: return al::make_optional(ModulatorWaveform::Sawtooth);
|
||||
case AL_RING_MODULATOR_SQUARE: return al::make_optional(ModulatorWaveform::Square);
|
||||
case AL_RING_MODULATOR_SINUSOID: return ModulatorWaveform::Sinusoid;
|
||||
case AL_RING_MODULATOR_SAWTOOTH: return ModulatorWaveform::Sawtooth;
|
||||
case AL_RING_MODULATOR_SQUARE: return ModulatorWaveform::Square;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
ALenum EnumFromWaveform(ModulatorWaveform type)
|
||||
constexpr ALenum EnumFromWaveform(ModulatorWaveform type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
@@ -44,40 +43,31 @@ ALenum EnumFromWaveform(ModulatorWaveform type)
|
||||
std::to_string(static_cast<int>(type))};
|
||||
}
|
||||
|
||||
void Modulator_setParamf(EffectProps *props, ALenum param, float val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
|
||||
props->Modulator.Frequency = val;
|
||||
break;
|
||||
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
|
||||
props->Modulator.HighPassCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
||||
}
|
||||
ModulatorProps props{};
|
||||
props.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
|
||||
props.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
|
||||
props.Waveform = WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM).value();
|
||||
return props;
|
||||
}
|
||||
void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Modulator_setParamf(props, param, vals[0]); }
|
||||
void Modulator_setParami(EffectProps *props, ALenum param, int val)
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps ModulatorEffectProps{genDefaultProps()};
|
||||
|
||||
void EffectHandler::SetParami(ModulatorProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
Modulator_setParamf(props, param, static_cast<float>(val));
|
||||
SetParamf(props, param, static_cast<float>(val));
|
||||
break;
|
||||
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
if(auto formopt = WaveformFromEmum(val))
|
||||
props->Modulator.Waveform = *formopt;
|
||||
props.Waveform = *formopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
|
||||
break;
|
||||
@@ -87,396 +77,189 @@ void Modulator_setParami(EffectProps *props, ALenum param, int val)
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Modulator_setParami(props, param, vals[0]); }
|
||||
void EffectHandler::SetParamiv(ModulatorProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
|
||||
void Modulator_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void EffectHandler::SetParamf(ModulatorProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
*val = static_cast<int>(props->Modulator.Frequency);
|
||||
break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
*val = static_cast<int>(props->Modulator.HighPassCutoff);
|
||||
break;
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
*val = EnumFromWaveform(props->Modulator.Waveform);
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
|
||||
props.Frequency = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Modulator_getParami(props, param, vals); }
|
||||
void Modulator_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
*val = props->Modulator.Frequency;
|
||||
break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
*val = props->Modulator.HighPassCutoff;
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
|
||||
props.HighPassCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Modulator_getParamf(props, param, vals); }
|
||||
void EffectHandler::SetParamfv(ModulatorProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
void EffectHandler::GetParami(const ModulatorProps &props, ALenum param, int *val)
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
|
||||
props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
|
||||
props.Modulator.Waveform = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM);
|
||||
return props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY: *val = static_cast<int>(props.Frequency); break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast<int>(props.HighPassCutoff); break;
|
||||
case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamiv(const ModulatorProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
void EffectHandler::GetParamf(const ModulatorProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY: *val = props.Frequency; break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = props.HighPassCutoff; break;
|
||||
|
||||
} // namespace
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamfv(const ModulatorProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Modulator);
|
||||
|
||||
const EffectProps ModulatorEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxRingModulatorEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
using ModulatorCommitter = EaxCommitter<EaxModulatorCommitter>;
|
||||
|
||||
struct EaxRingModulatorEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxRingModulatorEffectDirtyFlagsValue flFrequency : 1;
|
||||
EaxRingModulatorEffectDirtyFlagsValue flHighPassCutOff : 1;
|
||||
EaxRingModulatorEffectDirtyFlagsValue ulWaveform : 1;
|
||||
}; // EaxPitchShifterEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxRingModulatorEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxRingModulatorEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
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();
|
||||
|
||||
void 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);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxRingModulatorEffect
|
||||
|
||||
|
||||
class EaxRingModulatorEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxRingModulatorEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_RING_MODULATOR_EFFECT", message}
|
||||
struct FrequencyValidator {
|
||||
void operator()(float flFrequency) const
|
||||
{
|
||||
eax_validate_range<ModulatorCommitter::Exception>(
|
||||
"Frequency",
|
||||
flFrequency,
|
||||
EAXRINGMODULATOR_MINFREQUENCY,
|
||||
EAXRINGMODULATOR_MAXFREQUENCY);
|
||||
}
|
||||
}; // EaxRingModulatorEffectException
|
||||
}; // FrequencyValidator
|
||||
|
||||
|
||||
EaxRingModulatorEffect::EaxRingModulatorEffect()
|
||||
: EaxEffect{AL_EFFECT_RING_MODULATOR}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
struct HighPassCutOffValidator {
|
||||
void operator()(float flHighPassCutOff) const
|
||||
{
|
||||
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."};
|
||||
eax_validate_range<ModulatorCommitter::Exception>(
|
||||
"High-Pass Cutoff",
|
||||
flHighPassCutOff,
|
||||
EAXRINGMODULATOR_MINHIGHPASSCUTOFF,
|
||||
EAXRINGMODULATOR_MAXHIGHPASSCUTOFF);
|
||||
}
|
||||
}
|
||||
}; // HighPassCutOffValidator
|
||||
|
||||
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{})
|
||||
struct WaveformValidator {
|
||||
void operator()(unsigned long ulWaveform) const
|
||||
{
|
||||
eax_validate_range<ModulatorCommitter::Exception>(
|
||||
"Waveform",
|
||||
ulWaveform,
|
||||
EAXRINGMODULATOR_MINWAVEFORM,
|
||||
EAXRINGMODULATOR_MAXWAVEFORM);
|
||||
}
|
||||
}; // WaveformValidator
|
||||
|
||||
struct AllValidator {
|
||||
void operator()(const EAXRINGMODULATORPROPERTIES& all) const
|
||||
{
|
||||
FrequencyValidator{}(all.flFrequency);
|
||||
HighPassCutOffValidator{}(all.flHighPassCutOff);
|
||||
WaveformValidator{}(all.ulWaveform);
|
||||
}
|
||||
}; // AllValidator
|
||||
|
||||
} // namespace
|
||||
|
||||
template<>
|
||||
struct ModulatorCommitter::Exception : public EaxException {
|
||||
explicit Exception(const char *message) : EaxException{"EAX_RING_MODULATOR_EFFECT", message}
|
||||
{ }
|
||||
};
|
||||
|
||||
template<>
|
||||
[[noreturn]] void ModulatorCommitter::fail(const char *message)
|
||||
{
|
||||
throw Exception{message};
|
||||
}
|
||||
|
||||
bool EaxModulatorCommitter::commit(const EAXRINGMODULATORPROPERTIES &props)
|
||||
{
|
||||
if(auto *cur = std::get_if<EAXRINGMODULATORPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
mEaxProps = props;
|
||||
|
||||
if (eax_dirty_flags_.flFrequency)
|
||||
auto get_waveform = [](unsigned long form)
|
||||
{
|
||||
set_efx_frequency();
|
||||
}
|
||||
if(form == EAX_RINGMODULATOR_SINUSOID)
|
||||
return ModulatorWaveform::Sinusoid;
|
||||
if(form == EAX_RINGMODULATOR_SAWTOOTH)
|
||||
return ModulatorWaveform::Sawtooth;
|
||||
if(form == EAX_RINGMODULATOR_SQUARE)
|
||||
return ModulatorWaveform::Square;
|
||||
return ModulatorWaveform::Sinusoid;
|
||||
};
|
||||
|
||||
if (eax_dirty_flags_.flHighPassCutOff)
|
||||
{
|
||||
set_efx_high_pass_cutoff();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.ulWaveform)
|
||||
{
|
||||
set_efx_waveform();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxRingModulatorEffectDirtyFlags{};
|
||||
mAlProps = [&]{
|
||||
ModulatorProps ret{};
|
||||
ret.Frequency = props.flFrequency;
|
||||
ret.HighPassCutoff = props.flHighPassCutOff;
|
||||
ret.Waveform = get_waveform(props.ulWaveform);
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::set(const EaxEaxCall& eax_call)
|
||||
void EaxModulatorCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
switch (eax_call.get_property_id())
|
||||
static constexpr EAXRINGMODULATORPROPERTIES defprops{[]
|
||||
{
|
||||
case EAXRINGMODULATOR_NONE:
|
||||
break;
|
||||
EAXRINGMODULATORPROPERTIES ret{};
|
||||
ret.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
|
||||
ret.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
|
||||
ret.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
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."};
|
||||
void EaxModulatorCommitter::Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXRINGMODULATOR_NONE: break;
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.flHighPassCutOff); break;
|
||||
case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_ring_modulator_effect()
|
||||
void EaxModulatorCommitter::Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props)
|
||||
{
|
||||
return std::make_unique<EaxRingModulatorEffect>();
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXRINGMODULATOR_NONE: break;
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.flHighPassCutOff); break;
|
||||
case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
@@ -8,145 +8,136 @@
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Null_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Null_setParami(props, param, vals[0]);
|
||||
}
|
||||
}
|
||||
void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Null_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Null_setParamf(props, param, vals[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Null_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Null_getParami(props, param, vals);
|
||||
}
|
||||
}
|
||||
void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Null_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Null_getParamf(props, param, vals);
|
||||
}
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
return props;
|
||||
return std::monostate{};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Null);
|
||||
|
||||
const EffectProps NullEffectProps{genDefaultProps()};
|
||||
|
||||
void EffectHandler::SetParami(std::monostate& /*props*/, ALenum param, int /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::SetParamiv(std::monostate &props, ALenum param, const int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SetParami(props, param, *vals);
|
||||
}
|
||||
}
|
||||
void EffectHandler::SetParamf(std::monostate& /*props*/, ALenum param, float /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::SetParamfv(std::monostate &props, ALenum param, const float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SetParamf(props, param, *vals);
|
||||
}
|
||||
}
|
||||
|
||||
void EffectHandler::GetParami(const std::monostate& /*props*/, ALenum param, int* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamiv(const std::monostate &props, ALenum param, int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
GetParami(props, param, vals);
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamf(const std::monostate& /*props*/, ALenum param, float* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void EffectHandler::GetParamfv(const std::monostate &props, ALenum param, float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
GetParamf(props, param, vals);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
class EaxNullEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxNullEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
}; // EaxNullEffect
|
||||
|
||||
|
||||
class EaxNullEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxNullEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_NULL_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxNullEffectException
|
||||
|
||||
|
||||
EaxNullEffect::EaxNullEffect()
|
||||
: EaxEffect{AL_EFFECT_NULL}
|
||||
{
|
||||
}
|
||||
|
||||
void EaxNullEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
if(eax_call.get_property_id() != 0)
|
||||
throw EaxNullEffectException{"Unsupported property id."};
|
||||
}
|
||||
|
||||
bool EaxNullEffect::apply_deferred()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
using NullCommitter = EaxCommitter<EaxNullCommitter>;
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_null_effect()
|
||||
template<>
|
||||
struct NullCommitter::Exception : public EaxException
|
||||
{
|
||||
return std::make_unique<EaxNullEffect>();
|
||||
explicit Exception(const char *message) : EaxException{"EAX_NULL_EFFECT", message}
|
||||
{ }
|
||||
};
|
||||
|
||||
template<>
|
||||
[[noreturn]] void NullCommitter::fail(const char *message)
|
||||
{
|
||||
throw Exception{message};
|
||||
}
|
||||
|
||||
bool EaxNullCommitter::commit(const std::monostate &props)
|
||||
{
|
||||
const bool ret{std::holds_alternative<std::monostate>(mEaxProps)};
|
||||
mEaxProps = props;
|
||||
mAlProps = std::monostate{};
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EaxNullCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props = std::monostate{};
|
||||
}
|
||||
|
||||
void EaxNullCommitter::Get(const EaxCall &call, const std::monostate&)
|
||||
{
|
||||
if(call.get_property_id() != 0)
|
||||
fail_unknown_property_id();
|
||||
}
|
||||
|
||||
void EaxNullCommitter::Set(const EaxCall &call, std::monostate&)
|
||||
{
|
||||
if(call.get_property_id() != 0)
|
||||
fail_unknown_property_id();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
@@ -9,36 +9,40 @@
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Pshifter_setParamf(EffectProps*, ALenum param, float)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||
void Pshifter_setParamfv(EffectProps*, ALenum param, const float*)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
|
||||
param};
|
||||
PshifterProps props{};
|
||||
props.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
|
||||
props.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
|
||||
return props;
|
||||
}
|
||||
|
||||
void Pshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||
} // namespace
|
||||
|
||||
const EffectProps PshifterEffectProps{genDefaultProps()};
|
||||
|
||||
void EffectHandler::SetParami(PshifterProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
||||
if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Pitch shifter coarse tune out of range"};
|
||||
props->Pshifter.CoarseTune = val;
|
||||
props.CoarseTune = val;
|
||||
break;
|
||||
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
||||
if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"};
|
||||
props->Pshifter.FineTune = val;
|
||||
props.FineTune = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -46,319 +50,134 @@ void Pshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Pshifter_setParami(props, param, vals[0]); }
|
||||
void EffectHandler::SetParamiv(PshifterProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
|
||||
void Pshifter_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void EffectHandler::SetParamf(PshifterProps&, ALenum param, float)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||
void EffectHandler::SetParamfv(PshifterProps&, ALenum param, const float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void EffectHandler::GetParami(const PshifterProps &props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
||||
*val = props->Pshifter.CoarseTune;
|
||||
break;
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
||||
*val = props->Pshifter.FineTune;
|
||||
break;
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE: *val = props.CoarseTune; break;
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Pshifter_getParami(props, param, vals); }
|
||||
void EffectHandler::GetParamiv(const PshifterProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
|
||||
void Pshifter_getParamf(const EffectProps*, ALenum param, float*)
|
||||
void EffectHandler::GetParamf(const PshifterProps&, ALenum param, float*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||
void Pshifter_getParamfv(const EffectProps*, ALenum param, float*)
|
||||
void EffectHandler::GetParamfv(const PshifterProps&, ALenum param, float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
|
||||
props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Pshifter);
|
||||
|
||||
const EffectProps PshifterEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxPitchShifterEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
using PitchShifterCommitter = EaxCommitter<EaxPitchShifterCommitter>;
|
||||
|
||||
struct EaxPitchShifterEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxPitchShifterEffectDirtyFlagsValue lCoarseTune : 1;
|
||||
EaxPitchShifterEffectDirtyFlagsValue lFineTune : 1;
|
||||
}; // EaxPitchShifterEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxPitchShifterEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxPitchShifterEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXPITCHSHIFTERPROPERTIES eax_{};
|
||||
EAXPITCHSHIFTERPROPERTIES eax_d_{};
|
||||
EaxPitchShifterEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_coarse_tune();
|
||||
void set_efx_fine_tune();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_coarse_tune(long lCoarseTune);
|
||||
void validate_fine_tune(long lFineTune);
|
||||
void validate_all(const EAXPITCHSHIFTERPROPERTIES& all);
|
||||
|
||||
void defer_coarse_tune(long lCoarseTune);
|
||||
void defer_fine_tune(long lFineTune);
|
||||
void defer_all(const EAXPITCHSHIFTERPROPERTIES& all);
|
||||
|
||||
void defer_coarse_tune(const EaxEaxCall& eax_call);
|
||||
void defer_fine_tune(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxPitchShifterEffect
|
||||
|
||||
|
||||
class EaxPitchShifterEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxPitchShifterEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_PITCH_SHIFTER_EFFECT", message}
|
||||
struct CoarseTuneValidator {
|
||||
void operator()(long lCoarseTune) const
|
||||
{
|
||||
eax_validate_range<PitchShifterCommitter::Exception>(
|
||||
"Coarse Tune",
|
||||
lCoarseTune,
|
||||
EAXPITCHSHIFTER_MINCOARSETUNE,
|
||||
EAXPITCHSHIFTER_MAXCOARSETUNE);
|
||||
}
|
||||
}; // EaxPitchShifterEffectException
|
||||
}; // CoarseTuneValidator
|
||||
|
||||
|
||||
EaxPitchShifterEffect::EaxPitchShifterEffect()
|
||||
: EaxEffect{AL_EFFECT_PITCH_SHIFTER}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE;
|
||||
eax_.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::set_efx_coarse_tune()
|
||||
{
|
||||
const auto coarse_tune = clamp(
|
||||
static_cast<ALint>(eax_.lCoarseTune),
|
||||
AL_PITCH_SHIFTER_MIN_COARSE_TUNE,
|
||||
AL_PITCH_SHIFTER_MAX_COARSE_TUNE);
|
||||
|
||||
al_effect_props_.Pshifter.CoarseTune = coarse_tune;
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::set_efx_fine_tune()
|
||||
{
|
||||
const auto fine_tune = clamp(
|
||||
static_cast<ALint>(eax_.lFineTune),
|
||||
AL_PITCH_SHIFTER_MIN_FINE_TUNE,
|
||||
AL_PITCH_SHIFTER_MAX_FINE_TUNE);
|
||||
|
||||
al_effect_props_.Pshifter.FineTune = fine_tune;
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_coarse_tune();
|
||||
set_efx_fine_tune();
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
struct FineTuneValidator {
|
||||
void operator()(long lFineTune) const
|
||||
{
|
||||
case EAXPITCHSHIFTER_NONE:
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxPitchShifterEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_COARSETUNE:
|
||||
eax_call.set_value<EaxPitchShifterEffectException>(eax_.lCoarseTune);
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_FINETUNE:
|
||||
eax_call.set_value<EaxPitchShifterEffectException>(eax_.lFineTune);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxPitchShifterEffectException{"Unsupported property id."};
|
||||
eax_validate_range<PitchShifterCommitter::Exception>(
|
||||
"Fine Tune",
|
||||
lFineTune,
|
||||
EAXPITCHSHIFTER_MINFINETUNE,
|
||||
EAXPITCHSHIFTER_MAXFINETUNE);
|
||||
}
|
||||
}
|
||||
}; // FineTuneValidator
|
||||
|
||||
void EaxPitchShifterEffect::validate_coarse_tune(
|
||||
long lCoarseTune)
|
||||
{
|
||||
eax_validate_range<EaxPitchShifterEffectException>(
|
||||
"Coarse Tune",
|
||||
lCoarseTune,
|
||||
EAXPITCHSHIFTER_MINCOARSETUNE,
|
||||
EAXPITCHSHIFTER_MAXCOARSETUNE);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::validate_fine_tune(
|
||||
long lFineTune)
|
||||
{
|
||||
eax_validate_range<EaxPitchShifterEffectException>(
|
||||
"Fine Tune",
|
||||
lFineTune,
|
||||
EAXPITCHSHIFTER_MINFINETUNE,
|
||||
EAXPITCHSHIFTER_MAXFINETUNE);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::validate_all(
|
||||
const EAXPITCHSHIFTERPROPERTIES& all)
|
||||
{
|
||||
validate_coarse_tune(all.lCoarseTune);
|
||||
validate_fine_tune(all.lFineTune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_coarse_tune(
|
||||
long lCoarseTune)
|
||||
{
|
||||
eax_d_.lCoarseTune = lCoarseTune;
|
||||
eax_dirty_flags_.lCoarseTune = (eax_.lCoarseTune != eax_d_.lCoarseTune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_fine_tune(
|
||||
long lFineTune)
|
||||
{
|
||||
eax_d_.lFineTune = lFineTune;
|
||||
eax_dirty_flags_.lFineTune = (eax_.lFineTune != eax_d_.lFineTune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_all(
|
||||
const EAXPITCHSHIFTERPROPERTIES& all)
|
||||
{
|
||||
defer_coarse_tune(all.lCoarseTune);
|
||||
defer_fine_tune(all.lFineTune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_coarse_tune(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& coarse_tune =
|
||||
eax_call.get_value<EaxPitchShifterEffectException, const decltype(EAXPITCHSHIFTERPROPERTIES::lCoarseTune)>();
|
||||
|
||||
validate_coarse_tune(coarse_tune);
|
||||
defer_coarse_tune(coarse_tune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_fine_tune(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& fine_tune =
|
||||
eax_call.get_value<EaxPitchShifterEffectException, const decltype(EAXPITCHSHIFTERPROPERTIES::lFineTune)>();
|
||||
|
||||
validate_fine_tune(fine_tune);
|
||||
defer_fine_tune(fine_tune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<EaxPitchShifterEffectException, const EAXPITCHSHIFTERPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxPitchShifterEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxPitchShifterEffectDirtyFlags{})
|
||||
struct AllValidator {
|
||||
void operator()(const EAXPITCHSHIFTERPROPERTIES& all) const
|
||||
{
|
||||
CoarseTuneValidator{}(all.lCoarseTune);
|
||||
FineTuneValidator{}(all.lFineTune);
|
||||
}
|
||||
}; // AllValidator
|
||||
|
||||
} // namespace
|
||||
|
||||
template<>
|
||||
struct PitchShifterCommitter::Exception : public EaxException {
|
||||
explicit Exception(const char *message) : EaxException{"EAX_PITCH_SHIFTER_EFFECT", message}
|
||||
{ }
|
||||
};
|
||||
|
||||
template<>
|
||||
[[noreturn]] void PitchShifterCommitter::fail(const char *message)
|
||||
{
|
||||
throw Exception{message};
|
||||
}
|
||||
|
||||
bool EaxPitchShifterCommitter::commit(const EAXPITCHSHIFTERPROPERTIES &props)
|
||||
{
|
||||
if(auto *cur = std::get_if<EAXPITCHSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.lCoarseTune)
|
||||
{
|
||||
set_efx_coarse_tune();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lFineTune)
|
||||
{
|
||||
set_efx_fine_tune();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxPitchShifterEffectDirtyFlags{};
|
||||
mEaxProps = props;
|
||||
mAlProps = [&]{
|
||||
PshifterProps ret{};
|
||||
ret.CoarseTune = static_cast<int>(props.lCoarseTune);
|
||||
ret.FineTune = static_cast<int>(props.lFineTune);
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::set(const EaxEaxCall& eax_call)
|
||||
void EaxPitchShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
props = EAXPITCHSHIFTERPROPERTIES{EAXPITCHSHIFTER_DEFAULTCOARSETUNE,
|
||||
EAXPITCHSHIFTER_DEFAULTFINETUNE};
|
||||
}
|
||||
|
||||
void EaxPitchShifterCommitter::Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXPITCHSHIFTER_NONE:
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_COARSETUNE:
|
||||
defer_coarse_tune(eax_call);
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_FINETUNE:
|
||||
defer_fine_tune(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxPitchShifterEffectException{"Unsupported property id."};
|
||||
case EAXPITCHSHIFTER_NONE: break;
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.lCoarseTune); break;
|
||||
case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.lFineTune); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_pitch_shifter_effect()
|
||||
void EaxPitchShifterCommitter::Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props)
|
||||
{
|
||||
return std::make_unique<EaxPitchShifterEffect>();
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXPITCHSHIFTER_NONE: break;
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.lCoarseTune); break;
|
||||
case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.lFineTune); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
+960
-2250
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,30 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
|
||||
constexpr std::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val) noexcept
|
||||
{
|
||||
#define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \
|
||||
return al::make_optional(VMorpherPhenome::x)
|
||||
return VMorpherPhenome::x
|
||||
switch(val)
|
||||
{
|
||||
HANDLE_PHENOME(A);
|
||||
@@ -59,10 +58,10 @@ al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
|
||||
HANDLE_PHENOME(V);
|
||||
HANDLE_PHENOME(Z);
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
#undef HANDLE_PHENOME
|
||||
}
|
||||
ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
||||
constexpr ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
||||
{
|
||||
#define HANDLE_PHENOME(x) case VMorpherPhenome::x: return AL_VOCAL_MORPHER_PHONEME_ ## x
|
||||
switch(phenome)
|
||||
@@ -102,17 +101,17 @@ ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
||||
#undef HANDLE_PHENOME
|
||||
}
|
||||
|
||||
al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
|
||||
constexpr std::optional<VMorpherWaveform> WaveformFromEmum(ALenum value) noexcept
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_WAVEFORM_SINUSOID: return al::make_optional(VMorpherWaveform::Sinusoid);
|
||||
case AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE: return al::make_optional(VMorpherWaveform::Triangle);
|
||||
case AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH: return al::make_optional(VMorpherWaveform::Sawtooth);
|
||||
case AL_VOCAL_MORPHER_WAVEFORM_SINUSOID: return VMorpherWaveform::Sinusoid;
|
||||
case AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE: return VMorpherWaveform::Triangle;
|
||||
case AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH: return VMorpherWaveform::Sawtooth;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
ALenum EnumFromWaveform(VMorpherWaveform type)
|
||||
constexpr ALenum EnumFromWaveform(VMorpherWaveform type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
@@ -124,13 +123,29 @@ ALenum EnumFromWaveform(VMorpherWaveform type)
|
||||
std::to_string(static_cast<int>(type))};
|
||||
}
|
||||
|
||||
void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
VmorpherProps props{};
|
||||
props.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
|
||||
props.PhonemeA = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA).value();
|
||||
props.PhonemeB = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB).value();
|
||||
props.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
|
||||
props.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
|
||||
props.Waveform = WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM).value();
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
||||
|
||||
void EffectHandler::SetParami(VmorpherProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_PHONEMEA:
|
||||
if(auto phenomeopt = PhenomeFromEnum(val))
|
||||
props->Vmorpher.PhonemeA = *phenomeopt;
|
||||
props.PhonemeA = *phenomeopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val};
|
||||
break;
|
||||
@@ -138,12 +153,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
||||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
|
||||
if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"};
|
||||
props->Vmorpher.PhonemeACoarseTuning = val;
|
||||
props.PhonemeACoarseTuning = val;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEB:
|
||||
if(auto phenomeopt = PhenomeFromEnum(val))
|
||||
props->Vmorpher.PhonemeB = *phenomeopt;
|
||||
props.PhonemeB = *phenomeopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val};
|
||||
break;
|
||||
@@ -151,12 +166,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
||||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
|
||||
if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"};
|
||||
props->Vmorpher.PhonemeBCoarseTuning = val;
|
||||
props.PhonemeBCoarseTuning = val;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_WAVEFORM:
|
||||
if(auto formopt = WaveformFromEmum(val))
|
||||
props->Vmorpher.Waveform = *formopt;
|
||||
props.Waveform = *formopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val};
|
||||
break;
|
||||
@@ -166,19 +181,19 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void EffectHandler::SetParamiv(VmorpherProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void EffectHandler::SetParamf(VmorpherProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_RATE:
|
||||
if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"};
|
||||
props->Vmorpher.Rate = val;
|
||||
props.Rate = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -186,49 +201,35 @@ void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Vmorpher_setParamf(props, param, vals[0]); }
|
||||
void EffectHandler::SetParamfv(VmorpherProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val)
|
||||
void EffectHandler::GetParami(const VmorpherProps &props, ALenum param, int* val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_PHONEMEA:
|
||||
*val = EnumFromPhenome(props->Vmorpher.PhonemeA);
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
|
||||
*val = props->Vmorpher.PhonemeACoarseTuning;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEB:
|
||||
*val = EnumFromPhenome(props->Vmorpher.PhonemeB);
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
|
||||
*val = props->Vmorpher.PhonemeBCoarseTuning;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_WAVEFORM:
|
||||
*val = EnumFromWaveform(props->Vmorpher.Waveform);
|
||||
break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEA: *val = EnumFromPhenome(props.PhonemeA); break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; break;
|
||||
case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void EffectHandler::GetParamiv(const VmorpherProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void EffectHandler::GetParamf(const VmorpherProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_RATE:
|
||||
*val = props->Vmorpher.Rate;
|
||||
*val = props.Rate;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -236,551 +237,221 @@ void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Vmorpher_getParamf(props, param, vals); }
|
||||
void EffectHandler::GetParamfv(const VmorpherProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
|
||||
props.Vmorpher.PhonemeA = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA);
|
||||
props.Vmorpher.PhonemeB = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB);
|
||||
props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
|
||||
props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
|
||||
props.Vmorpher.Waveform = *WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM);
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Vmorpher);
|
||||
|
||||
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxVocalMorpherEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
using VocalMorpherCommitter = EaxCommitter<EaxVocalMorpherCommitter>;
|
||||
|
||||
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();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
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();
|
||||
|
||||
void 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);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxVocalMorpherEffect
|
||||
|
||||
|
||||
class EaxVocalMorpherEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxVocalMorpherEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_VOCAL_MORPHER_EFFECT", message}
|
||||
struct PhonemeAValidator {
|
||||
void operator()(unsigned long ulPhonemeA) const
|
||||
{
|
||||
eax_validate_range<VocalMorpherCommitter::Exception>(
|
||||
"Phoneme A",
|
||||
ulPhonemeA,
|
||||
EAXVOCALMORPHER_MINPHONEMEA,
|
||||
EAXVOCALMORPHER_MAXPHONEMEA);
|
||||
}
|
||||
}; // EaxVocalMorpherEffectException
|
||||
}; // PhonemeAValidator
|
||||
|
||||
|
||||
EaxVocalMorpherEffect::EaxVocalMorpherEffect()
|
||||
: EaxEffect{AL_EFFECT_VOCAL_MORPHER}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
struct PhonemeACoarseTuningValidator {
|
||||
void operator()(long lPhonemeACoarseTuning) const
|
||||
{
|
||||
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."};
|
||||
eax_validate_range<VocalMorpherCommitter::Exception>(
|
||||
"Phoneme A Coarse Tuning",
|
||||
lPhonemeACoarseTuning,
|
||||
EAXVOCALMORPHER_MINPHONEMEACOARSETUNING,
|
||||
EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING);
|
||||
}
|
||||
}
|
||||
}; // PhonemeACoarseTuningValidator
|
||||
|
||||
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{})
|
||||
struct PhonemeBValidator {
|
||||
void operator()(unsigned long ulPhonemeB) const
|
||||
{
|
||||
eax_validate_range<VocalMorpherCommitter::Exception>(
|
||||
"Phoneme B",
|
||||
ulPhonemeB,
|
||||
EAXVOCALMORPHER_MINPHONEMEB,
|
||||
EAXVOCALMORPHER_MAXPHONEMEB);
|
||||
}
|
||||
}; // PhonemeBValidator
|
||||
|
||||
struct PhonemeBCoarseTuningValidator {
|
||||
void operator()(long lPhonemeBCoarseTuning) const
|
||||
{
|
||||
eax_validate_range<VocalMorpherCommitter::Exception>(
|
||||
"Phoneme B Coarse Tuning",
|
||||
lPhonemeBCoarseTuning,
|
||||
EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING,
|
||||
EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING);
|
||||
}
|
||||
}; // PhonemeBCoarseTuningValidator
|
||||
|
||||
struct WaveformValidator {
|
||||
void operator()(unsigned long ulWaveform) const
|
||||
{
|
||||
eax_validate_range<VocalMorpherCommitter::Exception>(
|
||||
"Waveform",
|
||||
ulWaveform,
|
||||
EAXVOCALMORPHER_MINWAVEFORM,
|
||||
EAXVOCALMORPHER_MAXWAVEFORM);
|
||||
}
|
||||
}; // WaveformValidator
|
||||
|
||||
struct RateValidator {
|
||||
void operator()(float flRate) const
|
||||
{
|
||||
eax_validate_range<VocalMorpherCommitter::Exception>(
|
||||
"Rate",
|
||||
flRate,
|
||||
EAXVOCALMORPHER_MINRATE,
|
||||
EAXVOCALMORPHER_MAXRATE);
|
||||
}
|
||||
}; // RateValidator
|
||||
|
||||
struct AllValidator {
|
||||
void operator()(const EAXVOCALMORPHERPROPERTIES& all) const
|
||||
{
|
||||
PhonemeAValidator{}(all.ulPhonemeA);
|
||||
PhonemeACoarseTuningValidator{}(all.lPhonemeACoarseTuning);
|
||||
PhonemeBValidator{}(all.ulPhonemeB);
|
||||
PhonemeBCoarseTuningValidator{}(all.lPhonemeBCoarseTuning);
|
||||
WaveformValidator{}(all.ulWaveform);
|
||||
RateValidator{}(all.flRate);
|
||||
}
|
||||
}; // AllValidator
|
||||
|
||||
} // namespace
|
||||
|
||||
template<>
|
||||
struct VocalMorpherCommitter::Exception : public EaxException {
|
||||
explicit Exception(const char *message) : EaxException{"EAX_VOCAL_MORPHER_EFFECT", message}
|
||||
{ }
|
||||
};
|
||||
|
||||
template<>
|
||||
[[noreturn]] void VocalMorpherCommitter::fail(const char *message)
|
||||
{
|
||||
throw Exception{message};
|
||||
}
|
||||
|
||||
bool EaxVocalMorpherCommitter::commit(const EAXVOCALMORPHERPROPERTIES &props)
|
||||
{
|
||||
if(auto *cur = std::get_if<EAXVOCALMORPHERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
mEaxProps = props;
|
||||
|
||||
if (eax_dirty_flags_.ulPhonemeA)
|
||||
auto get_phoneme = [](unsigned long phoneme) noexcept
|
||||
{
|
||||
set_efx_phoneme_a();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lPhonemeACoarseTuning)
|
||||
#define HANDLE_PHENOME(x) case x: return VMorpherPhenome::x
|
||||
switch(phoneme)
|
||||
{
|
||||
HANDLE_PHENOME(A);
|
||||
HANDLE_PHENOME(E);
|
||||
HANDLE_PHENOME(I);
|
||||
HANDLE_PHENOME(O);
|
||||
HANDLE_PHENOME(U);
|
||||
HANDLE_PHENOME(AA);
|
||||
HANDLE_PHENOME(AE);
|
||||
HANDLE_PHENOME(AH);
|
||||
HANDLE_PHENOME(AO);
|
||||
HANDLE_PHENOME(EH);
|
||||
HANDLE_PHENOME(ER);
|
||||
HANDLE_PHENOME(IH);
|
||||
HANDLE_PHENOME(IY);
|
||||
HANDLE_PHENOME(UH);
|
||||
HANDLE_PHENOME(UW);
|
||||
HANDLE_PHENOME(B);
|
||||
HANDLE_PHENOME(D);
|
||||
HANDLE_PHENOME(F);
|
||||
HANDLE_PHENOME(G);
|
||||
HANDLE_PHENOME(J);
|
||||
HANDLE_PHENOME(K);
|
||||
HANDLE_PHENOME(L);
|
||||
HANDLE_PHENOME(M);
|
||||
HANDLE_PHENOME(N);
|
||||
HANDLE_PHENOME(P);
|
||||
HANDLE_PHENOME(R);
|
||||
HANDLE_PHENOME(S);
|
||||
HANDLE_PHENOME(T);
|
||||
HANDLE_PHENOME(V);
|
||||
HANDLE_PHENOME(Z);
|
||||
}
|
||||
return VMorpherPhenome::A;
|
||||
#undef HANDLE_PHENOME
|
||||
};
|
||||
auto get_waveform = [](unsigned long form) noexcept
|
||||
{
|
||||
set_efx_phoneme_a_coarse_tuning();
|
||||
}
|
||||
if(form == EAX_VOCALMORPHER_SINUSOID) return VMorpherWaveform::Sinusoid;
|
||||
if(form == EAX_VOCALMORPHER_TRIANGLE) return VMorpherWaveform::Triangle;
|
||||
if(form == EAX_VOCALMORPHER_SAWTOOTH) return VMorpherWaveform::Sawtooth;
|
||||
return VMorpherWaveform::Sinusoid;
|
||||
};
|
||||
|
||||
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{};
|
||||
mAlProps = [&]{
|
||||
VmorpherProps ret{};
|
||||
ret.PhonemeA = get_phoneme(props.ulPhonemeA);
|
||||
ret.PhonemeACoarseTuning = static_cast<int>(props.lPhonemeACoarseTuning);
|
||||
ret.PhonemeB = get_phoneme(props.ulPhonemeB);
|
||||
ret.PhonemeBCoarseTuning = static_cast<int>(props.lPhonemeBCoarseTuning);
|
||||
ret.Waveform = get_waveform(props.ulWaveform);
|
||||
ret.Rate = props.flRate;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::set(const EaxEaxCall& eax_call)
|
||||
void EaxVocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
static constexpr EAXVOCALMORPHERPROPERTIES defprops{[]
|
||||
{
|
||||
case EAXVOCALMORPHER_NONE:
|
||||
break;
|
||||
EAXVOCALMORPHERPROPERTIES ret{};
|
||||
ret.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
|
||||
ret.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
|
||||
ret.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
|
||||
ret.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
|
||||
ret.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
|
||||
ret.flRate = EAXVOCALMORPHER_DEFAULTRATE;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
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."};
|
||||
void EaxVocalMorpherCommitter::Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXVOCALMORPHER_NONE: break;
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXVOCALMORPHER_PHONEMEA: call.set_value<Exception>(props.ulPhonemeA); break;
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING: call.set_value<Exception>(props.lPhonemeACoarseTuning); break;
|
||||
case EAXVOCALMORPHER_PHONEMEB: call.set_value<Exception>(props.ulPhonemeB); break;
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: call.set_value<Exception>(props.lPhonemeBCoarseTuning); break;
|
||||
case EAXVOCALMORPHER_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
|
||||
case EAXVOCALMORPHER_RATE: call.set_value<Exception>(props.flRate); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
EaxEffectUPtr eax_create_eax_vocal_morpher_effect()
|
||||
void EaxVocalMorpherCommitter::Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props)
|
||||
{
|
||||
return std::make_unique<EaxVocalMorpherEffect>();
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXVOCALMORPHER_NONE: break;
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXVOCALMORPHER_PHONEMEA: defer<PhonemeAValidator>(call, props.ulPhonemeA); break;
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING: defer<PhonemeACoarseTuningValidator>(call, props.lPhonemeACoarseTuning); break;
|
||||
case EAXVOCALMORPHER_PHONEMEB: defer<PhonemeBValidator>(call, props.ulPhonemeB); break;
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: defer<PhonemeBCoarseTuningValidator>(call, props.lPhonemeBCoarseTuning); break;
|
||||
case EAXVOCALMORPHER_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
|
||||
case EAXVOCALMORPHER_RATE: defer<RateValidator>(call, props.flRate); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
@@ -29,27 +31,45 @@
|
||||
#include <csignal>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "al/debug.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "core/except.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "core/logging.h"
|
||||
#include "opthelpers.h"
|
||||
#include "vector.h"
|
||||
#include "strutils.h"
|
||||
|
||||
|
||||
bool TrapALError{false};
|
||||
namespace al {
|
||||
context_error::context_error(ALenum code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||
std::va_list args;
|
||||
va_start(args, msg);
|
||||
setMessage(msg, args);
|
||||
va_end(args);
|
||||
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||
}
|
||||
context_error::~context_error() = default;
|
||||
} /* namespace al */
|
||||
|
||||
void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
||||
{
|
||||
auto message = al::vector<char>(256);
|
||||
auto message = std::vector<char>(256);
|
||||
|
||||
va_list args, args2;
|
||||
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||
std::va_list args, args2;
|
||||
va_start(args, msg);
|
||||
va_copy(args2, args);
|
||||
int msglen{std::vsnprintf(message.data(), message.size(), msg, args)};
|
||||
@@ -60,9 +80,15 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
||||
}
|
||||
va_end(args2);
|
||||
va_end(args);
|
||||
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||
|
||||
if(msglen >= 0) msg = message.data();
|
||||
else msg = "<internal error constructing message>";
|
||||
if(msglen >= 0)
|
||||
msg = message.data();
|
||||
else
|
||||
{
|
||||
msg = "<internal error constructing message>";
|
||||
msglen = static_cast<int>(strlen(msg));
|
||||
}
|
||||
|
||||
WARN("Error generated on context %p, code 0x%04x, \"%s\"\n",
|
||||
decltype(std::declval<void*>()){this}, errorCode, msg);
|
||||
@@ -77,30 +103,55 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
||||
#endif
|
||||
}
|
||||
|
||||
ALenum curerr{AL_NO_ERROR};
|
||||
mLastError.compare_exchange_strong(curerr, errorCode);
|
||||
if(mLastThreadError.get() == AL_NO_ERROR)
|
||||
mLastThreadError.set(errorCode);
|
||||
|
||||
debugMessage(DebugSource::API, DebugType::Error, 0, DebugSeverity::High,
|
||||
{msg, static_cast<uint>(msglen)});
|
||||
}
|
||||
|
||||
AL_API ALenum AL_APIENTRY alGetError(void)
|
||||
START_API_FUNC
|
||||
/* Special-case alGetError since it (potentially) raises a debug signal and
|
||||
* returns a non-default value for a null context.
|
||||
*/
|
||||
AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(unlikely(!context))
|
||||
{
|
||||
static constexpr ALenum deferror{AL_INVALID_OPERATION};
|
||||
WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
|
||||
if(TrapALError)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(IsDebuggerPresent())
|
||||
DebugBreak();
|
||||
#elif defined(SIGTRAP)
|
||||
raise(SIGTRAP);
|
||||
#endif
|
||||
}
|
||||
return deferror;
|
||||
}
|
||||
if(auto context = GetContextRef()) LIKELY
|
||||
return alGetErrorDirect(context.get());
|
||||
|
||||
return context->mLastError.exchange(AL_NO_ERROR);
|
||||
auto get_value = [](const char *envname, const char *optname) -> ALenum
|
||||
{
|
||||
auto optstr = al::getenv(envname);
|
||||
if(!optstr)
|
||||
optstr = ConfigValueStr({}, "game_compat", optname);
|
||||
if(optstr)
|
||||
{
|
||||
char *end{};
|
||||
auto value = std::strtoul(optstr->c_str(), &end, 0);
|
||||
if(end && *end == '\0' && value <= std::numeric_limits<ALenum>::max())
|
||||
return static_cast<ALenum>(value);
|
||||
ERR("Invalid default error value: \"%s\"", optstr->c_str());
|
||||
}
|
||||
return AL_INVALID_OPERATION;
|
||||
};
|
||||
static const ALenum deferror{get_value("__ALSOFT_DEFAULT_ERROR", "default-error")};
|
||||
|
||||
WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
|
||||
if(TrapALError)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(IsDebuggerPresent())
|
||||
DebugBreak();
|
||||
#elif defined(SIGTRAP)
|
||||
raise(SIGTRAP);
|
||||
#endif
|
||||
}
|
||||
return deferror;
|
||||
}
|
||||
|
||||
FORCE_ALIGN ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) noexcept
|
||||
{
|
||||
ALenum ret{context->mLastThreadError.get()};
|
||||
if(ret != AL_NO_ERROR) UNLIKELY
|
||||
context->mLastThreadError.set(AL_NO_ERROR);
|
||||
return ret;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef AL_ERROR_H
|
||||
#define AL_ERROR_H
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "core/except.h"
|
||||
|
||||
namespace al {
|
||||
|
||||
class context_error final : public al::base_exception {
|
||||
ALenum mErrorCode{};
|
||||
|
||||
public:
|
||||
#ifdef __MINGW32__
|
||||
[[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
context_error(ALenum code, const char *msg, ...);
|
||||
~context_error() final;
|
||||
|
||||
[[nodiscard]] auto errorCode() const noexcept -> ALenum { return mErrorCode; }
|
||||
};
|
||||
|
||||
} /* namespace al */
|
||||
|
||||
#endif /* AL_ERROR_H */
|
||||
+121
-98
@@ -3,39 +3,54 @@
|
||||
|
||||
#include "event.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <bitset>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alsem.h"
|
||||
#include "alspan.h"
|
||||
#include "core/async_event.h"
|
||||
#include "core/except.h"
|
||||
#include "core/context.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/voice_change.h"
|
||||
#include "debug.h"
|
||||
#include "direct_defs.h"
|
||||
#include "error.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
static int EventThread(ALCcontext *context)
|
||||
namespace {
|
||||
|
||||
template<typename... Ts>
|
||||
struct overloaded : Ts... { using Ts::operator()...; };
|
||||
|
||||
template<typename... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
int EventThread(ALCcontext *context)
|
||||
{
|
||||
RingBuffer *ring{context->mAsyncEvents.get()};
|
||||
bool quitnow{false};
|
||||
while(likely(!quitnow))
|
||||
while(!quitnow)
|
||||
{
|
||||
auto evt_data = ring->getReadVector().first;
|
||||
if(evt_data.len == 0)
|
||||
@@ -44,81 +59,100 @@ static int EventThread(ALCcontext *context)
|
||||
continue;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mEventCbLock};
|
||||
do {
|
||||
auto *evt_ptr = reinterpret_cast<AsyncEvent*>(evt_data.buf);
|
||||
evt_data.buf += sizeof(AsyncEvent);
|
||||
evt_data.len -= 1;
|
||||
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||
auto evt_span = al::span{std::launder(reinterpret_cast<AsyncEvent*>(evt_data.buf)),
|
||||
evt_data.len};
|
||||
for(auto &event : evt_span)
|
||||
{
|
||||
quitnow = std::holds_alternative<AsyncKillThread>(event);
|
||||
if(quitnow) UNLIKELY break;
|
||||
|
||||
AsyncEvent evt{*evt_ptr};
|
||||
al::destroy_at(evt_ptr);
|
||||
ring->readAdvance(1);
|
||||
|
||||
quitnow = evt.EnumType == AsyncEvent::KillThread;
|
||||
if(unlikely(quitnow)) break;
|
||||
|
||||
if(evt.EnumType == AsyncEvent::ReleaseEffectState)
|
||||
auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
|
||||
auto proc_killthread = [](AsyncKillThread&) { };
|
||||
auto proc_release = [](AsyncEffectReleaseEvent &evt)
|
||||
{
|
||||
evt.u.mEffectState->release();
|
||||
continue;
|
||||
}
|
||||
|
||||
uint enabledevts{context->mEnabledEvts.load(std::memory_order_acquire)};
|
||||
if(!context->mEventCb) continue;
|
||||
|
||||
if(evt.EnumType == AsyncEvent::SourceStateChange)
|
||||
al::intrusive_ptr<EffectState>{evt.mEffectState};
|
||||
};
|
||||
auto proc_srcstate = [context,enabledevts](AsyncSourceStateEvent &evt)
|
||||
{
|
||||
if(!(enabledevts&AsyncEvent::SourceStateChange))
|
||||
continue;
|
||||
if(!context->mEventCb
|
||||
|| !enabledevts.test(al::to_underlying(AsyncEnableBits::SourceState)))
|
||||
return;
|
||||
|
||||
ALuint state{};
|
||||
std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
|
||||
std::string msg{"Source ID " + std::to_string(evt.mId)};
|
||||
msg += " state has changed to ";
|
||||
switch(evt.u.srcstate.state)
|
||||
switch(evt.mState)
|
||||
{
|
||||
case AsyncEvent::SrcState::Reset:
|
||||
case AsyncSrcState::Reset:
|
||||
msg += "AL_INITIAL";
|
||||
state = AL_INITIAL;
|
||||
break;
|
||||
case AsyncEvent::SrcState::Stop:
|
||||
case AsyncSrcState::Stop:
|
||||
msg += "AL_STOPPED";
|
||||
state = AL_STOPPED;
|
||||
break;
|
||||
case AsyncEvent::SrcState::Play:
|
||||
case AsyncSrcState::Play:
|
||||
msg += "AL_PLAYING";
|
||||
state = AL_PLAYING;
|
||||
break;
|
||||
case AsyncEvent::SrcState::Pause:
|
||||
case AsyncSrcState::Pause:
|
||||
msg += "AL_PAUSED";
|
||||
state = AL_PAUSED;
|
||||
break;
|
||||
}
|
||||
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
|
||||
state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
}
|
||||
else if(evt.EnumType == AsyncEvent::BufferCompleted)
|
||||
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state,
|
||||
static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
};
|
||||
auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt)
|
||||
{
|
||||
if(!(enabledevts&AsyncEvent::BufferCompleted))
|
||||
continue;
|
||||
std::string msg{std::to_string(evt.u.bufcomp.count)};
|
||||
if(evt.u.bufcomp.count == 1) msg += " buffer completed";
|
||||
if(!context->mEventCb
|
||||
|| !enabledevts.test(al::to_underlying(AsyncEnableBits::BufferCompleted)))
|
||||
return;
|
||||
|
||||
std::string msg{std::to_string(evt.mCount)};
|
||||
if(evt.mCount == 1) msg += " buffer completed";
|
||||
else msg += " buffers completed";
|
||||
context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id,
|
||||
evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(),
|
||||
context->mEventParam);
|
||||
}
|
||||
else if(evt.EnumType == AsyncEvent::Disconnected)
|
||||
context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount,
|
||||
static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
};
|
||||
auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt)
|
||||
{
|
||||
if(!(enabledevts&AsyncEvent::Disconnected))
|
||||
continue;
|
||||
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
|
||||
static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg,
|
||||
context->mEventParam);
|
||||
}
|
||||
} while(evt_data.len != 0);
|
||||
const std::string_view message{evt.msg.data()};
|
||||
|
||||
context->debugMessage(DebugSource::System, DebugType::Error, 0,
|
||||
DebugSeverity::High, message);
|
||||
|
||||
if(context->mEventCb
|
||||
&& enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
|
||||
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
|
||||
static_cast<ALsizei>(message.length()), message.data(),
|
||||
context->mEventParam);
|
||||
};
|
||||
|
||||
std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
|
||||
proc_killthread}, event);
|
||||
}
|
||||
std::destroy(evt_span.begin(), evt_span.end());
|
||||
ring->readAdvance(evt_span.size());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr std::optional<AsyncEnableBits> GetEventType(ALenum etype) noexcept
|
||||
{
|
||||
switch(etype)
|
||||
{
|
||||
case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: return AsyncEnableBits::BufferCompleted;
|
||||
case AL_EVENT_TYPE_DISCONNECTED_SOFT: return AsyncEnableBits::Disconnected;
|
||||
case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT: return AsyncEnableBits::SourceState;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void StartEventThrd(ALCcontext *ctx)
|
||||
{
|
||||
try {
|
||||
@@ -143,7 +177,7 @@ void StopEventThrd(ALCcontext *ctx)
|
||||
evt_data = ring->getWriteVector().first;
|
||||
} while(evt_data.len == 0);
|
||||
}
|
||||
al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), AsyncEvent::KillThread);
|
||||
std::ignore = InitAsyncEvent<AsyncKillThread>(evt_data.buf);
|
||||
ring->writeAdvance(1);
|
||||
|
||||
ctx->mEventSem.post();
|
||||
@@ -151,38 +185,29 @@ void StopEventThrd(ALCcontext *ctx)
|
||||
ctx->mEventThread.join();
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(unlikely(!context)) return;
|
||||
AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei,count, const ALenum*,types, ALboolean,enable)
|
||||
FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count,
|
||||
const ALenum *types, ALboolean enable) noexcept
|
||||
try {
|
||||
if(count < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Controlling %d events", count};
|
||||
if(count <= 0) UNLIKELY return;
|
||||
|
||||
if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count);
|
||||
if(count <= 0) return;
|
||||
if(!types) SETERR_RETURN(context, AL_INVALID_VALUE,, "NULL pointer");
|
||||
if(!types)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
uint flags{0};
|
||||
const ALenum *types_end = types+count;
|
||||
auto bad_type = std::find_if_not(types, types_end,
|
||||
[&flags](ALenum type) noexcept -> bool
|
||||
{
|
||||
if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
|
||||
flags |= AsyncEvent::BufferCompleted;
|
||||
else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
|
||||
flags |= AsyncEvent::SourceStateChange;
|
||||
else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
|
||||
flags |= AsyncEvent::Disconnected;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
if(bad_type != types_end)
|
||||
SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid event type 0x%04x", *bad_type);
|
||||
ContextBase::AsyncEventBitset flags{};
|
||||
for(ALenum evttype : al::span{types, static_cast<uint>(count)})
|
||||
{
|
||||
auto etype = GetEventType(evttype);
|
||||
if(!etype)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype};
|
||||
flags.set(al::to_underlying(*etype));
|
||||
}
|
||||
|
||||
if(enable)
|
||||
{
|
||||
uint enabledevts{context->mEnabledEvts.load(std::memory_order_relaxed)};
|
||||
auto enabledevts = context->mEnabledEvts.load(std::memory_order_relaxed);
|
||||
while(context->mEnabledEvts.compare_exchange_weak(enabledevts, enabledevts|flags,
|
||||
std::memory_order_acq_rel, std::memory_order_acquire) == 0)
|
||||
{
|
||||
@@ -193,7 +218,7 @@ START_API_FUNC
|
||||
}
|
||||
else
|
||||
{
|
||||
uint enabledevts{context->mEnabledEvts.load(std::memory_order_relaxed)};
|
||||
auto enabledevts = context->mEnabledEvts.load(std::memory_order_relaxed);
|
||||
while(context->mEnabledEvts.compare_exchange_weak(enabledevts, enabledevts&~flags,
|
||||
std::memory_order_acq_rel, std::memory_order_acquire) == 0)
|
||||
{
|
||||
@@ -201,20 +226,18 @@ START_API_FUNC
|
||||
/* Wait to ensure the event handler sees the changed flags before
|
||||
* returning.
|
||||
*/
|
||||
std::lock_guard<std::mutex> _{context->mEventCbLock};
|
||||
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
|
||||
START_API_FUNC
|
||||
AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT,callback, void*,userParam)
|
||||
FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context,
|
||||
ALEVENTPROCSOFT callback, void *userParam) noexcept
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(unlikely(!context)) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> __{context->mEventCbLock};
|
||||
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||
context->mEventCb = callback;
|
||||
context->mEventParam = userParam;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
@@ -20,60 +20,59 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alc/context.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "alstring.h"
|
||||
#include "core/except.h"
|
||||
#include "direct_defs.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
|
||||
START_API_FUNC
|
||||
AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*,extName)
|
||||
FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extName) noexcept
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(unlikely(!context)) return AL_FALSE;
|
||||
|
||||
if(!extName)
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE, AL_FALSE, "NULL pointer");
|
||||
|
||||
size_t len{strlen(extName)};
|
||||
const char *ptr{context->mExtensionList};
|
||||
while(ptr && *ptr)
|
||||
if(!extName) UNLIKELY
|
||||
{
|
||||
if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len])))
|
||||
return AL_TRUE;
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
return AL_FALSE;
|
||||
}
|
||||
|
||||
if((ptr=strchr(ptr, ' ')) != nullptr)
|
||||
{
|
||||
do {
|
||||
++ptr;
|
||||
} while(isspace(*ptr));
|
||||
}
|
||||
const std::string_view tofind{extName};
|
||||
for(std::string_view ext : context->mExtensions)
|
||||
{
|
||||
if(al::case_compare(ext, tofind) == 0)
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
return AL_FALSE;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
|
||||
START_API_FUNC
|
||||
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) noexcept
|
||||
{
|
||||
if(!funcName) return nullptr;
|
||||
return alcGetProcAddress(nullptr, funcName);
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
|
||||
START_API_FUNC
|
||||
FORCE_ALIGN ALvoid* AL_APIENTRY alGetProcAddressDirect(ALCcontext*, const ALchar *funcName) noexcept
|
||||
{
|
||||
if(!enumName) return static_cast<ALenum>(0);
|
||||
if(!funcName) return nullptr;
|
||||
return alcGetProcAddress(nullptr, funcName);
|
||||
}
|
||||
|
||||
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) noexcept
|
||||
{
|
||||
if(!enumName) return ALenum{0};
|
||||
return alcGetEnumValue(nullptr, enumName);
|
||||
}
|
||||
|
||||
FORCE_ALIGN ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext*, const ALchar *enumName) noexcept
|
||||
{
|
||||
if(!enumName) return ALenum{0};
|
||||
return alcGetEnumValue(nullptr, enumName);
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
+434
-521
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,40 @@
|
||||
#ifndef AL_FILTER_H
|
||||
#define AL_FILTER_H
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
|
||||
#define LOWPASSFREQREF 5000.0f
|
||||
#define HIGHPASSFREQREF 250.0f
|
||||
|
||||
inline constexpr float LowPassFreqRef{5000.0f};
|
||||
inline constexpr float HighPassFreqRef{250.0f};
|
||||
|
||||
template<typename T>
|
||||
struct FilterTable {
|
||||
static void setParami(struct ALfilter*, ALenum, int);
|
||||
static void setParamiv(struct ALfilter*, ALenum, const int*);
|
||||
static void setParamf(struct ALfilter*, ALenum, float);
|
||||
static void setParamfv(struct ALfilter*, ALenum, const float*);
|
||||
|
||||
static void getParami(const struct ALfilter*, ALenum, int*);
|
||||
static void getParamiv(const struct ALfilter*, ALenum, int*);
|
||||
static void getParamf(const struct ALfilter*, ALenum, float*);
|
||||
static void getParamfv(const struct ALfilter*, ALenum, float*);
|
||||
};
|
||||
|
||||
struct NullFilterTable : public FilterTable<NullFilterTable> { };
|
||||
struct LowpassFilterTable : public FilterTable<LowpassFilterTable> { };
|
||||
struct HighpassFilterTable : public FilterTable<HighpassFilterTable> { };
|
||||
struct BandpassFilterTable : public FilterTable<BandpassFilterTable> { };
|
||||
|
||||
|
||||
struct ALfilter {
|
||||
@@ -17,36 +42,35 @@ struct ALfilter {
|
||||
|
||||
float Gain{1.0f};
|
||||
float GainHF{1.0f};
|
||||
float HFReference{LOWPASSFREQREF};
|
||||
float HFReference{LowPassFreqRef};
|
||||
float GainLF{1.0f};
|
||||
float LFReference{HIGHPASSFREQREF};
|
||||
float LFReference{HighPassFreqRef};
|
||||
|
||||
struct Vtable {
|
||||
void (*const setParami )(ALfilter *filter, ALenum param, int val);
|
||||
void (*const setParamiv)(ALfilter *filter, ALenum param, const int *vals);
|
||||
void (*const setParamf )(ALfilter *filter, ALenum param, float val);
|
||||
void (*const setParamfv)(ALfilter *filter, ALenum param, const float *vals);
|
||||
|
||||
void (*const getParami )(const ALfilter *filter, ALenum param, int *val);
|
||||
void (*const getParamiv)(const ALfilter *filter, ALenum param, int *vals);
|
||||
void (*const getParamf )(const ALfilter *filter, ALenum param, float *val);
|
||||
void (*const getParamfv)(const ALfilter *filter, ALenum param, float *vals);
|
||||
};
|
||||
const Vtable *vtab{nullptr};
|
||||
using TableTypes = std::variant<NullFilterTable,LowpassFilterTable,HighpassFilterTable,
|
||||
BandpassFilterTable>;
|
||||
TableTypes mTypeVariant;
|
||||
|
||||
/* Self ID */
|
||||
ALuint id{0};
|
||||
|
||||
void setParami(ALenum param, int value) { vtab->setParami(this, param, value); }
|
||||
void setParamiv(ALenum param, const int *values) { vtab->setParamiv(this, param, values); }
|
||||
void setParamf(ALenum param, float value) { vtab->setParamf(this, param, value); }
|
||||
void setParamfv(ALenum param, const float *values) { vtab->setParamfv(this, param, values); }
|
||||
void getParami(ALenum param, int *value) const { vtab->getParami(this, param, value); }
|
||||
void getParamiv(ALenum param, int *values) const { vtab->getParamiv(this, param, values); }
|
||||
void getParamf(ALenum param, float *value) const { vtab->getParamf(this, param, value); }
|
||||
void getParamfv(ALenum param, float *values) const { vtab->getParamfv(this, param, values); }
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC()
|
||||
DISABLE_ALLOC
|
||||
};
|
||||
|
||||
struct FilterSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALfilter,64>*> Filters{nullptr};
|
||||
|
||||
FilterSubList() noexcept = default;
|
||||
FilterSubList(const FilterSubList&) = delete;
|
||||
FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; }
|
||||
~FilterSubList();
|
||||
|
||||
FilterSubList& operator=(const FilterSubList&) = delete;
|
||||
FilterSubList& operator=(FilterSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+219
-266
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "listener.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
|
||||
@@ -30,9 +31,10 @@
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "core/except.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "alspan.h"
|
||||
#include "direct_defs.h"
|
||||
#include "error.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
@@ -48,402 +50,353 @@ inline void UpdateProps(ALCcontext *context)
|
||||
context->mPropsDirty = true;
|
||||
}
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
inline void CommitAndUpdateProps(ALCcontext *context)
|
||||
{
|
||||
if(!context->mDeferUpdates)
|
||||
{
|
||||
if(context->has_eax())
|
||||
#ifdef ALSOFT_EAX
|
||||
if(context->eaxNeedsCommit())
|
||||
{
|
||||
context->mHoldUpdates.store(true, std::memory_order_release);
|
||||
while((context->mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
|
||||
/* busy-wait */
|
||||
}
|
||||
|
||||
context->eax_commit_and_update_sources();
|
||||
context->mPropsDirty = true;
|
||||
context->applyAllUpdates();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
UpdateContextProps(context);
|
||||
context->mHoldUpdates.store(false, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
context->mPropsDirty = true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
inline void CommitAndUpdateProps(ALCcontext *context)
|
||||
{ UpdateProps(context); }
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
AL_API DECL_FUNC2(void, alListenerf, ALenum,param, ALfloat,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) noexcept
|
||||
try {
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
if(!(value >= 0.0f && std::isfinite(value)))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Listener gain out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener gain out of range"};
|
||||
listener.Gain = value;
|
||||
UpdateProps(context.get());
|
||||
break;
|
||||
UpdateProps(context);
|
||||
return;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Listener meters per unit out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener meters per unit out of range"};
|
||||
listener.mMetersPerUnit = value;
|
||||
UpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float property");
|
||||
UpdateProps(context);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
AL_API DECL_FUNC4(void, alListener3f, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3)
|
||||
FORCE_ALIGN void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1,
|
||||
ALfloat value2, ALfloat value3) noexcept
|
||||
try {
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Listener position out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener position out of range"};
|
||||
listener.Position[0] = value1;
|
||||
listener.Position[1] = value2;
|
||||
listener.Position[2] = value3;
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
CommitAndUpdateProps(context);
|
||||
return;
|
||||
|
||||
case AL_VELOCITY:
|
||||
if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Listener velocity out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener velocity out of range"};
|
||||
listener.Velocity[0] = value1;
|
||||
listener.Velocity[1] = value2;
|
||||
listener.Velocity[2] = value3;
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
|
||||
CommitAndUpdateProps(context);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
if(values)
|
||||
AL_API DECL_FUNC2(void, alListenerfv, ALenum,param, const ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param,
|
||||
const ALfloat *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
case AL_METERS_PER_UNIT:
|
||||
alListenerf(param, values[0]);
|
||||
return;
|
||||
case AL_GAIN:
|
||||
case AL_METERS_PER_UNIT:
|
||||
alListenerfDirect(context, param, *values);
|
||||
return;
|
||||
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(param, values[0], values[1], values[2]);
|
||||
return;
|
||||
}
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
auto vals = al::span<const float,3>{values, 3_uz};
|
||||
alListener3fDirect(context, param, vals[0], vals[1], vals[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!values) SETERR_RETURN(context, AL_INVALID_VALUE,, "NULL pointer");
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
if(!(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]) &&
|
||||
std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5])))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Listener orientation out of range");
|
||||
auto vals = al::span<const float,6>{values, 6_uz};
|
||||
if(!std::all_of(vals.cbegin(), vals.cend(), [](float f) { return std::isfinite(f); }))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener orientation out of range");
|
||||
/* AT then UP */
|
||||
listener.OrientAt[0] = values[0];
|
||||
listener.OrientAt[1] = values[1];
|
||||
listener.OrientAt[2] = values[2];
|
||||
listener.OrientUp[0] = values[3];
|
||||
listener.OrientUp[1] = values[4];
|
||||
listener.OrientUp[2] = values[5];
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
|
||||
std::copy_n(vals.cbegin(), 3, listener.OrientAt.begin());
|
||||
std::copy_n(vals.cbegin()+3, 3, listener.OrientUp.begin());
|
||||
CommitAndUpdateProps(context);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alListeneri(ALenum param, ALint /*value*/)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
|
||||
AL_API DECL_FUNC2(void, alListeneri, ALenum,param, ALint,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept
|
||||
try {
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC4(void, alListener3i, ALenum,param, ALint,value1, ALint,value2, ALint,value3)
|
||||
FORCE_ALIGN void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1,
|
||||
ALint value2, ALint value3) noexcept
|
||||
try {
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(param, static_cast<ALfloat>(value1), static_cast<ALfloat>(value2), static_cast<ALfloat>(value3));
|
||||
alListener3fDirect(context, param, static_cast<ALfloat>(value1),
|
||||
static_cast<ALfloat>(value2), static_cast<ALfloat>(value3));
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
AL_API DECL_FUNC2(void, alListeneriv, ALenum,param, const ALint*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param,
|
||||
const ALint *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
al::span<const ALint> vals;
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
vals = {values, 3_uz};
|
||||
alListener3fDirect(context, param, static_cast<ALfloat>(vals[0]),
|
||||
static_cast<ALfloat>(vals[1]), static_cast<ALfloat>(vals[2]));
|
||||
return;
|
||||
|
||||
case AL_ORIENTATION:
|
||||
vals = {values, 6_uz};
|
||||
const std::array fvals{static_cast<ALfloat>(vals[0]), static_cast<ALfloat>(vals[1]),
|
||||
static_cast<ALfloat>(vals[2]), static_cast<ALfloat>(vals[3]),
|
||||
static_cast<ALfloat>(vals[4]), static_cast<ALfloat>(vals[5]),
|
||||
};
|
||||
alListenerfvDirect(context, param, fvals.data());
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
|
||||
param};
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
if(values)
|
||||
{
|
||||
ALfloat fvals[6];
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(param, static_cast<ALfloat>(values[0]), static_cast<ALfloat>(values[1]), static_cast<ALfloat>(values[2]));
|
||||
return;
|
||||
|
||||
case AL_ORIENTATION:
|
||||
fvals[0] = static_cast<ALfloat>(values[0]);
|
||||
fvals[1] = static_cast<ALfloat>(values[1]);
|
||||
fvals[2] = static_cast<ALfloat>(values[2]);
|
||||
fvals[3] = static_cast<ALfloat>(values[3]);
|
||||
fvals[4] = static_cast<ALfloat>(values[4]);
|
||||
fvals[5] = static_cast<ALfloat>(values[5]);
|
||||
alListenerfv(param, fvals);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!values)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
AL_API DECL_FUNC2(void, alGetListenerf, ALenum,param, ALfloat*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param,
|
||||
ALfloat *value) noexcept
|
||||
try {
|
||||
if(!value)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
*value = listener.Gain;
|
||||
break;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
*value = listener.mMetersPerUnit;
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float property");
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN: *value = listener.Gain; return;
|
||||
case AL_METERS_PER_UNIT: *value = listener.mMetersPerUnit; return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC4(void, alGetListener3f, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param,
|
||||
ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept
|
||||
try {
|
||||
if(!value1 || !value2 || !value3)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
*value1 = listener.Position[0];
|
||||
*value2 = listener.Position[1];
|
||||
*value3 = listener.Position[2];
|
||||
break;
|
||||
return;
|
||||
|
||||
case AL_VELOCITY:
|
||||
*value1 = listener.Velocity[0];
|
||||
*value2 = listener.Velocity[1];
|
||||
*value3 = listener.Velocity[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
AL_API DECL_FUNC2(void, alGetListenerfv, ALenum,param, ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param,
|
||||
ALfloat *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
case AL_METERS_PER_UNIT:
|
||||
alGetListenerf(param, values);
|
||||
alGetListenerfDirect(context, param, values);
|
||||
return;
|
||||
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alGetListener3f(param, values+0, values+1, values+2);
|
||||
auto vals = al::span<ALfloat,3>{values, 3_uz};
|
||||
alGetListener3fDirect(context, param, &vals[0], &vals[1], &vals[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!values)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
al::span<ALfloat,6> vals{values, 6_uz};
|
||||
// AT then UP
|
||||
values[0] = listener.OrientAt[0];
|
||||
values[1] = listener.OrientAt[1];
|
||||
values[2] = listener.OrientAt[2];
|
||||
values[3] = listener.OrientUp[0];
|
||||
values[4] = listener.OrientUp[1];
|
||||
values[5] = listener.OrientUp[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
|
||||
std::copy_n(listener.OrientAt.cbegin(), 3, vals.begin());
|
||||
std::copy_n(listener.OrientUp.cbegin(), 3, vals.begin()+3);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!value)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
AL_API DECL_FUNC2(void, alGetListeneri, ALenum,param, ALint*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept
|
||||
try {
|
||||
if(!value) throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC4(void, alGetListener3i, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param,
|
||||
ALint *value1, ALint *value2, ALint *value3) noexcept
|
||||
try {
|
||||
if(!value1 || !value2 || !value3)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!value1 || !value2 || !value3)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
*value1 = static_cast<ALint>(listener.Position[0]);
|
||||
*value2 = static_cast<ALint>(listener.Position[1]);
|
||||
*value3 = static_cast<ALint>(listener.Position[2]);
|
||||
break;
|
||||
return;
|
||||
|
||||
case AL_VELOCITY:
|
||||
*value1 = static_cast<ALint>(listener.Velocity[0]);
|
||||
*value2 = static_cast<ALint>(listener.Velocity[1]);
|
||||
*value3 = static_cast<ALint>(listener.Velocity[2]);
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
|
||||
START_API_FUNC
|
||||
{
|
||||
AL_API DECL_FUNC2(void, alGetListeneriv, ALenum,param, ALint*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param,
|
||||
ALint *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alGetListener3i(param, values+0, values+1, values+2);
|
||||
auto vals = al::span<ALint,3>{values, 3_uz};
|
||||
alGetListener3iDirect(context, param, &vals[0], &vals[1], &vals[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!values)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
|
||||
static constexpr auto f2i = [](const float val) noexcept { return static_cast<ALint>(val); };
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
auto vals = al::span<ALint,6>{values, 6_uz};
|
||||
// AT then UP
|
||||
values[0] = static_cast<ALint>(listener.OrientAt[0]);
|
||||
values[1] = static_cast<ALint>(listener.OrientAt[1]);
|
||||
values[2] = static_cast<ALint>(listener.OrientAt[2]);
|
||||
values[3] = static_cast<ALint>(listener.OrientUp[0]);
|
||||
values[4] = static_cast<ALint>(listener.OrientUp[1]);
|
||||
values[5] = static_cast<ALint>(listener.OrientUp[2]);
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
|
||||
std::transform(listener.OrientAt.cbegin(), listener.OrientAt.cend(), vals.begin(), f2i);
|
||||
std::transform(listener.OrientUp.cbegin(), listener.OrientUp.cend(), vals.begin()+3, f2i);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
|
||||
param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
@@ -18,7 +16,7 @@ struct ALlistener {
|
||||
float Gain{1.0f};
|
||||
float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
DISABLE_ALLOC
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+3287
-4311
File diff suppressed because it is too large
Load Diff
+877
-632
File diff suppressed because it is too large
Load Diff
+386
-724
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user