Update OpenAL-soft to 1.23.1-bc7cb17.

This commit is contained in:
Miku AuahDark
2024-03-20 11:06:03 +08:00
parent 4a512be715
commit 73a6fc9196
294 changed files with 44342 additions and 40077 deletions
File diff suppressed because it is too large Load Diff
+286 -178
View File
@@ -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
File diff suppressed because it is too large Load Diff
+33 -36
View File
@@ -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
+620
View File
@@ -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());
}
+70
View File
@@ -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 */
+127
View File
@@ -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
+218
View File
@@ -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
};
}
+97
View File
@@ -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
+457
View File
@@ -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
+32
View File
@@ -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;
}
+18
View File
@@ -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
@@ -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
+6
View File
@@ -0,0 +1,6 @@
#ifndef EAX_GLOBALS_INCLUDED
#define EAX_GLOBALS_INCLUDED
inline bool eax_g_is_enabled{true};
#endif /* EAX_GLOBALS_INCLUDED */
+26
View File
@@ -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
-324
View File
@@ -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
};
}
-117
View File
@@ -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
-3
View File
@@ -1,3 +0,0 @@
#include "config.h"
#include "eax_effect.h"
-44
View File
@@ -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
-63
View File
@@ -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;
}
-25
View File
@@ -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
-21
View File
@@ -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";
-22
View File
@@ -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
-36
View File
@@ -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.");
}
}
-3
View File
@@ -1,3 +0,0 @@
#include "config.h"
#include "eax_x_ram.h"
+361 -385
View File
@@ -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);
}
+33 -12
View File
@@ -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
+149 -457
View File
@@ -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
File diff suppressed because it is too large Load Diff
+76 -216
View File
@@ -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
+101 -77
View File
@@ -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);
}
}
+70 -22
View File
@@ -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); }
+161 -475
View File
@@ -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
+161 -476
View File
@@ -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
-63
View File
@@ -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
+42 -61
View File
@@ -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
+159 -371
View File
@@ -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
+160 -377
View File
@@ -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
+110 -119
View File
@@ -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
+102 -283
View File
@@ -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
File diff suppressed because it is too large Load Diff
+235 -564
View File
@@ -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
+82 -31
View File
@@ -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
+27
View File
@@ -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
View File
@@ -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
+30 -31
View File
@@ -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
File diff suppressed because it is too large Load Diff
+50 -26
View File
@@ -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
View File
@@ -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
+1 -3
View File
@@ -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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff