First Commit

This commit is contained in:
2025-02-06 22:24:29 +08:00
parent ed7df4c81e
commit 7539e6a53c
18116 changed files with 6181499 additions and 0 deletions

1563
externals/openal-soft/al/auxeffectslot.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

368
externals/openal-soft/al/auxeffectslot.h vendored Normal file
View File

@@ -0,0 +1,368 @@
#ifndef AL_AUXEFFECTSLOT_H
#define AL_AUXEFFECTSLOT_H
#include <atomic>
#include <cstddef>
#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 "core/effectslot.h"
#include "intrusive_ptr.h"
#include "vector.h"
#ifdef ALSOFT_EAX
#include <memory>
#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,
Playing = AL_PLAYING,
Stopped = AL_STOPPED,
};
struct ALeffectslot {
float Gain{1.0f};
bool AuxSendAuto{true};
ALeffectslot *Target{nullptr};
ALbuffer *Buffer{nullptr};
struct {
EffectSlotType Type{EffectSlotType::None};
EffectProps Props{};
al::intrusive_ptr<EffectState> State;
} Effect;
bool mPropsDirty{true};
SlotState mState{SlotState::Initial};
RefCount ref{0u};
EffectSlot *mSlot{nullptr};
/* Self ID */
ALuint id{};
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);
/* This can be new'd for the context's default effect slot. */
DEF_NEWDEL(ALeffectslot)
#ifdef ALSOFT_EAX
public:
void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index);
EaxFxSlotIndexValue eax_get_index() const noexcept { return eax_fx_slot_index_; }
const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept
{ return eax_; }
// Returns `true` if all sources should be updated, or `false` otherwise.
bool eax_dispatch(const EaxCall& call)
{ return call.is_get() ? eax_get(call) : eax_set(call); }
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_{};
int eax_version_{}; // Current EAX version.
EaxDirtyFlags eax_df_{}; // Dirty flags for the current EAX version.
EaxEffectUPtr eax_effect_{};
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();
// 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;
}
constexpr bool eax4_fx_slot_is_legacy() const noexcept
{ return eax_fx_slot_index_ < 2; }
void eax4_fx_slot_ensure_unlocked() const;
static ALenum eax_get_efx_effect_type(const GUID& guid);
const GUID& eax_get_eax_default_effect_guid() const noexcept;
long eax_get_eax_default_lock() const noexcept;
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();
void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) const;
void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) const;
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_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);
bool eax_fx_slot_should_update_sources() const noexcept;
// 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);
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 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_efx_slot_effect(EaxEffect &effect);
// `alAuxiliaryEffectSloti(effect_slot, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, value)`
void eax_set_efx_slot_send_auto(bool is_send_auto);
// `alAuxiliaryEffectSlotf(effect_slot, AL_EFFECTSLOT_GAIN, gain)`
void eax_set_efx_slot_gain(ALfloat gain);
public:
class EaxDeleter {
public:
void operator()(ALeffectslot *effect_slot);
};
#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);
#endif // ALSOFT_EAX
#endif

1692
externals/openal-soft/al/buffer.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

58
externals/openal-soft/al/buffer.h vendored Normal file
View File

@@ -0,0 +1,58 @@
#ifndef AL_BUFFER_H
#define AL_BUFFER_H
#include <atomic>
#include "AL/al.h"
#include "albyte.h"
#include "alc/inprogext.h"
#include "almalloc.h"
#include "atomic.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
struct ALbuffer : public BufferStorage {
ALbitfieldSOFT Access{0u};
al::vector<al::byte,16> mDataStorage;
ALuint OriginalSize{0};
ALuint UnpackAlign{0};
ALuint PackAlign{0};
ALuint UnpackAmbiOrder{1};
ALbitfieldSOFT MappedAccess{0u};
ALsizei MappedOffset{0};
ALsizei MappedSize{0};
ALuint mLoopStart{0u};
ALuint mLoopEnd{0u};
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
RefCount ref{0u};
/* Self ID */
ALuint id{0};
DISABLE_ALLOC()
#ifdef ALSOFT_EAX
EaxStorage eax_x_ram_mode{EaxStorage::Automatic};
bool eax_x_ram_is_hardware{};
#endif // ALSOFT_EAX
};
#endif

1621
externals/openal-soft/al/eax/api.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

1493
externals/openal-soft/al/eax/api.h vendored Normal file

File diff suppressed because it is too large Load Diff

219
externals/openal-soft/al/eax/call.cpp vendored Normal file
View File

@@ -0,0 +1,219 @@
#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}, mVersion{0}, mPropertySetId{EaxCallPropertySetId::none}
, 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
externals/openal-soft/al/eax/call.h vendored Normal file
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);
bool is_get() const noexcept { return mCallType == EaxCallType::get; }
bool is_deferred() const noexcept { return mIsDeferred; }
int get_version() const noexcept { return mVersion; }
EaxCallPropertySetId get_property_set_id() const noexcept { return mPropertySetId; }
ALuint get_property_id() const noexcept { return mPropertyId; }
ALuint get_property_al_name() const noexcept { return mPropertySourceId; }
EaxFxSlotIndex get_fx_slot_index() const noexcept { return mFxSlotIndex; }
template<typename TException, typename TValue>
TValue& get_value() const
{
if(mPropertyBufferSize < sizeof(TValue))
fail_too_small();
return *static_cast<TValue*>(mPropertyBuffer);
}
template<typename TValue>
al::span<TValue> get_values(size_t max_count) const
{
if(max_count == 0 || mPropertyBufferSize < sizeof(TValue))
fail_too_small();
const auto count = minz(mPropertyBufferSize / sizeof(TValue), max_count);
return al::as_span(static_cast<TValue*>(mPropertyBuffer), count);
}
template<typename TValue>
al::span<TValue> get_values() const
{
return get_values<TValue>(~size_t{});
}
template<typename TException, typename TValue>
void set_value(const TValue& value) const
{
get_value<TException, TValue>() = value;
}
private:
const EaxCallType mCallType;
int mVersion;
EaxFxSlotIndex mFxSlotIndex;
EaxCallPropertySetId mPropertySetId;
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

418
externals/openal-soft/al/eax/effect.h vendored Normal file
View File

@@ -0,0 +1,418 @@
#ifndef EAX_EFFECT_INCLUDED
#define EAX_EFFECT_INCLUDED
#include <cassert>
#include <memory>
#include "alnumeric.h"
#include "AL/al.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
/* TODO: Use std::variant (C++17). */
enum class EaxEffectType {
None, Reverb, Chorus, Autowah, Compressor, Distortion, Echo, Equalizer, Flanger,
FrequencyShifter, Modulator, PitchShifter, VocalMorpher
};
struct EaxEffectProps {
EaxEffectType mType;
union {
EAXREVERBPROPERTIES mReverb;
EAXCHORUSPROPERTIES mChorus;
EAXAUTOWAHPROPERTIES mAutowah;
EAXAGCCOMPRESSORPROPERTIES mCompressor;
EAXDISTORTIONPROPERTIES mDistortion;
EAXECHOPROPERTIES mEcho;
EAXEQUALIZERPROPERTIES mEqualizer;
EAXFLANGERPROPERTIES mFlanger;
EAXFREQUENCYSHIFTERPROPERTIES mFrequencyShifter;
EAXRINGMODULATORPROPERTIES mModulator;
EAXPITCHSHIFTERPROPERTIES mPitchShifter;
EAXVOCALMORPHERPROPERTIES mVocalMorpher;
};
};
constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props)
{
switch(props.mType)
{
case EaxEffectType::None: break;
case EaxEffectType::Reverb: return AL_EFFECT_EAXREVERB;
case EaxEffectType::Chorus: return AL_EFFECT_CHORUS;
case EaxEffectType::Autowah: return AL_EFFECT_AUTOWAH;
case EaxEffectType::Compressor: return AL_EFFECT_COMPRESSOR;
case EaxEffectType::Distortion: return AL_EFFECT_DISTORTION;
case EaxEffectType::Echo: return AL_EFFECT_ECHO;
case EaxEffectType::Equalizer: return AL_EFFECT_EQUALIZER;
case EaxEffectType::Flanger: return AL_EFFECT_FLANGER;
case EaxEffectType::FrequencyShifter: return AL_EFFECT_FREQUENCY_SHIFTER;
case EaxEffectType::Modulator: return AL_EFFECT_RING_MODULATOR;
case EaxEffectType::PitchShifter: return AL_EFFECT_PITCH_SHIFTER;
case EaxEffectType::VocalMorpher: return AL_EFFECT_VOCAL_MORPHER;
}
return AL_EFFECT_NULL;
}
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);
bool commit(const EaxEffectProps &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 Get(const EaxCall &call, const EaxEffectProps &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 Set(const EaxCall &call, EaxEffectProps &props);
static void translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
static void translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept;
static void translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& 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()); }
bool commit(const EaxEffectProps &props);
static void SetDefaults(EaxEffectProps &props);
static void Get(const EaxCall &call, const EaxEffectProps &props);
static void Set(const EaxCall &call, EaxEffectProps &props);
};
struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
using EaxCommitter<EaxAutowahCommitter>::EaxCommitter;
};
struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
using EaxCommitter<EaxChorusCommitter>::EaxCommitter;
};
struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
using EaxCommitter<EaxCompressorCommitter>::EaxCommitter;
};
struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
using EaxCommitter<EaxDistortionCommitter>::EaxCommitter;
};
struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
using EaxCommitter<EaxEchoCommitter>::EaxCommitter;
};
struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter;
};
struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
using EaxCommitter<EaxFlangerCommitter>::EaxCommitter;
};
struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> {
using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter;
};
struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
using EaxCommitter<EaxModulatorCommitter>::EaxCommitter;
};
struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> {
using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter;
};
struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> {
using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter;
};
struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> {
using EaxCommitter<EaxNullCommitter>::EaxCommitter;
};
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_{};
template<typename T, typename ...Args>
void call_set_defaults(Args&& ...args)
{ return T::SetDefaults(std::forward<Args>(args)...); }
void call_set_defaults(const ALenum altype, EaxEffectProps &props)
{
if(altype == AL_EFFECT_EAXREVERB)
return call_set_defaults<EaxReverbCommitter>(props);
if(altype == AL_EFFECT_CHORUS)
return call_set_defaults<EaxChorusCommitter>(props);
if(altype == AL_EFFECT_AUTOWAH)
return call_set_defaults<EaxAutowahCommitter>(props);
if(altype == AL_EFFECT_COMPRESSOR)
return call_set_defaults<EaxCompressorCommitter>(props);
if(altype == AL_EFFECT_DISTORTION)
return call_set_defaults<EaxDistortionCommitter>(props);
if(altype == AL_EFFECT_ECHO)
return call_set_defaults<EaxEchoCommitter>(props);
if(altype == AL_EFFECT_EQUALIZER)
return call_set_defaults<EaxEqualizerCommitter>(props);
if(altype == AL_EFFECT_FLANGER)
return call_set_defaults<EaxFlangerCommitter>(props);
if(altype == AL_EFFECT_FREQUENCY_SHIFTER)
return call_set_defaults<EaxFrequencyShifterCommitter>(props);
if(altype == AL_EFFECT_RING_MODULATOR)
return call_set_defaults<EaxModulatorCommitter>(props);
if(altype == AL_EFFECT_PITCH_SHIFTER)
return call_set_defaults<EaxPitchShifterCommitter>(props);
if(altype == AL_EFFECT_VOCAL_MORPHER)
return call_set_defaults<EaxVocalMorpherCommitter>(props);
return call_set_defaults<EaxNullCommitter>(props);
}
template<typename T>
void init()
{
call_set_defaults<EaxReverbCommitter>(state1_.d);
state1_.i = state1_.d;
call_set_defaults<EaxReverbCommitter>(state2_.d);
state2_.i = state2_.d;
call_set_defaults<EaxReverbCommitter>(state3_.d);
state3_.i = state3_.d;
call_set_defaults<T>(state4_.d);
state4_.i = state4_.d;
call_set_defaults<T>(state5_.d);
state5_.i = state5_.d;
}
void set_defaults(int eax_version, ALenum altype)
{
switch(eax_version)
{
case 1: call_set_defaults<EaxReverbCommitter>(state1_.d); break;
case 2: call_set_defaults<EaxReverbCommitter>(state2_.d); break;
case 3: call_set_defaults<EaxReverbCommitter>(state3_.d); break;
case 4: call_set_defaults(altype, state4_.d); break;
case 5: call_set_defaults(altype, state5_.d); break;
}
changed_ = true;
}
#define EAXCALL(T, Callable, ...) \
if(T == EaxEffectType::Reverb) \
return Callable<EaxReverbCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::Chorus) \
return Callable<EaxChorusCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::Autowah) \
return Callable<EaxAutowahCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::Compressor) \
return Callable<EaxCompressorCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::Distortion) \
return Callable<EaxDistortionCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::Echo) \
return Callable<EaxEchoCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::Equalizer) \
return Callable<EaxEqualizerCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::Flanger) \
return Callable<EaxFlangerCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::FrequencyShifter) \
return Callable<EaxFrequencyShifterCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::Modulator) \
return Callable<EaxModulatorCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::PitchShifter) \
return Callable<EaxPitchShifterCommitter>(__VA_ARGS__); \
if(T == EaxEffectType::VocalMorpher) \
return Callable<EaxVocalMorpherCommitter>(__VA_ARGS__); \
return Callable<EaxNullCommitter>(__VA_ARGS__)
template<typename T, typename ...Args>
static void call_set(Args&& ...args)
{ return T::Set(std::forward<Args>(args)...); }
static void call_set(const EaxCall &call, EaxEffectProps &props)
{ EAXCALL(props.mType, call_set, call, props); }
void set(const EaxCall &call)
{
switch(call.get_version())
{
case 1: call_set<EaxReverbCommitter>(call, state1_.d); break;
case 2: call_set<EaxReverbCommitter>(call, state2_.d); break;
case 3: call_set<EaxReverbCommitter>(call, state3_.d); break;
case 4: call_set(call, state4_.d); break;
case 5: call_set(call, state5_.d); break;
}
changed_ = true;
}
template<typename T, typename ...Args>
static void call_get(Args&& ...args)
{ return T::Get(std::forward<Args>(args)...); }
static void call_get(const EaxCall &call, const EaxEffectProps &props)
{ EAXCALL(props.mType, call_get, call, props); }
void get(const EaxCall &call)
{
switch(call.get_version())
{
case 1: call_get<EaxReverbCommitter>(call, state1_.d); break;
case 2: call_get<EaxReverbCommitter>(call, state2_.d); break;
case 3: call_get<EaxReverbCommitter>(call, state3_.d); break;
case 4: call_get(call, state4_.d); break;
case 5: call_get(call, state5_.d); break;
}
}
template<typename T, typename ...Args>
bool call_commit(Args&& ...args)
{ return T{props_, al_effect_props_}.commit(std::forward<Args>(args)...); }
bool call_commit(const EaxEffectProps &props)
{ EAXCALL(props.mType, call_commit, 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 |= call_commit<EaxReverbCommitter>(state1_.d);
break;
case 2:
state2_.i = state2_.d;
ret |= call_commit<EaxReverbCommitter>(state2_.d);
break;
case 3:
state3_.i = state3_.d;
ret |= call_commit<EaxReverbCommitter>(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

View File

@@ -0,0 +1,59 @@
#include "config.h"
#include "exception.h"
#include <cassert>
#include <string>
EaxException::EaxException(const char *context, const char *message)
: std::runtime_error{make_message(context, message)}
{
}
EaxException::~EaxException() = default;
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;
}

View File

@@ -0,0 +1,18 @@
#ifndef EAX_EXCEPTION_INCLUDED
#define EAX_EXCEPTION_INCLUDED
#include <stdexcept>
#include <string>
class EaxException : public std::runtime_error {
static std::string make_message(const char *context, const char *message);
public:
EaxException(const char *context, const char *message);
~EaxException() override;
}; // EaxException
#endif // !EAX_EXCEPTION_INCLUDED

View File

@@ -0,0 +1,71 @@
#include "config.h"
#include "fx_slot_index.h"
#include "exception.h"
namespace
{
class EaxFxSlotIndexException :
public EaxException
{
public:
explicit EaxFxSlotIndexException(
const char* message)
:
EaxException{"EAX_FX_SLOT_INDEX", message}
{
}
}; // EaxFxSlotIndexException
} // namespace
void EaxFxSlotIndex::set(EaxFxSlotIndexValue index)
{
if(index >= EaxFxSlotIndexValue{EAX_MAX_FXSLOTS})
fail("Index out of range.");
emplace(index);
}
void EaxFxSlotIndex::set(const GUID &guid)
{
if (false)
{
}
else if (guid == EAX_NULL_GUID)
{
reset();
}
else if (guid == EAXPROPERTYID_EAX40_FXSlot0 || guid == EAXPROPERTYID_EAX50_FXSlot0)
{
emplace(0u);
}
else if (guid == EAXPROPERTYID_EAX40_FXSlot1 || guid == EAXPROPERTYID_EAX50_FXSlot1)
{
emplace(1u);
}
else if (guid == EAXPROPERTYID_EAX40_FXSlot2 || guid == EAXPROPERTYID_EAX50_FXSlot2)
{
emplace(2u);
}
else if (guid == EAXPROPERTYID_EAX40_FXSlot3 || guid == EAXPROPERTYID_EAX50_FXSlot3)
{
emplace(3u);
}
else
{
fail("Unsupported GUID.");
}
}
[[noreturn]]
void EaxFxSlotIndex::fail(const char* message)
{
throw EaxFxSlotIndexException{message};
}

View File

@@ -0,0 +1,41 @@
#ifndef EAX_FX_SLOT_INDEX_INCLUDED
#define EAX_FX_SLOT_INDEX_INCLUDED
#include <cstddef>
#include "aloptional.h"
#include "api.h"
using EaxFxSlotIndexValue = std::size_t;
class EaxFxSlotIndex : public al::optional<EaxFxSlotIndexValue>
{
public:
using al::optional<EaxFxSlotIndexValue>::optional;
EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; }
EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; }
void set(EaxFxSlotIndexValue index);
void set(const GUID& guid);
private:
[[noreturn]]
static void fail(const char *message);
}; // EaxFxSlotIndex
inline bool operator==(const EaxFxSlotIndex& lhs, const EaxFxSlotIndex& rhs) noexcept
{
if(lhs.has_value() != rhs.has_value())
return false;
if(lhs.has_value())
return *lhs == *rhs;
return true;
}
inline bool operator!=(const EaxFxSlotIndex& lhs, const EaxFxSlotIndex& rhs) noexcept
{ return !(lhs == rhs); }
#endif // !EAX_FX_SLOT_INDEX_INCLUDED

View File

@@ -0,0 +1,75 @@
#include "config.h"
#include "fx_slots.h"
#include <array>
#include "api.h"
#include "exception.h"
namespace
{
class EaxFxSlotsException :
public EaxException
{
public:
explicit EaxFxSlotsException(
const char* message)
:
EaxException{"EAX_FX_SLOTS", message}
{
}
}; // EaxFxSlotsException
} // namespace
void EaxFxSlots::initialize(ALCcontext& al_context)
{
initialize_fx_slots(al_context);
}
void EaxFxSlots::uninitialize() noexcept
{
for (auto& fx_slot : fx_slots_)
{
fx_slot = nullptr;
}
}
const ALeffectslot& EaxFxSlots::get(EaxFxSlotIndex index) const
{
if(!index.has_value())
fail("Empty index.");
return *fx_slots_[index.value()];
}
ALeffectslot& EaxFxSlots::get(EaxFxSlotIndex index)
{
if(!index.has_value())
fail("Empty index.");
return *fx_slots_[index.value()];
}
[[noreturn]]
void EaxFxSlots::fail(
const char* message)
{
throw EaxFxSlotsException{message};
}
void EaxFxSlots::initialize_fx_slots(ALCcontext& al_context)
{
auto fx_slot_index = EaxFxSlotIndexValue{};
for (auto& fx_slot : fx_slots_)
{
fx_slot = eax_create_al_effect_slot(al_context);
fx_slot->eax_initialize(al_context, fx_slot_index);
fx_slot_index += 1;
}
}

49
externals/openal-soft/al/eax/fx_slots.h vendored Normal file
View File

@@ -0,0 +1,49 @@
#ifndef EAX_FX_SLOTS_INCLUDED
#define EAX_FX_SLOTS_INCLUDED
#include <array>
#include "al/auxeffectslot.h"
#include "api.h"
#include "call.h"
#include "fx_slot_index.h"
class EaxFxSlots
{
public:
void initialize(ALCcontext& al_context);
void uninitialize() noexcept;
void commit()
{
for(auto& fx_slot : fx_slots_)
fx_slot->eax_commit();
}
const ALeffectslot& get(
EaxFxSlotIndex index) const;
ALeffectslot& get(
EaxFxSlotIndex index);
private:
using Items = std::array<EaxAlEffectSlotUPtr, EAX_MAX_FXSLOTS>;
Items fx_slots_{};
[[noreturn]]
static void fail(
const char* message);
void initialize_fx_slots(ALCcontext& al_context);
}; // EaxFxSlots
#endif // !EAX_FX_SLOTS_INCLUDED

View File

@@ -0,0 +1,21 @@
#include "config.h"
#include "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
externals/openal-soft/al/eax/globals.h vendored Normal file
View File

@@ -0,0 +1,22 @@
#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

26
externals/openal-soft/al/eax/utils.cpp vendored Normal file
View File

@@ -0,0 +1,26 @@
#include "config.h"
#include "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);
try {
std::rethrow_exception(exception_ptr);
}
catch(const std::exception& ex) {
const auto ex_message = ex.what();
ERR("%s %s\n", message ? message : "", ex_message);
}
catch(...) {
ERR("%s %s\n", message ? message : "", "Generic exception.");
}
}

95
externals/openal-soft/al/eax/utils.h vendored Normal file
View File

@@ -0,0 +1,95 @@
#ifndef EAX_UTILS_INCLUDED
#define EAX_UTILS_INCLUDED
#include <algorithm>
#include <cstdint>
#include <string>
#include <type_traits>
using EaxDirtyFlags = unsigned int;
struct EaxAlLowPassParam {
float gain;
float gain_hf;
};
void eax_log_exception(const char *message) noexcept;
template<typename TException, typename TValue>
void eax_validate_range(
const char* value_name,
const TValue& value,
const TValue& min_value,
const TValue& max_value)
{
if (value >= min_value && value <= max_value)
return;
const auto message =
std::string{value_name} +
" out of range (value: " +
std::to_string(value) + "; min: " +
std::to_string(min_value) + "; max: " +
std::to_string(max_value) + ").";
throw TException{message.c_str()};
}
namespace detail {
template<typename T>
struct EaxIsBitFieldStruct {
private:
using yes = std::true_type;
using no = std::false_type;
template<typename U>
static auto test(int) -> decltype(std::declval<typename U::EaxIsBitFieldStruct>(), yes{});
template<typename>
static no test(...);
public:
static constexpr auto value = std::is_same<decltype(test<T>(0)), yes>::value;
};
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
{
using Value = std::conditional_t<
sizeof(T) == 1,
std::uint8_t,
std::conditional_t<
sizeof(T) == 2,
std::uint16_t,
std::conditional_t<
sizeof(T) == 4,
std::uint32_t,
void>>>;
static_assert(!std::is_same<Value, void>::value, "Unsupported type.");
return detail::eax_bit_fields_are_equal<T, Value>(lhs, rhs);
}
template<
typename T,
std::enable_if_t<detail::EaxIsBitFieldStruct<T>::value, int> = 0
>
inline bool operator!=(const T& lhs, const T& rhs) noexcept
{
return !(lhs == rhs);
}
#endif // !EAX_UTILS_INCLUDED

38
externals/openal-soft/al/eax/x_ram.h vendored Normal file
View File

@@ -0,0 +1,38 @@
#ifndef EAX_X_RAM_INCLUDED
#define EAX_X_RAM_INCLUDED
#include "AL/al.h"
constexpr auto eax_x_ram_min_size = ALsizei{};
constexpr auto eax_x_ram_max_size = ALsizei{64 * 1'024 * 1'024};
constexpr auto AL_EAX_RAM_SIZE = ALenum{0x202201};
constexpr auto AL_EAX_RAM_FREE = ALenum{0x202202};
constexpr auto AL_STORAGE_AUTOMATIC = ALenum{0x202203};
constexpr auto AL_STORAGE_HARDWARE = ALenum{0x202204};
constexpr auto AL_STORAGE_ACCESSIBLE = ALenum{0x202205};
constexpr auto AL_EAX_RAM_SIZE_NAME = "AL_EAX_RAM_SIZE";
constexpr auto AL_EAX_RAM_FREE_NAME = "AL_EAX_RAM_FREE";
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);
#endif // !EAX_X_RAM_INCLUDED

766
externals/openal-soft/al/effect.cpp vendored Normal file
View File

@@ -0,0 +1,766 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include "effect.h"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <memory>
#include <mutex>
#include <new>
#include <numeric>
#include <utility>
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include "AL/efx-presets.h"
#include "AL/efx.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 "alstring.h"
#include "core/except.h"
#include "core/logging.h"
#include "opthelpers.h"
#include "vector.h"
#ifdef ALSOFT_EAX
#include <cassert>
#include "eax/exception.h"
#endif // ALSOFT_EAX
const EffectList gEffectList[16]{
{ "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
{ "reverb", REVERB_EFFECT, AL_EFFECT_REVERB },
{ "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH },
{ "chorus", CHORUS_EFFECT, AL_EFFECT_CHORUS },
{ "compressor", COMPRESSOR_EFFECT, AL_EFFECT_COMPRESSOR },
{ "distortion", DISTORTION_EFFECT, AL_EFFECT_DISTORTION },
{ "echo", ECHO_EFFECT, AL_EFFECT_ECHO },
{ "equalizer", EQUALIZER_EFFECT, AL_EFFECT_EQUALIZER },
{ "flanger", FLANGER_EFFECT, AL_EFFECT_FLANGER },
{ "fshifter", FSHIFTER_EFFECT, AL_EFFECT_FREQUENCY_SHIFTER },
{ "modulator", MODULATOR_EFFECT, AL_EFFECT_RING_MODULATOR },
{ "pshifter", PSHIFTER_EFFECT, AL_EFFECT_PITCH_SHIFTER },
{ "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 },
};
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);
}
effect_exception::~effect_exception() = default;
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 },
};
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)
{
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)) ? al::to_address(iter) : nullptr;
}
void InitEffectParams(ALeffect *effect, ALenum type)
{
const EffectPropsItem *item{getEffectPropsItemByType(type)};
if(item)
{
effect->Props = item->DefaultProps;
effect->vtab = &item->Vtable;
}
else
{
effect->Props = EffectProps{};
effect->vtab = &NullEffectVtable;
}
effect->type = type;
}
bool EnsureEffects(ALCdevice *device, size_t needed)
{
size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), size_t{0},
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
while(needed > count)
{
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(!sublist->Effects) UNLIKELY
{
device->EffectList.pop_back();
return false;
}
count += 64;
}
return true;
}
ALeffect *AllocEffect(ALCdevice *device)
{
auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
[](const EffectSubList &entry) noexcept -> bool
{ return entry.FreeMask != 0; });
auto lidx = static_cast<ALuint>(std::distance(device->EffectList.begin(), sublist));
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
ASSUME(slidx < 64);
ALeffect *effect{al::construct_at(sublist->Effects + slidx)};
InitEffectParams(effect, AL_EFFECT_NULL);
/* Add 1 to avoid effect ID 0. */
effect->id = ((lidx<<6) | slidx) + 1;
sublist->FreeMask &= ~(1_u64 << slidx);
return effect;
}
void FreeEffect(ALCdevice *device, ALeffect *effect)
{
const ALuint id{effect->id - 1};
const size_t lidx{id >> 6};
const ALuint slidx{id & 0x3f};
al::destroy_at(effect);
device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
}
inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
{
const size_t lidx{(id-1) >> 6};
const ALuint slidx{(id-1) & 0x3f};
if(lidx >= device->EffectList.size()) UNLIKELY
return nullptr;
EffectSubList &sublist = device->EffectList[lidx];
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
return nullptr;
return sublist.Effects + slidx;
}
} // namespace
AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(n < 0) UNLIKELY
context->setError(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;
}
if(n == 1) LIKELY
{
/* 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);
}
}
END_API_FUNC
AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
if(n <= 0) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{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(inveffect != effects_end) UNLIKELY
{
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", *inveffect);
return;
}
/* 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);
};
std::for_each(effects, effects_end, delete_effect);
}
END_API_FUNC
AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(context) LIKELY
{
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{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(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
ALeffect *aleffect{LookupEffect(device, effect)};
if(!aleffect) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
else if(param == AL_EFFECT_TYPE)
{
bool isOk{value == AL_EFFECT_NULL};
if(!isOk)
{
for(const EffectList &effectitem : gEffectList)
{
if(value == effectitem.val && !DisabledEffects[effectitem.type])
{
isOk = true;
break;
}
}
}
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]);
return;
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
ALeffect *aleffect{LookupEffect(device, effect)};
if(!aleffect) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
else try
{
/* Call the appropriate handler */
ALeffect_setParamiv(aleffect, param, values);
}
catch(effect_exception &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(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
ALeffect *aleffect{LookupEffect(device, effect)};
if(!aleffect) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
else try
{
/* Call the appropriate handler */
ALeffect_setParamf(aleffect, param, value);
}
catch(effect_exception &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(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
ALeffect *aleffect{LookupEffect(device, effect)};
if(!aleffect) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
else try
{
/* Call the appropriate handler */
ALeffect_setParamfv(aleffect, param, values);
}
catch(effect_exception &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(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
const ALeffect *aleffect{LookupEffect(device, effect)};
if(!aleffect) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
else if(param == 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(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
const ALeffect *aleffect{LookupEffect(device, effect)};
if(!aleffect) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
else try
{
/* Call the appropriate handler */
ALeffect_getParamiv(aleffect, param, values);
}
catch(effect_exception &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(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
const ALeffect *aleffect{LookupEffect(device, effect)};
if(!aleffect) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
else try
{
/* Call the appropriate handler */
ALeffect_getParamf(aleffect, param, value);
}
catch(effect_exception &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(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
const ALeffect *aleffect{LookupEffect(device, effect)};
if(!aleffect) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
else try
{
/* Call the appropriate handler */
ALeffect_getParamfv(aleffect, param, values);
}
catch(effect_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
END_API_FUNC
void InitEffect(ALeffect *effect)
{
InitEffectParams(effect, AL_EFFECT_NULL);
}
EffectSubList::~EffectSubList()
{
uint64_t usemask{~FreeMask};
while(usemask)
{
const int idx{al::countr_zero(usemask)};
al::destroy_at(Effects+idx);
usemask &= ~(1_u64 << idx);
}
FreeMask = ~usemask;
al_free(Effects);
Effects = nullptr;
}
#define DECL(x) { #x, EFX_REVERB_PRESET_##x }
static const struct {
const char name[32];
EFXEAXREVERBPROPERTIES props;
} reverblist[] = {
DECL(GENERIC),
DECL(PADDEDCELL),
DECL(ROOM),
DECL(BATHROOM),
DECL(LIVINGROOM),
DECL(STONEROOM),
DECL(AUDITORIUM),
DECL(CONCERTHALL),
DECL(CAVE),
DECL(ARENA),
DECL(HANGAR),
DECL(CARPETEDHALLWAY),
DECL(HALLWAY),
DECL(STONECORRIDOR),
DECL(ALLEY),
DECL(FOREST),
DECL(CITY),
DECL(MOUNTAINS),
DECL(QUARRY),
DECL(PLAIN),
DECL(PARKINGLOT),
DECL(SEWERPIPE),
DECL(UNDERWATER),
DECL(DRUGGED),
DECL(DIZZY),
DECL(PSYCHOTIC),
DECL(CASTLE_SMALLROOM),
DECL(CASTLE_SHORTPASSAGE),
DECL(CASTLE_MEDIUMROOM),
DECL(CASTLE_LARGEROOM),
DECL(CASTLE_LONGPASSAGE),
DECL(CASTLE_HALL),
DECL(CASTLE_CUPBOARD),
DECL(CASTLE_COURTYARD),
DECL(CASTLE_ALCOVE),
DECL(FACTORY_SMALLROOM),
DECL(FACTORY_SHORTPASSAGE),
DECL(FACTORY_MEDIUMROOM),
DECL(FACTORY_LARGEROOM),
DECL(FACTORY_LONGPASSAGE),
DECL(FACTORY_HALL),
DECL(FACTORY_CUPBOARD),
DECL(FACTORY_COURTYARD),
DECL(FACTORY_ALCOVE),
DECL(ICEPALACE_SMALLROOM),
DECL(ICEPALACE_SHORTPASSAGE),
DECL(ICEPALACE_MEDIUMROOM),
DECL(ICEPALACE_LARGEROOM),
DECL(ICEPALACE_LONGPASSAGE),
DECL(ICEPALACE_HALL),
DECL(ICEPALACE_CUPBOARD),
DECL(ICEPALACE_COURTYARD),
DECL(ICEPALACE_ALCOVE),
DECL(SPACESTATION_SMALLROOM),
DECL(SPACESTATION_SHORTPASSAGE),
DECL(SPACESTATION_MEDIUMROOM),
DECL(SPACESTATION_LARGEROOM),
DECL(SPACESTATION_LONGPASSAGE),
DECL(SPACESTATION_HALL),
DECL(SPACESTATION_CUPBOARD),
DECL(SPACESTATION_ALCOVE),
DECL(WOODEN_SMALLROOM),
DECL(WOODEN_SHORTPASSAGE),
DECL(WOODEN_MEDIUMROOM),
DECL(WOODEN_LARGEROOM),
DECL(WOODEN_LONGPASSAGE),
DECL(WOODEN_HALL),
DECL(WOODEN_CUPBOARD),
DECL(WOODEN_COURTYARD),
DECL(WOODEN_ALCOVE),
DECL(SPORT_EMPTYSTADIUM),
DECL(SPORT_SQUASHCOURT),
DECL(SPORT_SMALLSWIMMINGPOOL),
DECL(SPORT_LARGESWIMMINGPOOL),
DECL(SPORT_GYMNASIUM),
DECL(SPORT_FULLSTADIUM),
DECL(SPORT_STADIUMTANNOY),
DECL(PREFAB_WORKSHOP),
DECL(PREFAB_SCHOOLROOM),
DECL(PREFAB_PRACTISEROOM),
DECL(PREFAB_OUTHOUSE),
DECL(PREFAB_CARAVAN),
DECL(DOME_TOMB),
DECL(PIPE_SMALL),
DECL(DOME_SAINTPAULS),
DECL(PIPE_LONGTHIN),
DECL(PIPE_LARGE),
DECL(PIPE_RESONANT),
DECL(OUTDOORS_BACKYARD),
DECL(OUTDOORS_ROLLINGPLAINS),
DECL(OUTDOORS_DEEPCANYON),
DECL(OUTDOORS_CREEK),
DECL(OUTDOORS_VALLEY),
DECL(MOOD_HEAVEN),
DECL(MOOD_HELL),
DECL(MOOD_MEMORY),
DECL(DRIVING_COMMENTATOR),
DECL(DRIVING_PITGARAGE),
DECL(DRIVING_INCAR_RACER),
DECL(DRIVING_INCAR_SPORTS),
DECL(DRIVING_INCAR_LUXURY),
DECL(DRIVING_FULLGRANDSTAND),
DECL(DRIVING_EMPTYGRANDSTAND),
DECL(DRIVING_TUNNEL),
DECL(CITY_STREETS),
DECL(CITY_SUBWAY),
DECL(CITY_MUSEUM),
DECL(CITY_LIBRARY),
DECL(CITY_UNDERPASS),
DECL(CITY_ABANDONED),
DECL(DUSTYROOM),
DECL(CHAPEL),
DECL(SMALLWATERROOM),
};
#undef DECL
void LoadReverbPreset(const char *name, ALeffect *effect)
{
if(al::strcasecmp(name, "NONE") == 0)
{
InitEffectParams(effect, AL_EFFECT_NULL);
TRACE("Loading reverb '%s'\n", "NONE");
return;
}
if(!DisabledEffects[EAXREVERB_EFFECT])
InitEffectParams(effect, AL_EFFECT_EAXREVERB);
else if(!DisabledEffects[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)
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;
return;
}
WARN("Reverb preset '%s' not found\n", name);
}
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;
}

62
externals/openal-soft/al/effect.h vendored Normal file
View File

@@ -0,0 +1,62 @@
#ifndef AL_EFFECT_H
#define AL_EFFECT_H
#include "AL/al.h"
#include "AL/efx.h"
#include "al/effects/effects.h"
#include "alc/effects/base.h"
enum {
EAXREVERB_EFFECT = 0,
REVERB_EFFECT,
AUTOWAH_EFFECT,
CHORUS_EFFECT,
COMPRESSOR_EFFECT,
DISTORTION_EFFECT,
ECHO_EFFECT,
EQUALIZER_EFFECT,
FLANGER_EFFECT,
FSHIFTER_EFFECT,
MODULATOR_EFFECT,
PSHIFTER_EFFECT,
VMORPHER_EFFECT,
DEDICATED_EFFECT,
CONVOLUTION_EFFECT,
MAX_EFFECTS
};
extern bool DisabledEffects[MAX_EFFECTS];
extern float ReverbBoost;
struct EffectList {
const char name[16];
int type;
ALenum val;
};
extern const EffectList gEffectList[16];
struct ALeffect {
// Effect type (AL_EFFECT_NULL, ...)
ALenum type{AL_EFFECT_NULL};
EffectProps Props{};
const EffectVtable *vtab{nullptr};
/* Self ID */
ALuint id{0u};
DISABLE_ALLOC()
};
void InitEffect(ALeffect *effect);
void LoadReverbPreset(const char *name, ALeffect *effect);
bool IsValidEffectType(ALenum type) noexcept;
#endif

View File

@@ -0,0 +1,252 @@
#include "config.h"
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include "AL/efx.h"
#include "alc/effects/base.h"
#include "effects.h"
#ifdef ALSOFT_EAX
#include "alnumeric.h"
#include "al/eax/exception.h"
#include "al/eax/utils.h"
#endif // ALSOFT_EAX
namespace {
void Autowah_setParamf(EffectProps *props, ALenum param, float val)
{
switch(param)
{
case AL_AUTOWAH_ATTACK_TIME:
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"};
props->Autowah.AttackTime = val;
break;
case AL_AUTOWAH_RELEASE_TIME:
if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"};
props->Autowah.ReleaseTime = val;
break;
case AL_AUTOWAH_RESONANCE:
if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"};
props->Autowah.Resonance = val;
break;
case AL_AUTOWAH_PEAK_GAIN:
if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"};
props->Autowah.PeakGain = val;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
}
}
void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals)
{ Autowah_setParamf(props, param, vals[0]); }
void Autowah_setParami(EffectProps*, ALenum param, int)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
void Autowah_setParamiv(EffectProps*, ALenum param, const int*)
{
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
param};
}
void Autowah_getParamf(const EffectProps *props, ALenum param, float *val)
{
switch(param)
{
case AL_AUTOWAH_ATTACK_TIME:
*val = props->Autowah.AttackTime;
break;
case AL_AUTOWAH_RELEASE_TIME:
*val = props->Autowah.ReleaseTime;
break;
case AL_AUTOWAH_RESONANCE:
*val = props->Autowah.Resonance;
break;
case AL_AUTOWAH_PEAK_GAIN:
*val = props->Autowah.PeakGain;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
}
}
void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals)
{ Autowah_getParamf(props, param, vals); }
void Autowah_getParami(const EffectProps*, ALenum param, int*)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
void Autowah_getParamiv(const EffectProps*, ALenum param, int*)
{
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
param};
}
EffectProps genDefaultProps() noexcept
{
EffectProps props{};
props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
return props;
}
} // namespace
DEFINE_ALEFFECT_VTABLE(Autowah);
const EffectProps AutowahEffectProps{genDefaultProps()};
#ifdef ALSOFT_EAX
namespace {
using AutowahCommitter = EaxCommitter<EaxAutowahCommitter>;
struct AttackTimeValidator {
void operator()(float flAttackTime) const
{
eax_validate_range<AutowahCommitter::Exception>(
"Attack Time",
flAttackTime,
EAXAUTOWAH_MINATTACKTIME,
EAXAUTOWAH_MAXATTACKTIME);
}
}; // AttackTimeValidator
struct ReleaseTimeValidator {
void operator()(float flReleaseTime) const
{
eax_validate_range<AutowahCommitter::Exception>(
"Release Time",
flReleaseTime,
EAXAUTOWAH_MINRELEASETIME,
EAXAUTOWAH_MAXRELEASETIME);
}
}; // ReleaseTimeValidator
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};
}
template<>
bool AutowahCommitter::commit(const EaxEffectProps &props)
{
if(props.mType == mEaxProps.mType
&& mEaxProps.mAutowah.flAttackTime == props.mAutowah.flAttackTime
&& mEaxProps.mAutowah.flReleaseTime == props.mAutowah.flReleaseTime
&& mEaxProps.mAutowah.lResonance == props.mAutowah.lResonance
&& mEaxProps.mAutowah.lPeakLevel == props.mAutowah.lPeakLevel)
return false;
mEaxProps = props;
mAlProps.Autowah.AttackTime = props.mAutowah.flAttackTime;
mAlProps.Autowah.ReleaseTime = props.mAutowah.flReleaseTime;
mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(props.mAutowah.lResonance));
mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(props.mAutowah.lPeakLevel));
return true;
}
template<>
void AutowahCommitter::SetDefaults(EaxEffectProps &props)
{
props.mType = EaxEffectType::Autowah;
props.mAutowah.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
props.mAutowah.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
props.mAutowah.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
props.mAutowah.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
}
template<>
void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXAUTOWAH_NONE: break;
case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props.mAutowah); break;
case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.mAutowah.flAttackTime); break;
case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.mAutowah.flReleaseTime); break;
case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.mAutowah.lResonance); break;
case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.mAutowah.lPeakLevel); break;
default: fail_unknown_property_id();
}
}
template<>
void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXAUTOWAH_NONE: break;
case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props.mAutowah); break;
case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.mAutowah.flAttackTime); break;
case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.mAutowah.flReleaseTime); break;
case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.mAutowah.lResonance); break;
case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.mAutowah.lPeakLevel); break;
default: fail_unknown_property_id();
}
}
#endif // ALSOFT_EAX

View File

@@ -0,0 +1,724 @@
#include "config.h"
#include <stdexcept>
#include "AL/al.h"
#include "AL/efx.h"
#include "alc/effects/base.h"
#include "aloptional.h"
#include "core/logging.h"
#include "effects.h"
#ifdef ALSOFT_EAX
#include <cassert>
#include "alnumeric.h"
#include "al/eax/exception.h"
#include "al/eax/utils.h"
#endif // ALSOFT_EAX
namespace {
static_assert(ChorusMaxDelay >= AL_CHORUS_MAX_DELAY, "Chorus max delay too small");
static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too small");
static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
inline al::optional<ChorusWaveform> WaveformFromEnum(ALenum type)
{
switch(type)
{
case AL_CHORUS_WAVEFORM_SINUSOID: return ChorusWaveform::Sinusoid;
case AL_CHORUS_WAVEFORM_TRIANGLE: return ChorusWaveform::Triangle;
}
return al::nullopt;
}
inline ALenum EnumFromWaveform(ChorusWaveform type)
{
switch(type)
{
case ChorusWaveform::Sinusoid: return AL_CHORUS_WAVEFORM_SINUSOID;
case ChorusWaveform::Triangle: return AL_CHORUS_WAVEFORM_TRIANGLE;
}
throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast<int>(type))};
}
void Chorus_setParami(EffectProps *props, ALenum param, int val)
{
switch(param)
{
case AL_CHORUS_WAVEFORM:
if(auto formopt = WaveformFromEnum(val))
props->Chorus.Waveform = *formopt;
else
throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val};
break;
case AL_CHORUS_PHASE:
if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range: %d", val};
props->Chorus.Phase = val;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
}
}
void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals)
{ Chorus_setParami(props, param, vals[0]); }
void Chorus_setParamf(EffectProps *props, ALenum param, float val)
{
switch(param)
{
case AL_CHORUS_RATE:
if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range: %f", val};
props->Chorus.Rate = val;
break;
case AL_CHORUS_DEPTH:
if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range: %f", val};
props->Chorus.Depth = val;
break;
case AL_CHORUS_FEEDBACK:
if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range: %f", val};
props->Chorus.Feedback = val;
break;
case AL_CHORUS_DELAY:
if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range: %f", val};
props->Chorus.Delay = val;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
}
}
void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals)
{ Chorus_setParamf(props, param, vals[0]); }
void Chorus_getParami(const EffectProps *props, ALenum param, int *val)
{
switch(param)
{
case AL_CHORUS_WAVEFORM:
*val = EnumFromWaveform(props->Chorus.Waveform);
break;
case AL_CHORUS_PHASE:
*val = props->Chorus.Phase;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
}
}
void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals)
{ Chorus_getParami(props, param, vals); }
void Chorus_getParamf(const EffectProps *props, ALenum param, float *val)
{
switch(param)
{
case AL_CHORUS_RATE:
*val = props->Chorus.Rate;
break;
case AL_CHORUS_DEPTH:
*val = props->Chorus.Depth;
break;
case AL_CHORUS_FEEDBACK:
*val = props->Chorus.Feedback;
break;
case AL_CHORUS_DELAY:
*val = props->Chorus.Delay;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
}
}
void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals)
{ Chorus_getParamf(props, param, vals); }
EffectProps genDefaultChorusProps() noexcept
{
EffectProps props{};
props.Chorus.Waveform = *WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM);
props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
return props;
}
void Flanger_setParami(EffectProps *props, ALenum param, int val)
{
switch(param)
{
case AL_FLANGER_WAVEFORM:
if(auto formopt = WaveformFromEnum(val))
props->Chorus.Waveform = *formopt;
else
throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val};
break;
case AL_FLANGER_PHASE:
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range: %d", val};
props->Chorus.Phase = val;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
}
}
void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals)
{ Flanger_setParami(props, param, vals[0]); }
void Flanger_setParamf(EffectProps *props, ALenum param, float val)
{
switch(param)
{
case AL_FLANGER_RATE:
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range: %f", val};
props->Chorus.Rate = val;
break;
case AL_FLANGER_DEPTH:
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range: %f", val};
props->Chorus.Depth = val;
break;
case AL_FLANGER_FEEDBACK:
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range: %f", val};
props->Chorus.Feedback = val;
break;
case AL_FLANGER_DELAY:
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range: %f", val};
props->Chorus.Delay = val;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
}
}
void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals)
{ Flanger_setParamf(props, param, vals[0]); }
void Flanger_getParami(const EffectProps *props, ALenum param, int *val)
{
switch(param)
{
case AL_FLANGER_WAVEFORM:
*val = EnumFromWaveform(props->Chorus.Waveform);
break;
case AL_FLANGER_PHASE:
*val = props->Chorus.Phase;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
}
}
void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals)
{ Flanger_getParami(props, param, vals); }
void Flanger_getParamf(const EffectProps *props, ALenum param, float *val)
{
switch(param)
{
case AL_FLANGER_RATE:
*val = props->Chorus.Rate;
break;
case AL_FLANGER_DEPTH:
*val = props->Chorus.Depth;
break;
case AL_FLANGER_FEEDBACK:
*val = props->Chorus.Feedback;
break;
case AL_FLANGER_DELAY:
*val = props->Chorus.Delay;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
}
}
void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals)
{ Flanger_getParamf(props, param, vals); }
EffectProps genDefaultFlangerProps() noexcept
{
EffectProps props{};
props.Chorus.Waveform = *WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM);
props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
return props;
}
} // namespace
DEFINE_ALEFFECT_VTABLE(Chorus);
const EffectProps ChorusEffectProps{genDefaultChorusProps()};
DEFINE_ALEFFECT_VTABLE(Flanger);
const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
#ifdef ALSOFT_EAX
namespace {
struct EaxChorusTraits {
using Props = EAXCHORUSPROPERTIES;
using Committer = EaxChorusCommitter;
static constexpr auto Field = &EaxEffectProps::mChorus;
static constexpr auto eax_effect_type() { return EaxEffectType::Chorus; }
static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; }
static constexpr auto eax_allparameters_param_id() { return EAXCHORUS_ALLPARAMETERS; }
static constexpr auto eax_waveform_param_id() { return EAXCHORUS_WAVEFORM; }
static constexpr auto eax_phase_param_id() { return EAXCHORUS_PHASE; }
static constexpr auto eax_rate_param_id() { return EAXCHORUS_RATE; }
static constexpr auto eax_depth_param_id() { return EAXCHORUS_DEPTH; }
static constexpr auto eax_feedback_param_id() { return EAXCHORUS_FEEDBACK; }
static constexpr auto eax_delay_param_id() { return EAXCHORUS_DELAY; }
static constexpr auto eax_min_waveform() { return EAXCHORUS_MINWAVEFORM; }
static constexpr auto eax_min_phase() { return EAXCHORUS_MINPHASE; }
static constexpr auto eax_min_rate() { return EAXCHORUS_MINRATE; }
static constexpr auto eax_min_depth() { return EAXCHORUS_MINDEPTH; }
static constexpr auto eax_min_feedback() { return EAXCHORUS_MINFEEDBACK; }
static constexpr auto eax_min_delay() { return EAXCHORUS_MINDELAY; }
static constexpr auto eax_max_waveform() { return EAXCHORUS_MAXWAVEFORM; }
static constexpr auto eax_max_phase() { return EAXCHORUS_MAXPHASE; }
static constexpr auto eax_max_rate() { return EAXCHORUS_MAXRATE; }
static constexpr auto eax_max_depth() { return EAXCHORUS_MAXDEPTH; }
static constexpr auto eax_max_feedback() { return EAXCHORUS_MAXFEEDBACK; }
static constexpr auto eax_max_delay() { return EAXCHORUS_MAXDELAY; }
static constexpr auto eax_default_waveform() { return EAXCHORUS_DEFAULTWAVEFORM; }
static constexpr auto eax_default_phase() { return EAXCHORUS_DEFAULTPHASE; }
static constexpr auto eax_default_rate() { return EAXCHORUS_DEFAULTRATE; }
static constexpr auto eax_default_depth() { return EAXCHORUS_DEFAULTDEPTH; }
static constexpr auto eax_default_feedback() { return EAXCHORUS_DEFAULTFEEDBACK; }
static constexpr auto eax_default_delay() { return EAXCHORUS_DEFAULTDELAY; }
static constexpr auto efx_min_waveform() { return AL_CHORUS_MIN_WAVEFORM; }
static constexpr auto efx_min_phase() { return AL_CHORUS_MIN_PHASE; }
static constexpr auto efx_min_rate() { return AL_CHORUS_MIN_RATE; }
static constexpr auto efx_min_depth() { return AL_CHORUS_MIN_DEPTH; }
static constexpr auto efx_min_feedback() { return AL_CHORUS_MIN_FEEDBACK; }
static constexpr auto efx_min_delay() { return AL_CHORUS_MIN_DELAY; }
static constexpr auto efx_max_waveform() { return AL_CHORUS_MAX_WAVEFORM; }
static constexpr auto efx_max_phase() { return AL_CHORUS_MAX_PHASE; }
static constexpr auto efx_max_rate() { return AL_CHORUS_MAX_RATE; }
static constexpr auto efx_max_depth() { return AL_CHORUS_MAX_DEPTH; }
static constexpr auto efx_max_feedback() { return AL_CHORUS_MAX_FEEDBACK; }
static constexpr auto efx_max_delay() { return AL_CHORUS_MAX_DELAY; }
static constexpr auto efx_default_waveform() { return AL_CHORUS_DEFAULT_WAVEFORM; }
static constexpr auto efx_default_phase() { return AL_CHORUS_DEFAULT_PHASE; }
static constexpr auto efx_default_rate() { return AL_CHORUS_DEFAULT_RATE; }
static constexpr auto efx_default_depth() { return AL_CHORUS_DEFAULT_DEPTH; }
static constexpr auto efx_default_feedback() { return AL_CHORUS_DEFAULT_FEEDBACK; }
static constexpr auto efx_default_delay() { return AL_CHORUS_DEFAULT_DELAY; }
static ChorusWaveform eax_waveform(unsigned long type)
{
if(type == EAX_CHORUS_SINUSOID) return ChorusWaveform::Sinusoid;
if(type == EAX_CHORUS_TRIANGLE) return ChorusWaveform::Triangle;
return ChorusWaveform::Sinusoid;
}
}; // EaxChorusTraits
struct EaxFlangerTraits {
using Props = EAXFLANGERPROPERTIES;
using Committer = EaxFlangerCommitter;
static constexpr auto Field = &EaxEffectProps::mFlanger;
static constexpr auto eax_effect_type() { return EaxEffectType::Flanger; }
static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; }
static constexpr auto eax_allparameters_param_id() { return EAXFLANGER_ALLPARAMETERS; }
static constexpr auto eax_waveform_param_id() { return EAXFLANGER_WAVEFORM; }
static constexpr auto eax_phase_param_id() { return EAXFLANGER_PHASE; }
static constexpr auto eax_rate_param_id() { return EAXFLANGER_RATE; }
static constexpr auto eax_depth_param_id() { return EAXFLANGER_DEPTH; }
static constexpr auto eax_feedback_param_id() { return EAXFLANGER_FEEDBACK; }
static constexpr auto eax_delay_param_id() { return EAXFLANGER_DELAY; }
static constexpr auto eax_min_waveform() { return EAXFLANGER_MINWAVEFORM; }
static constexpr auto eax_min_phase() { return EAXFLANGER_MINPHASE; }
static constexpr auto eax_min_rate() { return EAXFLANGER_MINRATE; }
static constexpr auto eax_min_depth() { return EAXFLANGER_MINDEPTH; }
static constexpr auto eax_min_feedback() { return EAXFLANGER_MINFEEDBACK; }
static constexpr auto eax_min_delay() { return EAXFLANGER_MINDELAY; }
static constexpr auto eax_max_waveform() { return EAXFLANGER_MAXWAVEFORM; }
static constexpr auto eax_max_phase() { return EAXFLANGER_MAXPHASE; }
static constexpr auto eax_max_rate() { return EAXFLANGER_MAXRATE; }
static constexpr auto eax_max_depth() { return EAXFLANGER_MAXDEPTH; }
static constexpr auto eax_max_feedback() { return EAXFLANGER_MAXFEEDBACK; }
static constexpr auto eax_max_delay() { return EAXFLANGER_MAXDELAY; }
static constexpr auto eax_default_waveform() { return EAXFLANGER_DEFAULTWAVEFORM; }
static constexpr auto eax_default_phase() { return EAXFLANGER_DEFAULTPHASE; }
static constexpr auto eax_default_rate() { return EAXFLANGER_DEFAULTRATE; }
static constexpr auto eax_default_depth() { return EAXFLANGER_DEFAULTDEPTH; }
static constexpr auto eax_default_feedback() { return EAXFLANGER_DEFAULTFEEDBACK; }
static constexpr auto eax_default_delay() { return EAXFLANGER_DEFAULTDELAY; }
static constexpr auto efx_min_waveform() { return AL_FLANGER_MIN_WAVEFORM; }
static constexpr auto efx_min_phase() { return AL_FLANGER_MIN_PHASE; }
static constexpr auto efx_min_rate() { return AL_FLANGER_MIN_RATE; }
static constexpr auto efx_min_depth() { return AL_FLANGER_MIN_DEPTH; }
static constexpr auto efx_min_feedback() { return AL_FLANGER_MIN_FEEDBACK; }
static constexpr auto efx_min_delay() { return AL_FLANGER_MIN_DELAY; }
static constexpr auto efx_max_waveform() { return AL_FLANGER_MAX_WAVEFORM; }
static constexpr auto efx_max_phase() { return AL_FLANGER_MAX_PHASE; }
static constexpr auto efx_max_rate() { return AL_FLANGER_MAX_RATE; }
static constexpr auto efx_max_depth() { return AL_FLANGER_MAX_DEPTH; }
static constexpr auto efx_max_feedback() { return AL_FLANGER_MAX_FEEDBACK; }
static constexpr auto efx_max_delay() { return AL_FLANGER_MAX_DELAY; }
static constexpr auto efx_default_waveform() { return AL_FLANGER_DEFAULT_WAVEFORM; }
static constexpr auto efx_default_phase() { return AL_FLANGER_DEFAULT_PHASE; }
static constexpr auto efx_default_rate() { return AL_FLANGER_DEFAULT_RATE; }
static constexpr auto efx_default_depth() { return AL_FLANGER_DEFAULT_DEPTH; }
static constexpr auto efx_default_feedback() { return AL_FLANGER_DEFAULT_FEEDBACK; }
static constexpr auto efx_default_delay() { return AL_FLANGER_DEFAULT_DELAY; }
static ChorusWaveform eax_waveform(unsigned long type)
{
if(type == EAX_FLANGER_SINUSOID) return ChorusWaveform::Sinusoid;
if(type == EAX_FLANGER_TRIANGLE) return ChorusWaveform::Triangle;
return ChorusWaveform::Sinusoid;
}
}; // EaxFlangerTraits
template<typename TTraits>
struct ChorusFlangerEffect {
using Traits = TTraits;
using Committer = typename Traits::Committer;
using Exception = typename Committer::Exception;
static constexpr auto Field = Traits::Field;
struct WaveformValidator {
void operator()(unsigned long ulWaveform) const
{
eax_validate_range<Exception>(
"Waveform",
ulWaveform,
Traits::eax_min_waveform(),
Traits::eax_max_waveform());
}
}; // WaveformValidator
struct PhaseValidator {
void operator()(long lPhase) const
{
eax_validate_range<Exception>(
"Phase",
lPhase,
Traits::eax_min_phase(),
Traits::eax_max_phase());
}
}; // PhaseValidator
struct RateValidator {
void operator()(float flRate) const
{
eax_validate_range<Exception>(
"Rate",
flRate,
Traits::eax_min_rate(),
Traits::eax_max_rate());
}
}; // RateValidator
struct DepthValidator {
void operator()(float flDepth) const
{
eax_validate_range<Exception>(
"Depth",
flDepth,
Traits::eax_min_depth(),
Traits::eax_max_depth());
}
}; // DepthValidator
struct FeedbackValidator {
void operator()(float flFeedback) const
{
eax_validate_range<Exception>(
"Feedback",
flFeedback,
Traits::eax_min_feedback(),
Traits::eax_max_feedback());
}
}; // FeedbackValidator
struct DelayValidator {
void operator()(float flDelay) const
{
eax_validate_range<Exception>(
"Delay",
flDelay,
Traits::eax_min_delay(),
Traits::eax_max_delay());
}
}; // DelayValidator
struct AllValidator {
void operator()(const typename Traits::Props& all) const
{
WaveformValidator{}(all.ulWaveform);
PhaseValidator{}(all.lPhase);
RateValidator{}(all.flRate);
DepthValidator{}(all.flDepth);
FeedbackValidator{}(all.flFeedback);
DelayValidator{}(all.flDelay);
}
}; // AllValidator
public:
static void SetDefaults(EaxEffectProps &props)
{
auto&& all = props.*Field;
props.mType = Traits::eax_effect_type();
all.ulWaveform = Traits::eax_default_waveform();
all.lPhase = Traits::eax_default_phase();
all.flRate = Traits::eax_default_rate();
all.flDepth = Traits::eax_default_depth();
all.flFeedback = Traits::eax_default_feedback();
all.flDelay = Traits::eax_default_delay();
}
static void Get(const EaxCall &call, const EaxEffectProps &props)
{
auto&& all = props.*Field;
switch(call.get_property_id())
{
case Traits::eax_none_param_id():
break;
case Traits::eax_allparameters_param_id():
call.template set_value<Exception>(all);
break;
case Traits::eax_waveform_param_id():
call.template set_value<Exception>(all.ulWaveform);
break;
case Traits::eax_phase_param_id():
call.template set_value<Exception>(all.lPhase);
break;
case Traits::eax_rate_param_id():
call.template set_value<Exception>(all.flRate);
break;
case Traits::eax_depth_param_id():
call.template set_value<Exception>(all.flDepth);
break;
case Traits::eax_feedback_param_id():
call.template set_value<Exception>(all.flFeedback);
break;
case Traits::eax_delay_param_id():
call.template set_value<Exception>(all.flDelay);
break;
default:
Committer::fail_unknown_property_id();
}
}
static void Set(const EaxCall &call, EaxEffectProps &props)
{
auto&& all = props.*Field;
switch(call.get_property_id())
{
case Traits::eax_none_param_id():
break;
case Traits::eax_allparameters_param_id():
Committer::template defer<AllValidator>(call, all);
break;
case Traits::eax_waveform_param_id():
Committer::template defer<WaveformValidator>(call, all.ulWaveform);
break;
case Traits::eax_phase_param_id():
Committer::template defer<PhaseValidator>(call, all.lPhase);
break;
case Traits::eax_rate_param_id():
Committer::template defer<RateValidator>(call, all.flRate);
break;
case Traits::eax_depth_param_id():
Committer::template defer<DepthValidator>(call, all.flDepth);
break;
case Traits::eax_feedback_param_id():
Committer::template defer<FeedbackValidator>(call, all.flFeedback);
break;
case Traits::eax_delay_param_id():
Committer::template defer<DelayValidator>(call, all.flDelay);
break;
default:
Committer::fail_unknown_property_id();
}
}
static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &al_props_)
{
if(props.mType == props_.mType)
{
auto&& src = props_.*Field;
auto&& dst = props.*Field;
if(dst.ulWaveform == src.ulWaveform && dst.lPhase == src.lPhase
&& dst.flRate == src.flRate && dst.flDepth == src.flDepth
&& dst.flFeedback == src.flFeedback && dst.flDelay == src.flDelay)
return false;
}
props_ = props;
auto&& dst = props.*Field;
al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform);
al_props_.Chorus.Phase = static_cast<int>(dst.lPhase);
al_props_.Chorus.Rate = dst.flRate;
al_props_.Chorus.Depth = dst.flDepth;
al_props_.Chorus.Feedback = dst.flFeedback;
al_props_.Chorus.Delay = dst.flDelay;
return true;
}
}; // EaxChorusFlangerEffect
using ChorusCommitter = EaxCommitter<EaxChorusCommitter>;
using FlangerCommitter = EaxCommitter<EaxFlangerCommitter>;
} // namespace
template<>
struct ChorusCommitter::Exception : public EaxException
{
explicit Exception(const char *message) : EaxException{"EAX_CHORUS_EFFECT", message}
{ }
};
template<>
[[noreturn]] void ChorusCommitter::fail(const char *message)
{
throw Exception{message};
}
template<>
bool ChorusCommitter::commit(const EaxEffectProps &props)
{
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
return Committer::Commit(props, mEaxProps, mAlProps);
}
template<>
void ChorusCommitter::SetDefaults(EaxEffectProps &props)
{
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
Committer::SetDefaults(props);
}
template<>
void ChorusCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
Committer::Get(call, props);
}
template<>
void ChorusCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
Committer::Set(call, props);
}
template<>
struct FlangerCommitter::Exception : public EaxException
{
explicit Exception(const char *message) : EaxException{"EAX_FLANGER_EFFECT", message}
{ }
};
template<>
[[noreturn]] void FlangerCommitter::fail(const char *message)
{
throw Exception{message};
}
template<>
bool FlangerCommitter::commit(const EaxEffectProps &props)
{
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
return Committer::Commit(props, mEaxProps, mAlProps);
}
template<>
void FlangerCommitter::SetDefaults(EaxEffectProps &props)
{
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
Committer::SetDefaults(props);
}
template<>
void FlangerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
Committer::Get(call, props);
}
template<>
void FlangerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
Committer::Set(call, props);
}
#endif // ALSOFT_EAX

View File

@@ -0,0 +1,162 @@
#include "config.h"
#include "AL/al.h"
#include "AL/efx.h"
#include "alc/effects/base.h"
#include "effects.h"
#ifdef ALSOFT_EAX
#include "alnumeric.h"
#include "al/eax/exception.h"
#include "al/eax/utils.h"
#endif // ALSOFT_EAX
namespace {
void Compressor_setParami(EffectProps *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);
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
param};
}
}
void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals)
{ Compressor_setParami(props, param, vals[0]); }
void Compressor_setParamf(EffectProps*, ALenum param, float)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
void Compressor_setParamfv(EffectProps*, 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)
{
switch(param)
{
case AL_COMPRESSOR_ONOFF:
*val = props->Compressor.OnOff;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
param};
}
}
void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals)
{ Compressor_getParami(props, param, vals); }
void Compressor_getParamf(const EffectProps*, ALenum param, float*)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
void Compressor_getParamfv(const EffectProps*, 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 CompressorCommitter = EaxCommitter<EaxCompressorCommitter>;
struct OnOffValidator {
void operator()(unsigned long ulOnOff) const
{
eax_validate_range<CompressorCommitter::Exception>(
"On-Off",
ulOnOff,
EAXAGCCOMPRESSOR_MINONOFF,
EAXAGCCOMPRESSOR_MAXONOFF);
}
}; // OnOffValidator
struct AllValidator {
void operator()(const EAXAGCCOMPRESSORPROPERTIES& all) const
{
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};
}
template<>
bool CompressorCommitter::commit(const EaxEffectProps &props)
{
if(props.mType == mEaxProps.mType
&& props.mCompressor.ulOnOff == mEaxProps.mCompressor.ulOnOff)
return false;
mEaxProps = props;
mAlProps.Compressor.OnOff = (props.mCompressor.ulOnOff != 0);
return true;
}
template<>
void CompressorCommitter::SetDefaults(EaxEffectProps &props)
{
props.mType = EaxEffectType::Compressor;
props.mCompressor.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF;
}
template<>
void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXAGCCOMPRESSOR_NONE: break;
case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props.mCompressor); break;
case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.mCompressor.ulOnOff); break;
default: fail_unknown_property_id();
}
}
template<>
void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXAGCCOMPRESSOR_NONE: break;
case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props.mCompressor); break;
case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.mCompressor.ulOnOff); break;
default: fail_unknown_property_id();
}
}
#endif // ALSOFT_EAX

View File

@@ -0,0 +1,93 @@
#include "config.h"
#include "AL/al.h"
#include "alc/inprogext.h"
#include "alc/effects/base.h"
#include "effects.h"
namespace {
void Convolution_setParami(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_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{};
return props;
}
} // namespace
DEFINE_ALEFFECT_VTABLE(Convolution);
const EffectProps ConvolutionEffectProps{genDefaultProps()};

View File

@@ -0,0 +1,72 @@
#include "config.h"
#include <cmath>
#include "AL/al.h"
#include "AL/alext.h"
#include "alc/effects/base.h"
#include "effects.h"
namespace {
void Dedicated_setParami(EffectProps*, ALenum param, int)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
void Dedicated_setParamiv(EffectProps*, 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)
{
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;
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 Dedicated_getParami(const EffectProps*, ALenum param, int*)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
void Dedicated_getParamiv(const EffectProps*, 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)
{
switch(param)
{
case AL_DEDICATED_GAIN:
*val = props->Dedicated.Gain;
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); }
EffectProps genDefaultProps() noexcept
{
EffectProps props{};
props.Dedicated.Gain = 1.0f;
return props;
}
} // namespace
DEFINE_ALEFFECT_VTABLE(Dedicated);
const EffectProps DedicatedEffectProps{genDefaultProps()};

View File

@@ -0,0 +1,271 @@
#include "config.h"
#include "AL/al.h"
#include "AL/efx.h"
#include "alc/effects/base.h"
#include "effects.h"
#ifdef ALSOFT_EAX
#include "alnumeric.h"
#include "al/eax/exception.h"
#include "al/eax/utils.h"
#endif // ALSOFT_EAX
namespace {
void Distortion_setParami(EffectProps*, ALenum param, int)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
void Distortion_setParamiv(EffectProps*, 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)
{
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;
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;
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;
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;
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;
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 Distortion_getParami(const EffectProps*, ALenum param, int*)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
void Distortion_getParamiv(const EffectProps*, 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)
{
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;
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); }
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 DistortionCommitter = EaxCommitter<EaxDistortionCommitter>;
struct EdgeValidator {
void operator()(float flEdge) const
{
eax_validate_range<DistortionCommitter::Exception>(
"Edge",
flEdge,
EAXDISTORTION_MINEDGE,
EAXDISTORTION_MAXEDGE);
}
}; // EdgeValidator
struct GainValidator {
void operator()(long lGain) const
{
eax_validate_range<DistortionCommitter::Exception>(
"Gain",
lGain,
EAXDISTORTION_MINGAIN,
EAXDISTORTION_MAXGAIN);
}
}; // GainValidator
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};
}
template<>
bool DistortionCommitter::commit(const EaxEffectProps &props)
{
if(props.mType == mEaxProps.mType && mEaxProps.mDistortion.flEdge == props.mDistortion.flEdge
&& mEaxProps.mDistortion.lGain == props.mDistortion.lGain
&& mEaxProps.mDistortion.flLowPassCutOff == props.mDistortion.flLowPassCutOff
&& mEaxProps.mDistortion.flEQCenter == props.mDistortion.flEQCenter
&& mEaxProps.mDistortion.flEQBandwidth == props.mDistortion.flEQBandwidth)
return false;
mEaxProps = props;
mAlProps.Distortion.Edge = props.mDistortion.flEdge;
mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(props.mDistortion.lGain));
mAlProps.Distortion.LowpassCutoff = props.mDistortion.flLowPassCutOff;
mAlProps.Distortion.EQCenter = props.mDistortion.flEQCenter;
mAlProps.Distortion.EQBandwidth = props.mDistortion.flEdge;
return true;
}
template<>
void DistortionCommitter::SetDefaults(EaxEffectProps &props)
{
props.mType = EaxEffectType::Distortion;
props.mDistortion.flEdge = EAXDISTORTION_DEFAULTEDGE;
props.mDistortion.lGain = EAXDISTORTION_DEFAULTGAIN;
props.mDistortion.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
props.mDistortion.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
props.mDistortion.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
}
template<>
void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXDISTORTION_NONE: break;
case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props.mDistortion); break;
case EAXDISTORTION_EDGE: call.set_value<Exception>(props.mDistortion.flEdge); break;
case EAXDISTORTION_GAIN: call.set_value<Exception>(props.mDistortion.lGain); break;
case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.mDistortion.flLowPassCutOff); break;
case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.mDistortion.flEQCenter); break;
case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.mDistortion.flEQBandwidth); break;
default: fail_unknown_property_id();
}
}
template<>
void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXDISTORTION_NONE: break;
case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props.mDistortion); break;
case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.mDistortion.flEdge); break;
case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.mDistortion.lGain); break;
case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.mDistortion.flLowPassCutOff); break;
case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.mDistortion.flEQCenter); break;
case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.mDistortion.flEQBandwidth); break;
default: fail_unknown_property_id();
}
}
#endif // ALSOFT_EAX

View File

@@ -0,0 +1,268 @@
#include "config.h"
#include "AL/al.h"
#include "AL/efx.h"
#include "alc/effects/base.h"
#include "effects.h"
#ifdef ALSOFT_EAX
#include "alnumeric.h"
#include "al/eax/exception.h"
#include "al/eax/utils.h"
#endif // ALSOFT_EAX
namespace {
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)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
void Echo_setParamiv(EffectProps*, 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)
{
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;
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;
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;
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;
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;
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 Echo_getParami(const EffectProps*, ALenum param, int*)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
void Echo_getParamiv(const EffectProps*, 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)
{
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;
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); }
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 EchoCommitter = EaxCommitter<EaxEchoCommitter>;
struct DelayValidator {
void operator()(float flDelay) const
{
eax_validate_range<EchoCommitter::Exception>(
"Delay",
flDelay,
EAXECHO_MINDELAY,
EAXECHO_MAXDELAY);
}
}; // DelayValidator
struct LrDelayValidator {
void operator()(float flLRDelay) const
{
eax_validate_range<EchoCommitter::Exception>(
"LR Delay",
flLRDelay,
EAXECHO_MINLRDELAY,
EAXECHO_MAXLRDELAY);
}
}; // LrDelayValidator
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};
}
template<>
bool EchoCommitter::commit(const EaxEffectProps &props)
{
if(props.mType == mEaxProps.mType && mEaxProps.mEcho.flDelay == props.mEcho.flDelay
&& mEaxProps.mEcho.flLRDelay == props.mEcho.flLRDelay
&& mEaxProps.mEcho.flDamping == props.mEcho.flDamping
&& mEaxProps.mEcho.flFeedback == props.mEcho.flFeedback
&& mEaxProps.mEcho.flSpread == props.mEcho.flSpread)
return false;
mEaxProps = props;
mAlProps.Echo.Delay = props.mEcho.flDelay;
mAlProps.Echo.LRDelay = props.mEcho.flLRDelay;
mAlProps.Echo.Damping = props.mEcho.flDamping;
mAlProps.Echo.Feedback = props.mEcho.flFeedback;
mAlProps.Echo.Spread = props.mEcho.flSpread;
return true;
}
template<>
void EchoCommitter::SetDefaults(EaxEffectProps &props)
{
props.mType = EaxEffectType::Echo;
props.mEcho.flDelay = EAXECHO_DEFAULTDELAY;
props.mEcho.flLRDelay = EAXECHO_DEFAULTLRDELAY;
props.mEcho.flDamping = EAXECHO_DEFAULTDAMPING;
props.mEcho.flFeedback = EAXECHO_DEFAULTFEEDBACK;
props.mEcho.flSpread = EAXECHO_DEFAULTSPREAD;
}
template<>
void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXECHO_NONE: break;
case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props.mEcho); break;
case EAXECHO_DELAY: call.set_value<Exception>(props.mEcho.flDelay); break;
case EAXECHO_LRDELAY: call.set_value<Exception>(props.mEcho.flLRDelay); break;
case EAXECHO_DAMPING: call.set_value<Exception>(props.mEcho.flDamping); break;
case EAXECHO_FEEDBACK: call.set_value<Exception>(props.mEcho.flFeedback); break;
case EAXECHO_SPREAD: call.set_value<Exception>(props.mEcho.flSpread); break;
default: fail_unknown_property_id();
}
}
template<>
void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXECHO_NONE: break;
case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props.mEcho); break;
case EAXECHO_DELAY: defer<DelayValidator>(call, props.mEcho.flDelay); break;
case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.mEcho.flLRDelay); break;
case EAXECHO_DAMPING: defer<DampingValidator>(call, props.mEcho.flDamping); break;
case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.mEcho.flFeedback); break;
case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.mEcho.flSpread); break;
default: fail_unknown_property_id();
}
}
#endif // ALSOFT_EAX

View File

@@ -0,0 +1,9 @@
#include "config.h"
#ifdef ALSOFT_EAX
#include <cassert>
#include "AL/efx.h"
#include "effects.h"
#endif // ALSOFT_EAX

View File

@@ -0,0 +1,88 @@
#ifndef AL_EFFECTS_EFFECTS_H
#define AL_EFFECTS_EFFECTS_H
#include "AL/al.h"
#include "core/except.h"
#ifdef ALSOFT_EAX
#include "al/eax/effect.h"
#endif // ALSOFT_EAX
union EffectProps;
class effect_exception final : public al::base_exception {
ALenum mErrorCode;
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, ...);
~effect_exception() override;
ALenum errorCode() const noexcept { return mErrorCode; }
};
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, \
}
/* Default properties for the given effect types. */
extern const EffectProps NullEffectProps;
extern const EffectProps ReverbEffectProps;
extern const EffectProps StdReverbEffectProps;
extern const EffectProps AutowahEffectProps;
extern const EffectProps ChorusEffectProps;
extern const EffectProps CompressorEffectProps;
extern const EffectProps DistortionEffectProps;
extern const EffectProps EchoEffectProps;
extern const EffectProps EqualizerEffectProps;
extern const EffectProps FlangerEffectProps;
extern const EffectProps FshifterEffectProps;
extern const EffectProps ModulatorEffectProps;
extern const EffectProps PshifterEffectProps;
extern const EffectProps VmorpherEffectProps;
extern const EffectProps DedicatedEffectProps;
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;
#endif /* AL_EFFECTS_EFFECTS_H */

View File

@@ -0,0 +1,411 @@
#include "config.h"
#include "AL/al.h"
#include "AL/efx.h"
#include "alc/effects/base.h"
#include "effects.h"
#ifdef ALSOFT_EAX
#include "alnumeric.h"
#include "al/eax/exception.h"
#include "al/eax/utils.h"
#endif // ALSOFT_EAX
namespace {
void Equalizer_setParami(EffectProps*, ALenum param, int)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
void Equalizer_setParamiv(EffectProps*, ALenum param, const int*)
{
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
param};
}
void Equalizer_setParamf(EffectProps *props, ALenum param, float val)
{
switch(param)
{
case AL_EQUALIZER_LOW_GAIN:
if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band gain out of range"};
props->Equalizer.LowGain = val;
break;
case AL_EQUALIZER_LOW_CUTOFF:
if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band cutoff out of range"};
props->Equalizer.LowCutoff = val;
break;
case AL_EQUALIZER_MID1_GAIN:
if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band gain out of range"};
props->Equalizer.Mid1Gain = val;
break;
case AL_EQUALIZER_MID1_CENTER:
if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band center out of range"};
props->Equalizer.Mid1Center = val;
break;
case AL_EQUALIZER_MID1_WIDTH:
if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band width out of range"};
props->Equalizer.Mid1Width = val;
break;
case AL_EQUALIZER_MID2_GAIN:
if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band gain out of range"};
props->Equalizer.Mid2Gain = val;
break;
case AL_EQUALIZER_MID2_CENTER:
if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band center out of range"};
props->Equalizer.Mid2Center = val;
break;
case AL_EQUALIZER_MID2_WIDTH:
if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band width out of range"};
props->Equalizer.Mid2Width = val;
break;
case AL_EQUALIZER_HIGH_GAIN:
if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band gain out of range"};
props->Equalizer.HighGain = val;
break;
case AL_EQUALIZER_HIGH_CUTOFF:
if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"};
props->Equalizer.HighCutoff = val;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
}
}
void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals)
{ Equalizer_setParamf(props, param, vals[0]); }
void Equalizer_getParami(const EffectProps*, ALenum param, int*)
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
void Equalizer_getParamiv(const EffectProps*, ALenum param, int*)
{
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
param};
}
void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val)
{
switch(param)
{
case AL_EQUALIZER_LOW_GAIN:
*val = props->Equalizer.LowGain;
break;
case AL_EQUALIZER_LOW_CUTOFF:
*val = props->Equalizer.LowCutoff;
break;
case AL_EQUALIZER_MID1_GAIN:
*val = props->Equalizer.Mid1Gain;
break;
case AL_EQUALIZER_MID1_CENTER:
*val = props->Equalizer.Mid1Center;
break;
case AL_EQUALIZER_MID1_WIDTH:
*val = props->Equalizer.Mid1Width;
break;
case AL_EQUALIZER_MID2_GAIN:
*val = props->Equalizer.Mid2Gain;
break;
case AL_EQUALIZER_MID2_CENTER:
*val = props->Equalizer.Mid2Center;
break;
case AL_EQUALIZER_MID2_WIDTH:
*val = props->Equalizer.Mid2Width;
break;
case AL_EQUALIZER_HIGH_GAIN:
*val = props->Equalizer.HighGain;
break;
case AL_EQUALIZER_HIGH_CUTOFF:
*val = props->Equalizer.HighCutoff;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
}
}
void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals)
{ Equalizer_getParamf(props, param, vals); }
EffectProps genDefaultProps() noexcept
{
EffectProps props{};
props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
return props;
}
} // namespace
DEFINE_ALEFFECT_VTABLE(Equalizer);
const EffectProps EqualizerEffectProps{genDefaultProps()};
#ifdef ALSOFT_EAX
namespace {
using EqualizerCommitter = EaxCommitter<EaxEqualizerCommitter>;
struct LowGainValidator {
void operator()(long lLowGain) const
{
eax_validate_range<EqualizerCommitter::Exception>(
"Low Gain",
lLowGain,
EAXEQUALIZER_MINLOWGAIN,
EAXEQUALIZER_MAXLOWGAIN);
}
}; // LowGainValidator
struct LowCutOffValidator {
void operator()(float flLowCutOff) const
{
eax_validate_range<EqualizerCommitter::Exception>(
"Low Cutoff",
flLowCutOff,
EAXEQUALIZER_MINLOWCUTOFF,
EAXEQUALIZER_MAXLOWCUTOFF);
}
}; // LowCutOffValidator
struct Mid1GainValidator {
void operator()(long lMid1Gain) const
{
eax_validate_range<EqualizerCommitter::Exception>(
"Mid1 Gain",
lMid1Gain,
EAXEQUALIZER_MINMID1GAIN,
EAXEQUALIZER_MAXMID1GAIN);
}
}; // Mid1GainValidator
struct Mid1CenterValidator {
void operator()(float flMid1Center) const
{
eax_validate_range<EqualizerCommitter::Exception>(
"Mid1 Center",
flMid1Center,
EAXEQUALIZER_MINMID1CENTER,
EAXEQUALIZER_MAXMID1CENTER);
}
}; // Mid1CenterValidator
struct Mid1WidthValidator {
void operator()(float flMid1Width) const
{
eax_validate_range<EqualizerCommitter::Exception>(
"Mid1 Width",
flMid1Width,
EAXEQUALIZER_MINMID1WIDTH,
EAXEQUALIZER_MAXMID1WIDTH);
}
}; // Mid1WidthValidator
struct Mid2GainValidator {
void operator()(long lMid2Gain) const
{
eax_validate_range<EqualizerCommitter::Exception>(
"Mid2 Gain",
lMid2Gain,
EAXEQUALIZER_MINMID2GAIN,
EAXEQUALIZER_MAXMID2GAIN);
}
}; // Mid2GainValidator
struct Mid2CenterValidator {
void operator()(float flMid2Center) const
{
eax_validate_range<EqualizerCommitter::Exception>(
"Mid2 Center",
flMid2Center,
EAXEQUALIZER_MINMID2CENTER,
EAXEQUALIZER_MAXMID2CENTER);
}
}; // Mid2CenterValidator
struct Mid2WidthValidator {
void operator()(float flMid2Width) const
{
eax_validate_range<EqualizerCommitter::Exception>(
"Mid2 Width",
flMid2Width,
EAXEQUALIZER_MINMID2WIDTH,
EAXEQUALIZER_MAXMID2WIDTH);
}
}; // Mid2WidthValidator
struct HighGainValidator {
void operator()(long lHighGain) const
{
eax_validate_range<EqualizerCommitter::Exception>(
"High Gain",
lHighGain,
EAXEQUALIZER_MINHIGHGAIN,
EAXEQUALIZER_MAXHIGHGAIN);
}
}; // HighGainValidator
struct HighCutOffValidator {
void operator()(float flHighCutOff) const
{
eax_validate_range<EqualizerCommitter::Exception>(
"High Cutoff",
flHighCutOff,
EAXEQUALIZER_MINHIGHCUTOFF,
EAXEQUALIZER_MAXHIGHCUTOFF);
}
}; // HighCutOffValidator
struct AllValidator {
void operator()(const EAXEQUALIZERPROPERTIES& all) const
{
LowGainValidator{}(all.lLowGain);
LowCutOffValidator{}(all.flLowCutOff);
Mid1GainValidator{}(all.lMid1Gain);
Mid1CenterValidator{}(all.flMid1Center);
Mid1WidthValidator{}(all.flMid1Width);
Mid2GainValidator{}(all.lMid2Gain);
Mid2CenterValidator{}(all.flMid2Center);
Mid2WidthValidator{}(all.flMid2Width);
HighGainValidator{}(all.lHighGain);
HighCutOffValidator{}(all.flHighCutOff);
}
}; // AllValidator
} // namespace
template<>
struct EqualizerCommitter::Exception : public EaxException {
explicit Exception(const char* message) : EaxException{"EAX_EQUALIZER_EFFECT", message}
{ }
};
template<>
[[noreturn]] void EqualizerCommitter::fail(const char *message)
{
throw Exception{message};
}
template<>
bool EqualizerCommitter::commit(const EaxEffectProps &props)
{
if(props.mType == mEaxProps.mType && mEaxProps.mEqualizer.lLowGain == props.mEqualizer.lLowGain
&& mEaxProps.mEqualizer.flLowCutOff == props.mEqualizer.flLowCutOff
&& mEaxProps.mEqualizer.lMid1Gain == props.mEqualizer.lMid1Gain
&& mEaxProps.mEqualizer.flMid1Center == props.mEqualizer.flMid1Center
&& mEaxProps.mEqualizer.flMid1Width == props.mEqualizer.flMid1Width
&& mEaxProps.mEqualizer.lMid2Gain == props.mEqualizer.lMid2Gain
&& mEaxProps.mEqualizer.flMid2Center == props.mEqualizer.flMid2Center
&& mEaxProps.mEqualizer.flMid2Width == props.mEqualizer.flMid2Width
&& mEaxProps.mEqualizer.lHighGain == props.mEqualizer.lHighGain
&& mEaxProps.mEqualizer.flHighCutOff == props.mEqualizer.flHighCutOff)
return false;
mEaxProps = props;
mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lLowGain));
mAlProps.Equalizer.LowCutoff = props.mEqualizer.flLowCutOff;
mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid1Gain));
mAlProps.Equalizer.Mid1Center = props.mEqualizer.flMid1Center;
mAlProps.Equalizer.Mid1Width = props.mEqualizer.flMid1Width;
mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid2Gain));
mAlProps.Equalizer.Mid2Center = props.mEqualizer.flMid2Center;
mAlProps.Equalizer.Mid2Width = props.mEqualizer.flMid2Width;
mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lHighGain));
mAlProps.Equalizer.HighCutoff = props.mEqualizer.flHighCutOff;
return true;
}
template<>
void EqualizerCommitter::SetDefaults(EaxEffectProps &props)
{
props.mType = EaxEffectType::Equalizer;
props.mEqualizer.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
props.mEqualizer.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
props.mEqualizer.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
props.mEqualizer.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
props.mEqualizer.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
props.mEqualizer.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
props.mEqualizer.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
props.mEqualizer.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
props.mEqualizer.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
props.mEqualizer.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
}
template<>
void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXEQUALIZER_NONE: break;
case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props.mEqualizer); break;
case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.mEqualizer.lLowGain); break;
case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.mEqualizer.flLowCutOff); break;
case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.mEqualizer.lMid1Gain); break;
case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.mEqualizer.flMid1Center); break;
case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.mEqualizer.flMid1Width); break;
case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.mEqualizer.lMid2Gain); break;
case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.mEqualizer.flMid2Center); break;
case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.mEqualizer.flMid2Width); break;
case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.mEqualizer.lHighGain); break;
case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.mEqualizer.flHighCutOff); break;
default: fail_unknown_property_id();
}
}
template<>
void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXEQUALIZER_NONE: break;
case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props.mEqualizer); break;
case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.mEqualizer.lLowGain); break;
case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.mEqualizer.flLowCutOff); break;
case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.mEqualizer.lMid1Gain); break;
case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.mEqualizer.flMid1Center); break;
case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.mEqualizer.flMid1Width); break;
case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.mEqualizer.lMid2Gain); break;
case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.mEqualizer.flMid2Center); break;
case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.mEqualizer.flMid2Width); break;
case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.mEqualizer.lHighGain); break;
case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.mEqualizer.flHighCutOff); break;
default: fail_unknown_property_id();
}
}
#endif // ALSOFT_EAX

View File

@@ -0,0 +1,264 @@
#include "config.h"
#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"
#endif // ALSOFT_EAX
namespace {
al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
{
switch(value)
{
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;
}
ALenum EnumFromDirection(FShifterDirection dir)
{
switch(dir)
{
case FShifterDirection::Down: return AL_FREQUENCY_SHIFTER_DIRECTION_DOWN;
case FShifterDirection::Up: return AL_FREQUENCY_SHIFTER_DIRECTION_UP;
case FShifterDirection::Off: return AL_FREQUENCY_SHIFTER_DIRECTION_OFF;
}
throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast<int>(dir))};
}
void Fshifter_setParamf(EffectProps *props, ALenum param, float val)
{
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};
}
}
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)
{
switch(param)
{
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
if(auto diropt = DirectionFromEmum(val))
props->Fshifter.LeftDirection = *diropt;
else
throw effect_exception{AL_INVALID_VALUE,
"Unsupported frequency shifter left direction: 0x%04x", val};
break;
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
if(auto diropt = DirectionFromEmum(val))
props->Fshifter.RightDirection = *diropt;
else
throw effect_exception{AL_INVALID_VALUE,
"Unsupported frequency shifter right direction: 0x%04x", val};
break;
default:
throw effect_exception{AL_INVALID_ENUM,
"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 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)
{
switch(param)
{
case AL_FREQUENCY_SHIFTER_FREQUENCY:
*val = props->Fshifter.Frequency;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
param};
}
}
void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals)
{ Fshifter_getParamf(props, param, vals); }
EffectProps genDefaultProps() noexcept
{
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;
}
} // namespace
DEFINE_ALEFFECT_VTABLE(Fshifter);
const EffectProps FshifterEffectProps{genDefaultProps()};
#ifdef ALSOFT_EAX
namespace {
using FrequencyShifterCommitter = EaxCommitter<EaxFrequencyShifterCommitter>;
struct FrequencyValidator {
void operator()(float flFrequency) const
{
eax_validate_range<FrequencyShifterCommitter::Exception>(
"Frequency",
flFrequency,
EAXFREQUENCYSHIFTER_MINFREQUENCY,
EAXFREQUENCYSHIFTER_MAXFREQUENCY);
}
}; // FrequencyValidator
struct LeftDirectionValidator {
void operator()(unsigned long ulLeftDirection) const
{
eax_validate_range<FrequencyShifterCommitter::Exception>(
"Left Direction",
ulLeftDirection,
EAXFREQUENCYSHIFTER_MINLEFTDIRECTION,
EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION);
}
}; // LeftDirectionValidator
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};
}
template<>
bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
{
if(props.mType == mEaxProps.mType
&& mEaxProps.mFrequencyShifter.flFrequency == props.mFrequencyShifter.flFrequency
&& mEaxProps.mFrequencyShifter.ulLeftDirection == props.mFrequencyShifter.ulLeftDirection
&& mEaxProps.mFrequencyShifter.ulRightDirection == props.mFrequencyShifter.ulRightDirection)
return false;
mEaxProps = props;
auto get_direction = [](unsigned long dir) noexcept
{
if(dir == EAX_FREQUENCYSHIFTER_DOWN)
return FShifterDirection::Down;
if(dir == EAX_FREQUENCYSHIFTER_UP)
return FShifterDirection::Up;
return FShifterDirection::Off;
};
mAlProps.Fshifter.Frequency = props.mFrequencyShifter.flFrequency;
mAlProps.Fshifter.LeftDirection = get_direction(props.mFrequencyShifter.ulLeftDirection);
mAlProps.Fshifter.RightDirection = get_direction(props.mFrequencyShifter.ulRightDirection);
return true;
}
template<>
void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
{
props.mType = EaxEffectType::FrequencyShifter;
props.mFrequencyShifter.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
props.mFrequencyShifter.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
props.mFrequencyShifter.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
}
template<>
void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXFREQUENCYSHIFTER_NONE: break;
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mFrequencyShifter); break;
case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.mFrequencyShifter.flFrequency); break;
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulLeftDirection); break;
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulRightDirection); break;
default: fail_unknown_property_id();
}
}
template<>
void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXFREQUENCYSHIFTER_NONE: break;
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mFrequencyShifter); break;
case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.mFrequencyShifter.flFrequency); break;
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.mFrequencyShifter.ulLeftDirection); break;
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.mFrequencyShifter.ulRightDirection); break;
default: fail_unknown_property_id();
}
}
#endif // ALSOFT_EAX

View File

@@ -0,0 +1,272 @@
#include "config.h"
#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"
#endif // ALSOFT_EAX
namespace {
al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
{
switch(value)
{
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;
}
ALenum EnumFromWaveform(ModulatorWaveform type)
{
switch(type)
{
case ModulatorWaveform::Sinusoid: return AL_RING_MODULATOR_SINUSOID;
case ModulatorWaveform::Sawtooth: return AL_RING_MODULATOR_SAWTOOTH;
case ModulatorWaveform::Square: return AL_RING_MODULATOR_SQUARE;
}
throw std::runtime_error{"Invalid modulator waveform: " +
std::to_string(static_cast<int>(type))};
}
void Modulator_setParamf(EffectProps *props, ALenum param, float val)
{
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};
}
}
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)
{
switch(param)
{
case AL_RING_MODULATOR_FREQUENCY:
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
Modulator_setParamf(props, param, static_cast<float>(val));
break;
case AL_RING_MODULATOR_WAVEFORM:
if(auto formopt = WaveformFromEmum(val))
props->Modulator.Waveform = *formopt;
else
throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
param};
}
}
void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals)
{ Modulator_setParami(props, param, vals[0]); }
void Modulator_getParami(const EffectProps *props, ALenum param, int *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);
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;
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); }
EffectProps genDefaultProps() noexcept
{
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;
}
} // namespace
DEFINE_ALEFFECT_VTABLE(Modulator);
const EffectProps ModulatorEffectProps{genDefaultProps()};
#ifdef ALSOFT_EAX
namespace {
using ModulatorCommitter = EaxCommitter<EaxModulatorCommitter>;
struct FrequencyValidator {
void operator()(float flFrequency) const
{
eax_validate_range<ModulatorCommitter::Exception>(
"Frequency",
flFrequency,
EAXRINGMODULATOR_MINFREQUENCY,
EAXRINGMODULATOR_MAXFREQUENCY);
}
}; // FrequencyValidator
struct HighPassCutOffValidator {
void operator()(float flHighPassCutOff) const
{
eax_validate_range<ModulatorCommitter::Exception>(
"High-Pass Cutoff",
flHighPassCutOff,
EAXRINGMODULATOR_MINHIGHPASSCUTOFF,
EAXRINGMODULATOR_MAXHIGHPASSCUTOFF);
}
}; // HighPassCutOffValidator
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};
}
template<>
bool ModulatorCommitter::commit(const EaxEffectProps &props)
{
if(props.mType == mEaxProps.mType
&& mEaxProps.mModulator.flFrequency == props.mModulator.flFrequency
&& mEaxProps.mModulator.flHighPassCutOff == props.mModulator.flHighPassCutOff
&& mEaxProps.mModulator.ulWaveform == props.mModulator.ulWaveform)
return false;
mEaxProps = props;
auto get_waveform = [](unsigned long form)
{
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;
};
mAlProps.Modulator.Frequency = props.mModulator.flFrequency;
mAlProps.Modulator.HighPassCutoff = props.mModulator.flHighPassCutOff;
mAlProps.Modulator.Waveform = get_waveform(props.mModulator.ulWaveform);
return true;
}
template<>
void ModulatorCommitter::SetDefaults(EaxEffectProps &props)
{
props.mType = EaxEffectType::Modulator;
props.mModulator.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
props.mModulator.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
props.mModulator.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
}
template<>
void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXRINGMODULATOR_NONE: break;
case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props.mModulator); break;
case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.mModulator.flFrequency); break;
case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.mModulator.flHighPassCutOff); break;
case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.mModulator.ulWaveform); break;
default: fail_unknown_property_id();
}
}
template<>
void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
switch (call.get_property_id())
{
case EAXRINGMODULATOR_NONE: break;
case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props.mModulator); break;
case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.mModulator.flFrequency); break;
case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.mModulator.flHighPassCutOff); break;
case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.mModulator.ulWaveform); break;
default: fail_unknown_property_id();
}
}
#endif // ALSOFT_EAX

View File

@@ -0,0 +1,149 @@
#include "config.h"
#include "AL/al.h"
#include "AL/efx.h"
#include "alc/effects/base.h"
#include "effects.h"
#ifdef ALSOFT_EAX
#include "al/eax/exception.h"
#endif // ALSOFT_EAX
namespace {
void Null_setParami(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_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;
}
} // namespace
DEFINE_ALEFFECT_VTABLE(Null);
const EffectProps NullEffectProps{genDefaultProps()};
#ifdef ALSOFT_EAX
namespace {
using NullCommitter = EaxCommitter<EaxNullCommitter>;
} // namespace
template<>
struct NullCommitter::Exception : public EaxException
{
explicit Exception(const char *message) : EaxException{"EAX_NULL_EFFECT", message}
{ }
};
template<>
[[noreturn]] void NullCommitter::fail(const char *message)
{
throw Exception{message};
}
template<>
bool NullCommitter::commit(const EaxEffectProps &props)
{
const bool ret{props.mType != mEaxProps.mType};
mEaxProps = props;
return ret;
}
template<>
void NullCommitter::SetDefaults(EaxEffectProps &props)
{
props = EaxEffectProps{};
props.mType = EaxEffectType::None;
}
template<>
void NullCommitter::Get(const EaxCall &call, const EaxEffectProps&)
{
if(call.get_property_id() != 0)
fail_unknown_property_id();
}
template<>
void NullCommitter::Set(const EaxCall &call, EaxEffectProps&)
{
if(call.get_property_id() != 0)
fail_unknown_property_id();
}
#endif // ALSOFT_EAX

View File

@@ -0,0 +1,191 @@
#include "config.h"
#include "AL/al.h"
#include "AL/efx.h"
#include "alc/effects/base.h"
#include "effects.h"
#ifdef ALSOFT_EAX
#include "alnumeric.h"
#include "al/eax/exception.h"
#include "al/eax/utils.h"
#endif // ALSOFT_EAX
namespace {
void 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*)
{
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
param};
}
void Pshifter_setParami(EffectProps *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;
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;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
param};
}
}
void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
{ Pshifter_setParami(props, param, vals[0]); }
void Pshifter_getParami(const EffectProps *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;
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 Pshifter_getParamf(const EffectProps*, 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*)
{
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 PitchShifterCommitter = EaxCommitter<EaxPitchShifterCommitter>;
struct CoarseTuneValidator {
void operator()(long lCoarseTune) const
{
eax_validate_range<PitchShifterCommitter::Exception>(
"Coarse Tune",
lCoarseTune,
EAXPITCHSHIFTER_MINCOARSETUNE,
EAXPITCHSHIFTER_MAXCOARSETUNE);
}
}; // CoarseTuneValidator
struct FineTuneValidator {
void operator()(long lFineTune) const
{
eax_validate_range<PitchShifterCommitter::Exception>(
"Fine Tune",
lFineTune,
EAXPITCHSHIFTER_MINFINETUNE,
EAXPITCHSHIFTER_MAXFINETUNE);
}
}; // FineTuneValidator
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};
}
template<>
bool PitchShifterCommitter::commit(const EaxEffectProps &props)
{
if(props.mType == mEaxProps.mType
&& mEaxProps.mPitchShifter.lCoarseTune == props.mPitchShifter.lCoarseTune
&& mEaxProps.mPitchShifter.lFineTune == props.mPitchShifter.lFineTune)
return false;
mEaxProps = props;
mAlProps.Pshifter.CoarseTune = static_cast<int>(mEaxProps.mPitchShifter.lCoarseTune);
mAlProps.Pshifter.FineTune = static_cast<int>(mEaxProps.mPitchShifter.lFineTune);
return true;
}
template<>
void PitchShifterCommitter::SetDefaults(EaxEffectProps &props)
{
props.mType = EaxEffectType::PitchShifter;
props.mPitchShifter.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE;
props.mPitchShifter.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE;
}
template<>
void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXPITCHSHIFTER_NONE: break;
case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mPitchShifter); break;
case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.mPitchShifter.lCoarseTune); break;
case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.mPitchShifter.lFineTune); break;
default: fail_unknown_property_id();
}
}
template<>
void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXPITCHSHIFTER_NONE: break;
case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mPitchShifter); break;
case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.mPitchShifter.lCoarseTune); break;
case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.mPitchShifter.lFineTune); break;
default: fail_unknown_property_id();
}
}
#endif // ALSOFT_EAX

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,520 @@
#include "config.h"
#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"
#endif // ALSOFT_EAX
namespace {
al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
{
#define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \
return VMorpherPhenome::x
switch(val)
{
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 al::nullopt;
#undef HANDLE_PHENOME
}
ALenum EnumFromPhenome(VMorpherPhenome phenome)
{
#define HANDLE_PHENOME(x) case VMorpherPhenome::x: return AL_VOCAL_MORPHER_PHONEME_ ## x
switch(phenome)
{
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);
}
throw std::runtime_error{"Invalid phenome: "+std::to_string(static_cast<int>(phenome))};
#undef HANDLE_PHENOME
}
al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
{
switch(value)
{
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;
}
ALenum EnumFromWaveform(VMorpherWaveform type)
{
switch(type)
{
case VMorpherWaveform::Sinusoid: return AL_VOCAL_MORPHER_WAVEFORM_SINUSOID;
case VMorpherWaveform::Triangle: return AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE;
case VMorpherWaveform::Sawtooth: return AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH;
}
throw std::runtime_error{"Invalid vocal morpher waveform: " +
std::to_string(static_cast<int>(type))};
}
void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
{
switch(param)
{
case AL_VOCAL_MORPHER_PHONEMEA:
if(auto phenomeopt = PhenomeFromEnum(val))
props->Vmorpher.PhonemeA = *phenomeopt;
else
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val};
break;
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;
break;
case AL_VOCAL_MORPHER_PHONEMEB:
if(auto phenomeopt = PhenomeFromEnum(val))
props->Vmorpher.PhonemeB = *phenomeopt;
else
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val};
break;
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;
break;
case AL_VOCAL_MORPHER_WAVEFORM:
if(auto formopt = WaveformFromEmum(val))
props->Vmorpher.Waveform = *formopt;
else
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val};
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
param};
}
}
void Vmorpher_setParamiv(EffectProps*, 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)
{
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;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x",
param};
}
}
void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals)
{ Vmorpher_setParamf(props, param, vals[0]); }
void Vmorpher_getParami(const EffectProps *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;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
param};
}
}
void Vmorpher_getParamiv(const EffectProps*, 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)
{
switch(param)
{
case AL_VOCAL_MORPHER_RATE:
*val = props->Vmorpher.Rate;
break;
default:
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x",
param};
}
}
void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals)
{ Vmorpher_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 VocalMorpherCommitter = EaxCommitter<EaxVocalMorpherCommitter>;
struct PhonemeAValidator {
void operator()(unsigned long ulPhonemeA) const
{
eax_validate_range<VocalMorpherCommitter::Exception>(
"Phoneme A",
ulPhonemeA,
EAXVOCALMORPHER_MINPHONEMEA,
EAXVOCALMORPHER_MAXPHONEMEA);
}
}; // PhonemeAValidator
struct PhonemeACoarseTuningValidator {
void operator()(long lPhonemeACoarseTuning) const
{
eax_validate_range<VocalMorpherCommitter::Exception>(
"Phoneme A Coarse Tuning",
lPhonemeACoarseTuning,
EAXVOCALMORPHER_MINPHONEMEACOARSETUNING,
EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING);
}
}; // PhonemeACoarseTuningValidator
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};
}
template<>
bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
{
if(props.mType == mEaxProps.mType
&& mEaxProps.mVocalMorpher.ulPhonemeA == props.mVocalMorpher.ulPhonemeA
&& mEaxProps.mVocalMorpher.lPhonemeACoarseTuning == props.mVocalMorpher.lPhonemeACoarseTuning
&& mEaxProps.mVocalMorpher.ulPhonemeB == props.mVocalMorpher.ulPhonemeB
&& mEaxProps.mVocalMorpher.lPhonemeBCoarseTuning == props.mVocalMorpher.lPhonemeBCoarseTuning
&& mEaxProps.mVocalMorpher.ulWaveform == props.mVocalMorpher.ulWaveform
&& mEaxProps.mVocalMorpher.flRate == props.mVocalMorpher.flRate)
return false;
mEaxProps = props;
auto get_phoneme = [](unsigned long phoneme) noexcept
{
#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
{
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;
};
mAlProps.Vmorpher.PhonemeA = get_phoneme(props.mVocalMorpher.ulPhonemeA);
mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeACoarseTuning);
mAlProps.Vmorpher.PhonemeB = get_phoneme(props.mVocalMorpher.ulPhonemeB);
mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeBCoarseTuning);
mAlProps.Vmorpher.Waveform = get_waveform(props.mVocalMorpher.ulWaveform);
mAlProps.Vmorpher.Rate = props.mVocalMorpher.flRate;
return true;
}
template<>
void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
{
props.mType = EaxEffectType::VocalMorpher;
props.mVocalMorpher.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
props.mVocalMorpher.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
props.mVocalMorpher.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
props.mVocalMorpher.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
props.mVocalMorpher.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
props.mVocalMorpher.flRate = EAXVOCALMORPHER_DEFAULTRATE;
}
template<>
void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXVOCALMORPHER_NONE:
break;
case EAXVOCALMORPHER_ALLPARAMETERS:
call.set_value<Exception>(props.mVocalMorpher);
break;
case EAXVOCALMORPHER_PHONEMEA:
call.set_value<Exception>(props.mVocalMorpher.ulPhonemeA);
break;
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
call.set_value<Exception>(props.mVocalMorpher.lPhonemeACoarseTuning);
break;
case EAXVOCALMORPHER_PHONEMEB:
call.set_value<Exception>(props.mVocalMorpher.ulPhonemeB);
break;
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
call.set_value<Exception>(props.mVocalMorpher.lPhonemeBCoarseTuning);
break;
case EAXVOCALMORPHER_WAVEFORM:
call.set_value<Exception>(props.mVocalMorpher.ulWaveform);
break;
case EAXVOCALMORPHER_RATE:
call.set_value<Exception>(props.mVocalMorpher.flRate);
break;
default:
fail_unknown_property_id();
}
}
template<>
void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
switch(call.get_property_id())
{
case EAXVOCALMORPHER_NONE:
break;
case EAXVOCALMORPHER_ALLPARAMETERS:
defer<AllValidator>(call, props.mVocalMorpher);
break;
case EAXVOCALMORPHER_PHONEMEA:
defer<PhonemeAValidator>(call, props.mVocalMorpher.ulPhonemeA);
break;
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
defer<PhonemeACoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeACoarseTuning);
break;
case EAXVOCALMORPHER_PHONEMEB:
defer<PhonemeBValidator>(call, props.mVocalMorpher.ulPhonemeB);
break;
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
defer<PhonemeBCoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeBCoarseTuning);
break;
case EAXVOCALMORPHER_WAVEFORM:
defer<WaveformValidator>(call, props.mVocalMorpher.ulWaveform);
break;
case EAXVOCALMORPHER_RATE:
defer<RateValidator>(call, props.mVocalMorpher.flRate);
break;
default:
fail_unknown_property_id();
}
}
#endif // ALSOFT_EAX

106
externals/openal-soft/al/error.cpp vendored Normal file
View File

@@ -0,0 +1,106 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2000 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <atomic>
#include <csignal>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <mutex>
#include "AL/al.h"
#include "AL/alc.h"
#include "alc/context.h"
#include "almalloc.h"
#include "core/except.h"
#include "core/logging.h"
#include "opthelpers.h"
#include "vector.h"
bool TrapALError{false};
void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
{
auto message = al::vector<char>(256);
va_list args, args2;
va_start(args, msg);
va_copy(args2, args);
int msglen{std::vsnprintf(message.data(), message.size(), msg, args)};
if(msglen >= 0 && static_cast<size_t>(msglen) >= message.size())
{
message.resize(static_cast<size_t>(msglen) + 1u);
msglen = std::vsnprintf(message.data(), message.size(), msg, args2);
}
va_end(args2);
va_end(args);
if(msglen >= 0) msg = message.data();
else msg = "<internal error constructing message>";
WARN("Error generated on context %p, code 0x%04x, \"%s\"\n",
decltype(std::declval<void*>()){this}, errorCode, msg);
if(TrapALError)
{
#ifdef _WIN32
/* DebugBreak will cause an exception if there is no debugger */
if(IsDebuggerPresent())
DebugBreak();
#elif defined(SIGTRAP)
raise(SIGTRAP);
#endif
}
ALenum curerr{AL_NO_ERROR};
mLastError.compare_exchange_strong(curerr, errorCode);
}
AL_API ALenum AL_APIENTRY alGetError(void)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY
{
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;
}
return context->mLastError.exchange(AL_NO_ERROR);
}
END_API_FUNC

215
externals/openal-soft/al/event.cpp vendored Normal file
View File

@@ -0,0 +1,215 @@
#include "config.h"
#include "event.h"
#include <algorithm>
#include <atomic>
#include <cstring>
#include <exception>
#include <memory>
#include <mutex>
#include <new>
#include <string>
#include <thread>
#include <utility>
#include "AL/al.h"
#include "AL/alc.h"
#include "albyte.h"
#include "alc/context.h"
#include "alc/effects/base.h"
#include "alc/inprogext.h"
#include "almalloc.h"
#include "core/async_event.h"
#include "core/except.h"
#include "core/logging.h"
#include "core/voice_change.h"
#include "opthelpers.h"
#include "ringbuffer.h"
#include "threads.h"
static int EventThread(ALCcontext *context)
{
RingBuffer *ring{context->mAsyncEvents.get()};
bool quitnow{false};
while(!quitnow)
{
auto evt_data = ring->getReadVector().first;
if(evt_data.len == 0)
{
context->mEventSem.wait();
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;
AsyncEvent evt{*evt_ptr};
al::destroy_at(evt_ptr);
ring->readAdvance(1);
quitnow = evt.EnumType == AsyncEvent::KillThread;
if(quitnow) UNLIKELY break;
if(evt.EnumType == AsyncEvent::ReleaseEffectState)
{
al::intrusive_ptr<EffectState>{evt.u.mEffectState};
continue;
}
auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
if(!context->mEventCb || !enabledevts.test(evt.EnumType))
continue;
if(evt.EnumType == AsyncEvent::SourceStateChange)
{
ALuint state{};
std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
msg += " state has changed to ";
switch(evt.u.srcstate.state)
{
case AsyncEvent::SrcState::Reset:
msg += "AL_INITIAL";
state = AL_INITIAL;
break;
case AsyncEvent::SrcState::Stop:
msg += "AL_STOPPED";
state = AL_STOPPED;
break;
case AsyncEvent::SrcState::Play:
msg += "AL_PLAYING";
state = AL_PLAYING;
break;
case AsyncEvent::SrcState::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)
{
std::string msg{std::to_string(evt.u.bufcomp.count)};
if(evt.u.bufcomp.count == 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_DISCONNECTED_SOFT, 0, 0,
static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg,
context->mEventParam);
}
} while(evt_data.len != 0);
}
return 0;
}
void StartEventThrd(ALCcontext *ctx)
{
try {
ctx->mEventThread = std::thread{EventThread, ctx};
}
catch(std::exception& e) {
ERR("Failed to start event thread: %s\n", e.what());
}
catch(...) {
ERR("Failed to start event thread! Expect problems.\n");
}
}
void StopEventThrd(ALCcontext *ctx)
{
RingBuffer *ring{ctx->mAsyncEvents.get()};
auto evt_data = ring->getWriteVector().first;
if(evt_data.len == 0)
{
do {
std::this_thread::yield();
evt_data = ring->getWriteVector().first;
} while(evt_data.len == 0);
}
al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), AsyncEvent::KillThread);
ring->writeAdvance(1);
ctx->mEventSem.post();
if(ctx->mEventThread.joinable())
ctx->mEventThread.join();
}
AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count);
if(count <= 0) return;
if(!types) return context->setError(AL_INVALID_VALUE, "NULL pointer");
ContextBase::AsyncEventBitset flags{};
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.set(AsyncEvent::BufferCompleted);
else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
flags.set(AsyncEvent::SourceStateChange);
else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
flags.set(AsyncEvent::Disconnected);
else
return false;
return true;
}
);
if(bad_type != types_end)
return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", *bad_type);
if(enable)
{
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)
{
/* enabledevts is (re-)filled with the current value on failure, so
* just try again.
*/
}
}
else
{
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)
{
}
/* Wait to ensure the event handler sees the changed flags before
* returning.
*/
std::lock_guard<std::mutex> _{context->mEventCbLock};
}
}
END_API_FUNC
AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mEventCbLock};
context->mEventCb = callback;
context->mEventParam = userParam;
}
END_API_FUNC

9
externals/openal-soft/al/event.h vendored Normal file
View File

@@ -0,0 +1,9 @@
#ifndef AL_EVENT_H
#define AL_EVENT_H
struct ALCcontext;
void StartEventThrd(ALCcontext *ctx);
void StopEventThrd(ALCcontext *ctx);
#endif

82
externals/openal-soft/al/extension.cpp vendored Normal file
View File

@@ -0,0 +1,82 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <cctype>
#include <cstdlib>
#include <cstring>
#include "AL/al.h"
#include "AL/alc.h"
#include "alc/context.h"
#include "alstring.h"
#include "core/except.h"
#include "opthelpers.h"
AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return AL_FALSE;
if(!extName) UNLIKELY
{
context->setError(AL_INVALID_VALUE, "NULL pointer");
return AL_FALSE;
}
size_t len{strlen(extName)};
const char *ptr{context->mExtensionList};
while(ptr && *ptr)
{
if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len])))
return AL_TRUE;
if((ptr=strchr(ptr, ' ')) != nullptr)
{
do {
++ptr;
} while(isspace(*ptr));
}
}
return AL_FALSE;
}
END_API_FUNC
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
START_API_FUNC
{
if(!funcName) return nullptr;
return alcGetProcAddress(nullptr, funcName);
}
END_API_FUNC
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
START_API_FUNC
{
if(!enumName) return static_cast<ALenum>(0);
return alcGetEnumValue(nullptr, enumName);
}
END_API_FUNC

722
externals/openal-soft/al/filter.cpp vendored Normal file
View File

@@ -0,0 +1,722 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include "filter.h"
#include <algorithm>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <iterator>
#include <memory>
#include <mutex>
#include <new>
#include <numeric>
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/efx.h"
#include "albit.h"
#include "alc/context.h"
#include "alc/device.h"
#include "almalloc.h"
#include "alnumeric.h"
#include "core/except.h"
#include "opthelpers.h"
#include "vector.h"
namespace {
class filter_exception final : public al::base_exception {
ALenum mErrorCode;
public:
#ifdef __USE_MINGW_ANSI_STDIO
[[gnu::format(gnu_printf, 3, 4)]]
#else
[[gnu::format(printf, 3, 4)]]
#endif
filter_exception(ALenum code, const char *msg, ...);
~filter_exception() override;
ALenum errorCode() const noexcept { return mErrorCode; }
};
filter_exception::filter_exception(ALenum code, const char* msg, ...) : mErrorCode{code}
{
std::va_list args;
va_start(args, msg);
setMessage(msg, args);
va_end(args);
}
filter_exception::~filter_exception() = default;
#define DEFINE_ALFILTER_VTABLE(T) \
const ALfilter::Vtable T##_vtable = { \
T##_setParami, T##_setParamiv, T##_setParamf, T##_setParamfv, \
T##_getParami, T##_getParamiv, T##_getParamf, T##_getParamfv, \
}
void ALlowpass_setParami(ALfilter*, ALenum param, int)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
void ALlowpass_setParamiv(ALfilter*, ALenum param, const int*)
{
throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x",
param};
}
void ALlowpass_setParamf(ALfilter *filter, ALenum param, float val)
{
switch(param)
{
case AL_LOWPASS_GAIN:
if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
throw filter_exception{AL_INVALID_VALUE, "Low-pass gain %f out of range", val};
filter->Gain = val;
break;
case AL_LOWPASS_GAINHF:
if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
throw filter_exception{AL_INVALID_VALUE, "Low-pass gainhf %f out of range", val};
filter->GainHF = val;
break;
default:
throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
}
}
void ALlowpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
{ ALlowpass_setParamf(filter, param, vals[0]); }
void ALlowpass_getParami(const ALfilter*, ALenum param, int*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
void ALlowpass_getParamiv(const ALfilter*, ALenum param, int*)
{
throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x",
param};
}
void ALlowpass_getParamf(const ALfilter *filter, ALenum param, float *val)
{
switch(param)
{
case AL_LOWPASS_GAIN:
*val = filter->Gain;
break;
case AL_LOWPASS_GAINHF:
*val = filter->GainHF;
break;
default:
throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
}
}
void ALlowpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
{ ALlowpass_getParamf(filter, param, vals); }
DEFINE_ALFILTER_VTABLE(ALlowpass);
void ALhighpass_setParami(ALfilter*, ALenum param, int)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
void ALhighpass_setParamiv(ALfilter*, ALenum param, const int*)
{
throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x",
param};
}
void ALhighpass_setParamf(ALfilter *filter, ALenum param, float val)
{
switch(param)
{
case AL_HIGHPASS_GAIN:
if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
throw filter_exception{AL_INVALID_VALUE, "High-pass gain %f out of range", val};
filter->Gain = val;
break;
case AL_HIGHPASS_GAINLF:
if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
throw filter_exception{AL_INVALID_VALUE, "High-pass gainlf %f out of range", val};
filter->GainLF = val;
break;
default:
throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
}
}
void ALhighpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
{ ALhighpass_setParamf(filter, param, vals[0]); }
void ALhighpass_getParami(const ALfilter*, ALenum param, int*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
void ALhighpass_getParamiv(const ALfilter*, ALenum param, int*)
{
throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x",
param};
}
void ALhighpass_getParamf(const ALfilter *filter, ALenum param, float *val)
{
switch(param)
{
case AL_HIGHPASS_GAIN:
*val = filter->Gain;
break;
case AL_HIGHPASS_GAINLF:
*val = filter->GainLF;
break;
default:
throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
}
}
void ALhighpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
{ ALhighpass_getParamf(filter, param, vals); }
DEFINE_ALFILTER_VTABLE(ALhighpass);
void ALbandpass_setParami(ALfilter*, ALenum param, int)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
void ALbandpass_setParamiv(ALfilter*, ALenum param, const int*)
{
throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x",
param};
}
void ALbandpass_setParamf(ALfilter *filter, ALenum param, float val)
{
switch(param)
{
case AL_BANDPASS_GAIN:
if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
throw filter_exception{AL_INVALID_VALUE, "Band-pass gain %f out of range", val};
filter->Gain = val;
break;
case AL_BANDPASS_GAINHF:
if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
throw filter_exception{AL_INVALID_VALUE, "Band-pass gainhf %f out of range", val};
filter->GainHF = val;
break;
case AL_BANDPASS_GAINLF:
if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
throw filter_exception{AL_INVALID_VALUE, "Band-pass gainlf %f out of range", val};
filter->GainLF = val;
break;
default:
throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
}
}
void ALbandpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
{ ALbandpass_setParamf(filter, param, vals[0]); }
void ALbandpass_getParami(const ALfilter*, ALenum param, int*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
void ALbandpass_getParamiv(const ALfilter*, ALenum param, int*)
{
throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x",
param};
}
void ALbandpass_getParamf(const ALfilter *filter, ALenum param, float *val)
{
switch(param)
{
case AL_BANDPASS_GAIN:
*val = filter->Gain;
break;
case AL_BANDPASS_GAINHF:
*val = filter->GainHF;
break;
case AL_BANDPASS_GAINLF:
*val = filter->GainLF;
break;
default:
throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
}
}
void ALbandpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
{ ALbandpass_getParamf(filter, param, vals); }
DEFINE_ALFILTER_VTABLE(ALbandpass);
void ALnullfilter_setParami(ALfilter*, ALenum param, int)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
void ALnullfilter_setParamiv(ALfilter*, ALenum param, const int*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
void ALnullfilter_setParamf(ALfilter*, ALenum param, float)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
void ALnullfilter_setParamfv(ALfilter*, ALenum param, const float*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
void ALnullfilter_getParami(const ALfilter*, ALenum param, int*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
void ALnullfilter_getParamiv(const ALfilter*, ALenum param, int*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
void ALnullfilter_getParamf(const ALfilter*, ALenum param, float*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
void ALnullfilter_getParamfv(const ALfilter*, ALenum param, float*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
DEFINE_ALFILTER_VTABLE(ALnullfilter);
void InitFilterParams(ALfilter *filter, ALenum type)
{
if(type == AL_FILTER_LOWPASS)
{
filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = 1.0f;
filter->LFReference = HIGHPASSFREQREF;
filter->vtab = &ALlowpass_vtable;
}
else if(type == AL_FILTER_HIGHPASS)
{
filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
filter->GainHF = 1.0f;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
filter->LFReference = HIGHPASSFREQREF;
filter->vtab = &ALhighpass_vtable;
}
else if(type == AL_FILTER_BANDPASS)
{
filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
filter->LFReference = HIGHPASSFREQREF;
filter->vtab = &ALbandpass_vtable;
}
else
{
filter->Gain = 1.0f;
filter->GainHF = 1.0f;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = 1.0f;
filter->LFReference = HIGHPASSFREQREF;
filter->vtab = &ALnullfilter_vtable;
}
filter->type = type;
}
bool EnsureFilters(ALCdevice *device, size_t needed)
{
size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), size_t{0},
[](size_t cur, const FilterSubList &sublist) noexcept -> size_t
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
while(needed > count)
{
if(device->FilterList.size() >= 1<<25) UNLIKELY
return false;
device->FilterList.emplace_back();
auto sublist = device->FilterList.end() - 1;
sublist->FreeMask = ~0_u64;
sublist->Filters = static_cast<ALfilter*>(al_calloc(alignof(ALfilter), sizeof(ALfilter)*64));
if(!sublist->Filters) UNLIKELY
{
device->FilterList.pop_back();
return false;
}
count += 64;
}
return true;
}
ALfilter *AllocFilter(ALCdevice *device)
{
auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
[](const FilterSubList &entry) noexcept -> bool
{ return entry.FreeMask != 0; });
auto lidx = static_cast<ALuint>(std::distance(device->FilterList.begin(), sublist));
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
ASSUME(slidx < 64);
ALfilter *filter{al::construct_at(sublist->Filters + slidx)};
InitFilterParams(filter, AL_FILTER_NULL);
/* Add 1 to avoid filter ID 0. */
filter->id = ((lidx<<6) | slidx) + 1;
sublist->FreeMask &= ~(1_u64 << slidx);
return filter;
}
void FreeFilter(ALCdevice *device, ALfilter *filter)
{
const ALuint id{filter->id - 1};
const size_t lidx{id >> 6};
const ALuint slidx{id & 0x3f};
al::destroy_at(filter);
device->FilterList[lidx].FreeMask |= 1_u64 << slidx;
}
inline ALfilter *LookupFilter(ALCdevice *device, ALuint id)
{
const size_t lidx{(id-1) >> 6};
const ALuint slidx{(id-1) & 0x3f};
if(lidx >= device->FilterList.size()) UNLIKELY
return nullptr;
FilterSubList &sublist = device->FilterList[lidx];
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
return nullptr;
return sublist.Filters + slidx;
}
} // namespace
AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Generating %d filters", n);
if(n <= 0) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
if(!EnsureFilters(device, static_cast<ALuint>(n)))
{
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n, (n==1)?"":"s");
return;
}
if(n == 1) LIKELY
{
/* Special handling for the easy and normal case. */
ALfilter *filter{AllocFilter(device)};
if(filter) filters[0] = filter->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 {
ALfilter *filter{AllocFilter(device)};
ids.emplace_back(filter->id);
} while(--n);
std::copy(ids.begin(), ids.end(), filters);
}
}
END_API_FUNC
AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Deleting %d filters", n);
if(n <= 0) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
/* First try to find any filters that are invalid. */
auto validate_filter = [device](const ALuint fid) -> bool
{ return !fid || LookupFilter(device, fid) != nullptr; };
const ALuint *filters_end = filters + n;
auto invflt = std::find_if_not(filters, filters_end, validate_filter);
if(invflt != filters_end) UNLIKELY
{
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", *invflt);
return;
}
/* All good. Delete non-0 filter IDs. */
auto delete_filter = [device](const ALuint fid) -> void
{
ALfilter *filter{fid ? LookupFilter(device, fid) : nullptr};
if(filter) FreeFilter(device, filter);
};
std::for_each(filters, filters_end, delete_filter);
}
END_API_FUNC
AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(context) LIKELY
{
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
if(!filter || LookupFilter(device, filter))
return AL_TRUE;
}
return AL_FALSE;
}
END_API_FUNC
AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
ALfilter *alfilt{LookupFilter(device, filter)};
if(!alfilt) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
else
{
if(param == AL_FILTER_TYPE)
{
if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
|| value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
InitFilterParams(alfilt, value);
else
context->setError(AL_INVALID_VALUE, "Invalid filter type 0x%04x", value);
}
else try
{
/* Call the appropriate handler */
alfilt->setParami(param, value);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
}
END_API_FUNC
AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values)
START_API_FUNC
{
switch(param)
{
case AL_FILTER_TYPE:
alFilteri(filter, param, values[0]);
return;
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
ALfilter *alfilt{LookupFilter(device, filter)};
if(!alfilt) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
else try
{
/* Call the appropriate handler */
alfilt->setParamiv(param, values);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
END_API_FUNC
AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
ALfilter *alfilt{LookupFilter(device, filter)};
if(!alfilt) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
else try
{
/* Call the appropriate handler */
alfilt->setParamf(param, value);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
END_API_FUNC
AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
ALfilter *alfilt{LookupFilter(device, filter)};
if(!alfilt) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
else try
{
/* Call the appropriate handler */
alfilt->setParamfv(param, values);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
const ALfilter *alfilt{LookupFilter(device, filter)};
if(!alfilt) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
else
{
if(param == AL_FILTER_TYPE)
*value = alfilt->type;
else try
{
/* Call the appropriate handler */
alfilt->getParami(param, value);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values)
START_API_FUNC
{
switch(param)
{
case AL_FILTER_TYPE:
alGetFilteri(filter, param, values);
return;
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
const ALfilter *alfilt{LookupFilter(device, filter)};
if(!alfilt) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
else try
{
/* Call the appropriate handler */
alfilt->getParamiv(param, values);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
const ALfilter *alfilt{LookupFilter(device, filter)};
if(!alfilt) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
else try
{
/* Call the appropriate handler */
alfilt->getParamf(param, value);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
const ALfilter *alfilt{LookupFilter(device, filter)};
if(!alfilt) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
else try
{
/* Call the appropriate handler */
alfilt->getParamfv(param, values);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
END_API_FUNC
FilterSubList::~FilterSubList()
{
uint64_t usemask{~FreeMask};
while(usemask)
{
const int idx{al::countr_zero(usemask)};
al::destroy_at(Filters+idx);
usemask &= ~(1_u64 << idx);
}
FreeMask = ~usemask;
al_free(Filters);
Filters = nullptr;
}

52
externals/openal-soft/al/filter.h vendored Normal file
View File

@@ -0,0 +1,52 @@
#ifndef AL_FILTER_H
#define AL_FILTER_H
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include "almalloc.h"
#define LOWPASSFREQREF 5000.0f
#define HIGHPASSFREQREF 250.0f
struct ALfilter {
ALenum type{AL_FILTER_NULL};
float Gain{1.0f};
float GainHF{1.0f};
float HFReference{LOWPASSFREQREF};
float GainLF{1.0f};
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};
/* 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); }
DISABLE_ALLOC()
};
#endif

444
externals/openal-soft/al/listener.cpp vendored Normal file
View File

@@ -0,0 +1,444 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2000 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include "listener.h"
#include <cmath>
#include <mutex>
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/efx.h"
#include "alc/context.h"
#include "almalloc.h"
#include "atomic.h"
#include "core/except.h"
#include "opthelpers.h"
namespace {
inline void UpdateProps(ALCcontext *context)
{
if(!context->mDeferUpdates)
{
UpdateContextProps(context);
return;
}
context->mPropsDirty = true;
}
inline void CommitAndUpdateProps(ALCcontext *context)
{
if(!context->mDeferUpdates)
{
#ifdef ALSOFT_EAX
if(context->eaxNeedsCommit())
{
context->mPropsDirty = true;
context->applyAllUpdates();
return;
}
#endif
UpdateContextProps(context);
return;
}
context->mPropsDirty = true;
}
} // namespace
AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
switch(param)
{
case AL_GAIN:
if(!(value >= 0.0f && std::isfinite(value)))
return context->setError(AL_INVALID_VALUE, "Listener gain out of range");
listener.Gain = value;
UpdateProps(context.get());
break;
case AL_METERS_PER_UNIT:
if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT))
return context->setError(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");
}
}
END_API_FUNC
AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
switch(param)
{
case AL_POSITION:
if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
return context->setError(AL_INVALID_VALUE, "Listener position out of range");
listener.Position[0] = value1;
listener.Position[1] = value2;
listener.Position[2] = value3;
CommitAndUpdateProps(context.get());
break;
case AL_VELOCITY:
if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
return context->setError(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");
}
}
END_API_FUNC
AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
START_API_FUNC
{
if(values)
{
switch(param)
{
case AL_GAIN:
case AL_METERS_PER_UNIT:
alListenerf(param, values[0]);
return;
case AL_POSITION:
case AL_VELOCITY:
alListener3f(param, values[0], values[1], values[2]);
return;
}
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(!values) UNLIKELY
return context->setError(AL_INVALID_VALUE, "NULL pointer");
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{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])))
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");
}
}
END_API_FUNC
AL_API void AL_APIENTRY alListeneri(ALenum param, ALint /*value*/)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
std::lock_guard<std::mutex> _{context->mPropLock};
switch(param)
{
default:
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
}
}
END_API_FUNC
AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
START_API_FUNC
{
switch(param)
{
case AL_POSITION:
case AL_VELOCITY:
alListener3f(param, static_cast<ALfloat>(value1), static_cast<ALfloat>(value2),
static_cast<ALfloat>(value3));
return;
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
std::lock_guard<std::mutex> _{context->mPropLock};
switch(param)
{
default:
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
}
}
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(!context) UNLIKELY return;
std::lock_guard<std::mutex> _{context->mPropLock};
if(!values) UNLIKELY
context->setError(AL_INVALID_VALUE, "NULL pointer");
else switch(param)
{
default:
context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
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(!context) UNLIKELY return;
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)
{
case AL_POSITION:
*value1 = listener.Position[0];
*value2 = listener.Position[1];
*value3 = listener.Position[2];
break;
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");
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
START_API_FUNC
{
switch(param)
{
case AL_GAIN:
case AL_METERS_PER_UNIT:
alGetListenerf(param, values);
return;
case AL_POSITION:
case AL_VELOCITY:
alGetListener3f(param, values+0, values+1, values+2);
return;
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
if(!values)
context->setError(AL_INVALID_VALUE, "NULL pointer");
else switch(param)
{
case AL_ORIENTATION:
// 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");
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY 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");
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
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)
{
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;
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");
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
START_API_FUNC
{
switch(param)
{
case AL_POSITION:
case AL_VELOCITY:
alGetListener3i(param, values+0, values+1, values+2);
return;
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
if(!values)
context->setError(AL_INVALID_VALUE, "NULL pointer");
else switch(param)
{
case AL_ORIENTATION:
// 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");
}
}
END_API_FUNC

24
externals/openal-soft/al/listener.h vendored Normal file
View File

@@ -0,0 +1,24 @@
#ifndef AL_LISTENER_H
#define AL_LISTENER_H
#include <array>
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/efx.h"
#include "almalloc.h"
struct ALlistener {
std::array<float,3> Position{{0.0f, 0.0f, 0.0f}};
std::array<float,3> Velocity{{0.0f, 0.0f, 0.0f}};
std::array<float,3> OrientAt{{0.0f, 0.0f, -1.0f}};
std::array<float,3> OrientUp{{0.0f, 1.0f, 0.0f}};
float Gain{1.0f};
float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT};
DISABLE_ALLOC()
};
#endif

5345
externals/openal-soft/al/source.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

1044
externals/openal-soft/al/source.h vendored Normal file

File diff suppressed because it is too large Load Diff

963
externals/openal-soft/al/state.cpp vendored Normal file
View File

@@ -0,0 +1,963 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2000 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include "version.h"
#include <atomic>
#include <cmath>
#include <mutex>
#include <stdexcept>
#include <string>
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include "alc/alu.h"
#include "alc/context.h"
#include "alc/inprogext.h"
#include "alnumeric.h"
#include "aloptional.h"
#include "atomic.h"
#include "core/context.h"
#include "core/except.h"
#include "core/mixer/defs.h"
#include "core/voice.h"
#include "intrusive_ptr.h"
#include "opthelpers.h"
#include "strutils.h"
#ifdef ALSOFT_EAX
#include "alc/device.h"
#include "eax/globals.h"
#include "eax/x_ram.h"
#endif // ALSOFT_EAX
namespace {
constexpr ALchar alVendor[] = "OpenAL Community";
constexpr ALchar alVersion[] = "1.1 ALSOFT " ALSOFT_VERSION;
constexpr ALchar alRenderer[] = "OpenAL Soft";
// Error Messages
constexpr ALchar alNoError[] = "No Error";
constexpr ALchar alErrInvalidName[] = "Invalid Name";
constexpr ALchar alErrInvalidEnum[] = "Invalid Enum";
constexpr ALchar alErrInvalidValue[] = "Invalid Value";
constexpr ALchar alErrInvalidOp[] = "Invalid Operation";
constexpr ALchar alErrOutOfMemory[] = "Out of Memory";
/* Resampler strings */
template<Resampler rtype> struct ResamplerName { };
template<> struct ResamplerName<Resampler::Point>
{ static constexpr const ALchar *Get() noexcept { return "Nearest"; } };
template<> struct ResamplerName<Resampler::Linear>
{ static constexpr const ALchar *Get() noexcept { return "Linear"; } };
template<> struct ResamplerName<Resampler::Cubic>
{ static constexpr const ALchar *Get() noexcept { return "Cubic"; } };
template<> struct ResamplerName<Resampler::FastBSinc12>
{ static constexpr const ALchar *Get() noexcept { return "11th order Sinc (fast)"; } };
template<> struct ResamplerName<Resampler::BSinc12>
{ static constexpr const ALchar *Get() noexcept { return "11th order Sinc"; } };
template<> struct ResamplerName<Resampler::FastBSinc24>
{ static constexpr const ALchar *Get() noexcept { return "23rd order Sinc (fast)"; } };
template<> struct ResamplerName<Resampler::BSinc24>
{ static constexpr const ALchar *Get() noexcept { return "23rd order Sinc"; } };
const ALchar *GetResamplerName(const Resampler rtype)
{
#define HANDLE_RESAMPLER(r) case r: return ResamplerName<r>::Get()
switch(rtype)
{
HANDLE_RESAMPLER(Resampler::Point);
HANDLE_RESAMPLER(Resampler::Linear);
HANDLE_RESAMPLER(Resampler::Cubic);
HANDLE_RESAMPLER(Resampler::FastBSinc12);
HANDLE_RESAMPLER(Resampler::BSinc12);
HANDLE_RESAMPLER(Resampler::FastBSinc24);
HANDLE_RESAMPLER(Resampler::BSinc24);
}
#undef HANDLE_RESAMPLER
/* Should never get here. */
throw std::runtime_error{"Unexpected resampler index"};
}
al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
{
switch(model)
{
case AL_NONE: return DistanceModel::Disable;
case AL_INVERSE_DISTANCE: return DistanceModel::Inverse;
case AL_INVERSE_DISTANCE_CLAMPED: return DistanceModel::InverseClamped;
case AL_LINEAR_DISTANCE: return DistanceModel::Linear;
case AL_LINEAR_DISTANCE_CLAMPED: return DistanceModel::LinearClamped;
case AL_EXPONENT_DISTANCE: return DistanceModel::Exponent;
case AL_EXPONENT_DISTANCE_CLAMPED: return DistanceModel::ExponentClamped;
}
return al::nullopt;
}
ALenum ALenumFromDistanceModel(DistanceModel model)
{
switch(model)
{
case DistanceModel::Disable: return AL_NONE;
case DistanceModel::Inverse: return AL_INVERSE_DISTANCE;
case DistanceModel::InverseClamped: return AL_INVERSE_DISTANCE_CLAMPED;
case DistanceModel::Linear: return AL_LINEAR_DISTANCE;
case DistanceModel::LinearClamped: return AL_LINEAR_DISTANCE_CLAMPED;
case DistanceModel::Exponent: return AL_EXPONENT_DISTANCE;
case DistanceModel::ExponentClamped: return AL_EXPONENT_DISTANCE_CLAMPED;
}
throw std::runtime_error{"Unexpected distance model "+std::to_string(static_cast<int>(model))};
}
} // namespace
/* WARNING: Non-standard export! Not part of any extension, or exposed in the
* alcFunctions list.
*/
AL_API const ALchar* AL_APIENTRY alsoft_get_version(void)
START_API_FUNC
{
static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION");
if(spoof) return spoof->c_str();
return ALSOFT_VERSION;
}
END_API_FUNC
#define DO_UPDATEPROPS() do { \
if(!context->mDeferUpdates) \
UpdateContextProps(context.get()); \
else \
context->mPropsDirty = true; \
} while(0)
AL_API void AL_APIENTRY alEnable(ALenum capability)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
switch(capability)
{
case AL_SOURCE_DISTANCE_MODEL:
{
std::lock_guard<std::mutex> _{context->mPropLock};
context->mSourceDistanceModel = true;
DO_UPDATEPROPS();
}
break;
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
break;
default:
context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability);
}
}
END_API_FUNC
AL_API void AL_APIENTRY alDisable(ALenum capability)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
switch(capability)
{
case AL_SOURCE_DISTANCE_MODEL:
{
std::lock_guard<std::mutex> _{context->mPropLock};
context->mSourceDistanceModel = false;
DO_UPDATEPROPS();
}
break;
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
context->mStopVoicesOnDisconnect = false;
break;
default:
context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability);
}
}
END_API_FUNC
AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return AL_FALSE;
std::lock_guard<std::mutex> _{context->mPropLock};
ALboolean value{AL_FALSE};
switch(capability)
{
case AL_SOURCE_DISTANCE_MODEL:
value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
break;
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE;
break;
default:
context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
}
return value;
}
END_API_FUNC
AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return AL_FALSE;
std::lock_guard<std::mutex> _{context->mPropLock};
ALboolean value{AL_FALSE};
switch(pname)
{
case AL_DOPPLER_FACTOR:
if(context->mDopplerFactor != 0.0f)
value = AL_TRUE;
break;
case AL_DOPPLER_VELOCITY:
if(context->mDopplerVelocity != 0.0f)
value = AL_TRUE;
break;
case AL_DISTANCE_MODEL:
if(context->mDistanceModel == DistanceModel::Default)
value = AL_TRUE;
break;
case AL_SPEED_OF_SOUND:
if(context->mSpeedOfSound != 0.0f)
value = AL_TRUE;
break;
case AL_DEFERRED_UPDATES_SOFT:
if(context->mDeferUpdates)
value = AL_TRUE;
break;
case AL_GAIN_LIMIT_SOFT:
if(GainMixMax/context->mGainBoost != 0.0f)
value = AL_TRUE;
break;
case AL_NUM_RESAMPLERS_SOFT:
/* Always non-0. */
value = AL_TRUE;
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = static_cast<int>(ResamplerDefault) ? AL_TRUE : AL_FALSE;
break;
default:
context->setError(AL_INVALID_VALUE, "Invalid boolean property 0x%04x", pname);
}
return value;
}
END_API_FUNC
AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return 0.0;
std::lock_guard<std::mutex> _{context->mPropLock};
ALdouble value{0.0};
switch(pname)
{
case AL_DOPPLER_FACTOR:
value = context->mDopplerFactor;
break;
case AL_DOPPLER_VELOCITY:
value = context->mDopplerVelocity;
break;
case AL_DISTANCE_MODEL:
value = static_cast<ALdouble>(ALenumFromDistanceModel(context->mDistanceModel));
break;
case AL_SPEED_OF_SOUND:
value = context->mSpeedOfSound;
break;
case AL_DEFERRED_UPDATES_SOFT:
if(context->mDeferUpdates)
value = static_cast<ALdouble>(AL_TRUE);
break;
case AL_GAIN_LIMIT_SOFT:
value = ALdouble{GainMixMax}/context->mGainBoost;
break;
case AL_NUM_RESAMPLERS_SOFT:
value = static_cast<ALdouble>(Resampler::Max) + 1.0;
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = static_cast<ALdouble>(ResamplerDefault);
break;
default:
context->setError(AL_INVALID_VALUE, "Invalid double property 0x%04x", pname);
}
return value;
}
END_API_FUNC
AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return 0.0f;
std::lock_guard<std::mutex> _{context->mPropLock};
ALfloat value{0.0f};
switch(pname)
{
case AL_DOPPLER_FACTOR:
value = context->mDopplerFactor;
break;
case AL_DOPPLER_VELOCITY:
value = context->mDopplerVelocity;
break;
case AL_DISTANCE_MODEL:
value = static_cast<ALfloat>(ALenumFromDistanceModel(context->mDistanceModel));
break;
case AL_SPEED_OF_SOUND:
value = context->mSpeedOfSound;
break;
case AL_DEFERRED_UPDATES_SOFT:
if(context->mDeferUpdates)
value = static_cast<ALfloat>(AL_TRUE);
break;
case AL_GAIN_LIMIT_SOFT:
value = GainMixMax/context->mGainBoost;
break;
case AL_NUM_RESAMPLERS_SOFT:
value = static_cast<ALfloat>(Resampler::Max) + 1.0f;
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = static_cast<ALfloat>(ResamplerDefault);
break;
default:
context->setError(AL_INVALID_VALUE, "Invalid float property 0x%04x", pname);
}
return value;
}
END_API_FUNC
AL_API ALint AL_APIENTRY alGetInteger(ALenum pname)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return 0;
std::lock_guard<std::mutex> _{context->mPropLock};
ALint value{0};
switch(pname)
{
case AL_DOPPLER_FACTOR:
value = static_cast<ALint>(context->mDopplerFactor);
break;
case AL_DOPPLER_VELOCITY:
value = static_cast<ALint>(context->mDopplerVelocity);
break;
case AL_DISTANCE_MODEL:
value = ALenumFromDistanceModel(context->mDistanceModel);
break;
case AL_SPEED_OF_SOUND:
value = static_cast<ALint>(context->mSpeedOfSound);
break;
case AL_DEFERRED_UPDATES_SOFT:
if(context->mDeferUpdates)
value = AL_TRUE;
break;
case AL_GAIN_LIMIT_SOFT:
value = static_cast<ALint>(GainMixMax/context->mGainBoost);
break;
case AL_NUM_RESAMPLERS_SOFT:
value = static_cast<int>(Resampler::Max) + 1;
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = static_cast<int>(ResamplerDefault);
break;
#ifdef ALSOFT_EAX
#define EAX_ERROR "[alGetInteger] EAX not enabled."
case AL_EAX_RAM_SIZE:
if (eax_g_is_enabled)
{
value = eax_x_ram_max_size;
}
else
{
context->setError(AL_INVALID_VALUE, EAX_ERROR);
}
break;
case AL_EAX_RAM_FREE:
if (eax_g_is_enabled)
{
auto device = context->mALDevice.get();
std::lock_guard<std::mutex> device_lock{device->BufferLock};
value = static_cast<ALint>(device->eax_x_ram_free_size);
}
else
{
context->setError(AL_INVALID_VALUE, EAX_ERROR);
}
break;
#undef EAX_ERROR
#endif // ALSOFT_EAX
default:
context->setError(AL_INVALID_VALUE, "Invalid integer property 0x%04x", pname);
}
return value;
}
END_API_FUNC
AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return 0_i64;
std::lock_guard<std::mutex> _{context->mPropLock};
ALint64SOFT value{0};
switch(pname)
{
case AL_DOPPLER_FACTOR:
value = static_cast<ALint64SOFT>(context->mDopplerFactor);
break;
case AL_DOPPLER_VELOCITY:
value = static_cast<ALint64SOFT>(context->mDopplerVelocity);
break;
case AL_DISTANCE_MODEL:
value = ALenumFromDistanceModel(context->mDistanceModel);
break;
case AL_SPEED_OF_SOUND:
value = static_cast<ALint64SOFT>(context->mSpeedOfSound);
break;
case AL_DEFERRED_UPDATES_SOFT:
if(context->mDeferUpdates)
value = AL_TRUE;
break;
case AL_GAIN_LIMIT_SOFT:
value = static_cast<ALint64SOFT>(GainMixMax/context->mGainBoost);
break;
case AL_NUM_RESAMPLERS_SOFT:
value = static_cast<ALint64SOFT>(Resampler::Max) + 1;
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = static_cast<ALint64SOFT>(ResamplerDefault);
break;
default:
context->setError(AL_INVALID_VALUE, "Invalid integer64 property 0x%04x", pname);
}
return value;
}
END_API_FUNC
AL_API ALvoid* AL_APIENTRY alGetPointerSOFT(ALenum pname)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return nullptr;
std::lock_guard<std::mutex> _{context->mPropLock};
void *value{nullptr};
switch(pname)
{
case AL_EVENT_CALLBACK_FUNCTION_SOFT:
value = reinterpret_cast<void*>(context->mEventCb);
break;
case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
value = context->mEventParam;
break;
default:
context->setError(AL_INVALID_VALUE, "Invalid pointer property 0x%04x", pname);
}
return value;
}
END_API_FUNC
AL_API void AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values)
START_API_FUNC
{
if(values)
{
switch(pname)
{
case AL_DOPPLER_FACTOR:
case AL_DOPPLER_VELOCITY:
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetBoolean(pname);
return;
}
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(!values)
context->setError(AL_INVALID_VALUE, "NULL pointer");
else switch(pname)
{
default:
context->setError(AL_INVALID_VALUE, "Invalid boolean-vector property 0x%04x", pname);
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values)
START_API_FUNC
{
if(values)
{
switch(pname)
{
case AL_DOPPLER_FACTOR:
case AL_DOPPLER_VELOCITY:
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetDouble(pname);
return;
}
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(!values)
context->setError(AL_INVALID_VALUE, "NULL pointer");
else switch(pname)
{
default:
context->setError(AL_INVALID_VALUE, "Invalid double-vector property 0x%04x", pname);
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values)
START_API_FUNC
{
if(values)
{
switch(pname)
{
case AL_DOPPLER_FACTOR:
case AL_DOPPLER_VELOCITY:
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetFloat(pname);
return;
}
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(!values)
context->setError(AL_INVALID_VALUE, "NULL pointer");
else switch(pname)
{
default:
context->setError(AL_INVALID_VALUE, "Invalid float-vector property 0x%04x", pname);
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
START_API_FUNC
{
if(values)
{
switch(pname)
{
case AL_DOPPLER_FACTOR:
case AL_DOPPLER_VELOCITY:
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetInteger(pname);
return;
}
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(!values)
context->setError(AL_INVALID_VALUE, "NULL pointer");
else switch(pname)
{
default:
context->setError(AL_INVALID_VALUE, "Invalid integer-vector property 0x%04x", pname);
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
START_API_FUNC
{
if(values)
{
switch(pname)
{
case AL_DOPPLER_FACTOR:
case AL_DOPPLER_VELOCITY:
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetInteger64SOFT(pname);
return;
}
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(!values)
context->setError(AL_INVALID_VALUE, "NULL pointer");
else switch(pname)
{
default:
context->setError(AL_INVALID_VALUE, "Invalid integer64-vector property 0x%04x", pname);
}
}
END_API_FUNC
AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, ALvoid **values)
START_API_FUNC
{
if(values)
{
switch(pname)
{
case AL_EVENT_CALLBACK_FUNCTION_SOFT:
case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
values[0] = alGetPointerSOFT(pname);
return;
}
}
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(!values)
context->setError(AL_INVALID_VALUE, "NULL pointer");
else switch(pname)
{
default:
context->setError(AL_INVALID_VALUE, "Invalid pointer-vector property 0x%04x", pname);
}
}
END_API_FUNC
AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return nullptr;
const ALchar *value{nullptr};
switch(pname)
{
case AL_VENDOR:
value = alVendor;
break;
case AL_VERSION:
value = alVersion;
break;
case AL_RENDERER:
value = alRenderer;
break;
case AL_EXTENSIONS:
value = context->mExtensionList;
break;
case AL_NO_ERROR:
value = alNoError;
break;
case AL_INVALID_NAME:
value = alErrInvalidName;
break;
case AL_INVALID_ENUM:
value = alErrInvalidEnum;
break;
case AL_INVALID_VALUE:
value = alErrInvalidValue;
break;
case AL_INVALID_OPERATION:
value = alErrInvalidOp;
break;
case AL_OUT_OF_MEMORY:
value = alErrOutOfMemory;
break;
default:
context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname);
}
return value;
}
END_API_FUNC
AL_API void AL_APIENTRY alDopplerFactor(ALfloat value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(!(value >= 0.0f && std::isfinite(value)))
context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value);
else
{
std::lock_guard<std::mutex> _{context->mPropLock};
context->mDopplerFactor = value;
DO_UPDATEPROPS();
}
}
END_API_FUNC
AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(!(value >= 0.0f && std::isfinite(value)))
context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value);
else
{
std::lock_guard<std::mutex> _{context->mPropLock};
context->mDopplerVelocity = value;
DO_UPDATEPROPS();
}
}
END_API_FUNC
AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(!(value > 0.0f && std::isfinite(value)))
context->setError(AL_INVALID_VALUE, "Speed of sound %f out of range", value);
else
{
std::lock_guard<std::mutex> _{context->mPropLock};
context->mSpeedOfSound = value;
DO_UPDATEPROPS();
}
}
END_API_FUNC
AL_API void AL_APIENTRY alDistanceModel(ALenum value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
if(auto model = DistanceModelFromALenum(value))
{
std::lock_guard<std::mutex> _{context->mPropLock};
context->mDistanceModel = *model;
if(!context->mSourceDistanceModel)
DO_UPDATEPROPS();
}
else
context->setError(AL_INVALID_VALUE, "Distance model 0x%04x out of range", value);
}
END_API_FUNC
AL_API void AL_APIENTRY alDeferUpdatesSOFT(void)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
std::lock_guard<std::mutex> _{context->mPropLock};
context->deferUpdates();
}
END_API_FUNC
AL_API void AL_APIENTRY alProcessUpdatesSOFT(void)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
std::lock_guard<std::mutex> _{context->mPropLock};
context->processUpdates();
}
END_API_FUNC
AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return nullptr;
const ALchar *value{nullptr};
switch(pname)
{
case AL_RESAMPLER_NAME_SOFT:
if(index < 0 || index > static_cast<ALint>(Resampler::Max))
context->setError(AL_INVALID_VALUE, "Resampler name index %d out of range", index);
else
value = GetResamplerName(static_cast<Resampler>(index));
break;
default:
context->setError(AL_INVALID_VALUE, "Invalid string indexed property");
}
return value;
}
END_API_FUNC
void UpdateContextProps(ALCcontext *context)
{
/* Get an unused proprty container, or allocate a new one as needed. */
ContextProps *props{context->mFreeContextProps.load(std::memory_order_acquire)};
if(!props)
props = new ContextProps{};
else
{
ContextProps *next;
do {
next = props->next.load(std::memory_order_relaxed);
} while(context->mFreeContextProps.compare_exchange_weak(props, next,
std::memory_order_seq_cst, std::memory_order_acquire) == 0);
}
/* Copy in current property values. */
ALlistener &listener = context->mListener;
props->Position = listener.Position;
props->Velocity = listener.Velocity;
props->OrientAt = listener.OrientAt;
props->OrientUp = listener.OrientUp;
props->Gain = listener.Gain;
props->MetersPerUnit = listener.mMetersPerUnit;
props->AirAbsorptionGainHF = context->mAirAbsorptionGainHF;
props->DopplerFactor = context->mDopplerFactor;
props->DopplerVelocity = context->mDopplerVelocity;
props->SpeedOfSound = context->mSpeedOfSound;
props->SourceDistanceModel = context->mSourceDistanceModel;
props->mDistanceModel = context->mDistanceModel;
/* Set the new container for updating internal parameters. */
props = context->mParams.ContextUpdate.exchange(props, std::memory_order_acq_rel);
if(props)
{
/* If there was an unused update container, put it back in the
* freelist.
*/
AtomicReplaceHead(context->mFreeContextProps, props);
}
}