#ifndef AL_SOURCE_H #define AL_SOURCE_H #include #include #include #include #include #include #include "AL/al.h" #include "AL/alc.h" #include "alc/alu.h" #include "alc/context.h" #include "alc/inprogext.h" #include "aldeque.h" #include "almalloc.h" #include "alnumeric.h" #include "atomic.h" #include "core/voice.h" #include "vector.h" #ifdef ALSOFT_EAX #include "eax_eax_call.h" #include "eax_fx_slot_index.h" #include "eax_utils.h" #endif // ALSOFT_EAX struct ALbuffer; struct ALeffectslot; enum class SourceStereo : bool { Normal = AL_NORMAL_SOFT, Enhanced = AL_SUPER_STEREO_SOFT }; #define DEFAULT_SENDS 2 #define INVALID_VOICE_IDX static_cast(-1) struct ALbufferQueueItem : public VoiceBufferItem { ALbuffer *mBuffer{nullptr}; DISABLE_ALLOC() }; #ifdef ALSOFT_EAX using EaxSourceSourceFilterDirtyFlagsValue = std::uint_least16_t; struct EaxSourceSourceFilterDirtyFlags { using EaxIsBitFieldStruct = bool; EaxSourceSourceFilterDirtyFlagsValue lDirect : 1; EaxSourceSourceFilterDirtyFlagsValue lDirectHF : 1; EaxSourceSourceFilterDirtyFlagsValue lRoom : 1; EaxSourceSourceFilterDirtyFlagsValue lRoomHF : 1; EaxSourceSourceFilterDirtyFlagsValue lObstruction : 1; EaxSourceSourceFilterDirtyFlagsValue flObstructionLFRatio : 1; EaxSourceSourceFilterDirtyFlagsValue lOcclusion : 1; EaxSourceSourceFilterDirtyFlagsValue flOcclusionLFRatio : 1; EaxSourceSourceFilterDirtyFlagsValue flOcclusionRoomRatio : 1; EaxSourceSourceFilterDirtyFlagsValue flOcclusionDirectRatio : 1; EaxSourceSourceFilterDirtyFlagsValue lExclusion : 1; EaxSourceSourceFilterDirtyFlagsValue flExclusionLFRatio : 1; }; // EaxSourceSourceFilterDirtyFlags using EaxSourceSourceMiscDirtyFlagsValue = std::uint_least8_t; struct EaxSourceSourceMiscDirtyFlags { using EaxIsBitFieldStruct = bool; EaxSourceSourceMiscDirtyFlagsValue lOutsideVolumeHF : 1; EaxSourceSourceMiscDirtyFlagsValue flDopplerFactor : 1; EaxSourceSourceMiscDirtyFlagsValue flRolloffFactor : 1; EaxSourceSourceMiscDirtyFlagsValue flRoomRolloffFactor : 1; EaxSourceSourceMiscDirtyFlagsValue flAirAbsorptionFactor : 1; EaxSourceSourceMiscDirtyFlagsValue ulFlags : 1; EaxSourceSourceMiscDirtyFlagsValue flMacroFXFactor : 1; EaxSourceSourceMiscDirtyFlagsValue speaker_levels : 1; }; // EaxSourceSourceMiscDirtyFlags using EaxSourceSendDirtyFlagsValue = std::uint_least8_t; struct EaxSourceSendDirtyFlags { using EaxIsBitFieldStruct = bool; EaxSourceSendDirtyFlagsValue lSend : 1; EaxSourceSendDirtyFlagsValue lSendHF : 1; EaxSourceSendDirtyFlagsValue lOcclusion : 1; EaxSourceSendDirtyFlagsValue flOcclusionLFRatio : 1; EaxSourceSendDirtyFlagsValue flOcclusionRoomRatio : 1; EaxSourceSendDirtyFlagsValue flOcclusionDirectRatio : 1; EaxSourceSendDirtyFlagsValue lExclusion : 1; EaxSourceSendDirtyFlagsValue flExclusionLFRatio : 1; }; // EaxSourceSendDirtyFlags struct EaxSourceSendsDirtyFlags { using EaxIsBitFieldStruct = bool; EaxSourceSendDirtyFlags sends[EAX_MAX_FXSLOTS]; }; // EaxSourceSendsDirtyFlags #endif // ALSOFT_EAX struct ALsource { /** Source properties. */ float Pitch{1.0f}; float Gain{1.0f}; float OuterGain{0.0f}; float MinGain{0.0f}; float MaxGain{1.0f}; float InnerAngle{360.0f}; float OuterAngle{360.0f}; float RefDistance{1.0f}; float MaxDistance{std::numeric_limits::max()}; float RolloffFactor{1.0f}; std::array Position{{0.0f, 0.0f, 0.0f}}; std::array Velocity{{0.0f, 0.0f, 0.0f}}; std::array Direction{{0.0f, 0.0f, 0.0f}}; std::array OrientAt{{0.0f, 0.0f, -1.0f}}; std::array OrientUp{{0.0f, 1.0f, 0.0f}}; bool HeadRelative{false}; bool Looping{false}; DistanceModel mDistanceModel{DistanceModel::Default}; Resampler mResampler{ResamplerDefault}; DirectMode DirectChannels{DirectMode::Off}; SpatializeMode mSpatialize{SpatializeMode::Auto}; SourceStereo mStereoMode{SourceStereo::Normal}; bool DryGainHFAuto{true}; bool WetGainAuto{true}; bool WetGainHFAuto{true}; float OuterGainHF{1.0f}; float AirAbsorptionFactor{0.0f}; float RoomRolloffFactor{0.0f}; float DopplerFactor{1.0f}; /* NOTE: Stereo pan angles are specified in radians, counter-clockwise * rather than clockwise. */ std::array StereoPan{{al::numbers::pi_v/6.0f, -al::numbers::pi_v/6.0f}}; float Radius{0.0f}; float EnhWidth{0.593f}; /** Direct filter and auxiliary send info. */ struct { float Gain; float GainHF; float HFReference; float GainLF; float LFReference; } Direct; struct SendData { ALeffectslot *Slot; float Gain; float GainHF; float HFReference; float GainLF; float LFReference; }; std::array Send; /** * Last user-specified offset, and the offset type (bytes, samples, or * seconds). */ double Offset{0.0}; ALenum OffsetType{AL_NONE}; /** Source type (static, streaming, or undetermined) */ ALenum SourceType{AL_UNDETERMINED}; /** Source state (initial, playing, paused, or stopped) */ ALenum state{AL_INITIAL}; /** Source Buffer Queue head. */ al::deque mQueue; al::atomic_invflag mPropsDirty; /* Index into the context's Voices array. Lazily updated, only checked and * reset when looking up the voice. */ ALuint VoiceIdx{INVALID_VOICE_IDX}; /** Self ID */ ALuint id{0}; ALsource(); ~ALsource(); ALsource(const ALsource&) = delete; ALsource& operator=(const ALsource&) = delete; DISABLE_ALLOC() #ifdef ALSOFT_EAX public: void eax_initialize(ALCcontext *context) noexcept; void eax_dispatch( const EaxEaxCall& eax_call); void eax_update_filters(); void eax_update( EaxContextSharedDirtyFlags dirty_flags); void eax_commit(); void eax_commit_and_update(); static ALsource* eax_lookup_source( ALCcontext& al_context, ALuint source_id) noexcept; private: static constexpr auto eax_max_speakers = 9; using EaxActiveFxSlots = std::array; using EaxSpeakerLevels = std::array; struct Eax { using Sends = std::array; EAX50ACTIVEFXSLOTS active_fx_slots{}; EAX50SOURCEPROPERTIES source{}; Sends sends{}; EaxSpeakerLevels speaker_levels{}; }; // Eax bool eax_uses_primary_id_{}; bool eax_has_active_fx_slots_{}; bool eax_are_active_fx_slots_dirty_{}; ALCcontext* eax_al_context_{}; Eax eax_{}; Eax eax_d_{}; EaxActiveFxSlots eax_active_fx_slots_{}; EaxSourceSendsDirtyFlags eax_sends_dirty_flags_{}; EaxSourceSourceFilterDirtyFlags eax_source_dirty_filter_flags_{}; EaxSourceSourceMiscDirtyFlags eax_source_dirty_misc_flags_{}; [[noreturn]] static void eax_fail( const char* message); bool eax_is_initialized() const noexcept { return eax_al_context_; } void eax_set_source_defaults() noexcept; void eax_set_active_fx_slots_defaults() noexcept; void eax_set_send_defaults(EAXSOURCEALLSENDPROPERTIES& eax_send) noexcept; void eax_set_sends_defaults() noexcept; void eax_set_speaker_levels_defaults() noexcept; void eax_set_defaults() noexcept; static float eax_calculate_dst_occlusion_mb( long src_occlusion_mb, float path_ratio, float lf_ratio) noexcept; EaxAlLowPassParam eax_create_direct_filter_param() const noexcept; EaxAlLowPassParam eax_create_room_filter_param( const ALeffectslot& fx_slot, const EAXSOURCEALLSENDPROPERTIES& send) const noexcept; void eax_set_fx_slots(); void eax_initialize_fx_slots(); void eax_update_direct_filter_internal(); void eax_update_room_filters_internal(); void eax_update_filters_internal(); void eax_update_primary_fx_slot_id(); void eax_defer_active_fx_slots( const EaxEaxCall& eax_call); static const char* eax_get_exclusion_name() noexcept; static const char* eax_get_exclusion_lf_ratio_name() noexcept; static const char* eax_get_occlusion_name() noexcept; static const char* eax_get_occlusion_lf_ratio_name() noexcept; static const char* eax_get_occlusion_direct_ratio_name() noexcept; static const char* eax_get_occlusion_room_ratio_name() noexcept; static void eax_validate_send_receiving_fx_slot_guid( const GUID& guidReceivingFXSlotID); static void eax_validate_send_send( long lSend); static void eax_validate_send_send_hf( long lSendHF); static void eax_validate_send_occlusion( long lOcclusion); static void eax_validate_send_occlusion_lf_ratio( float flOcclusionLFRatio); static void eax_validate_send_occlusion_room_ratio( float flOcclusionRoomRatio); static void eax_validate_send_occlusion_direct_ratio( float flOcclusionDirectRatio); static void eax_validate_send_exclusion( long lExclusion); static void eax_validate_send_exclusion_lf_ratio( float flExclusionLFRatio); static void eax_validate_send( const EAXSOURCESENDPROPERTIES& all); static void eax_validate_send_exclusion_all( const EAXSOURCEEXCLUSIONSENDPROPERTIES& all); static void eax_validate_send_occlusion_all( const EAXSOURCEOCCLUSIONSENDPROPERTIES& all); static void eax_validate_send_all( const EAXSOURCEALLSENDPROPERTIES& all); static EaxFxSlotIndexValue eax_get_send_index( const GUID& send_guid); void eax_defer_send_send( long lSend, EaxFxSlotIndexValue index); void eax_defer_send_send_hf( long lSendHF, EaxFxSlotIndexValue index); void eax_defer_send_occlusion( long lOcclusion, EaxFxSlotIndexValue index); void eax_defer_send_occlusion_lf_ratio( float flOcclusionLFRatio, EaxFxSlotIndexValue index); void eax_defer_send_occlusion_room_ratio( float flOcclusionRoomRatio, EaxFxSlotIndexValue index); void eax_defer_send_occlusion_direct_ratio( float flOcclusionDirectRatio, EaxFxSlotIndexValue index); void eax_defer_send_exclusion( long lExclusion, EaxFxSlotIndexValue index); void eax_defer_send_exclusion_lf_ratio( float flExclusionLFRatio, EaxFxSlotIndexValue index); void eax_defer_send( const EAXSOURCESENDPROPERTIES& all, EaxFxSlotIndexValue index); void eax_defer_send_exclusion_all( const EAXSOURCEEXCLUSIONSENDPROPERTIES& all, EaxFxSlotIndexValue index); void eax_defer_send_occlusion_all( const EAXSOURCEOCCLUSIONSENDPROPERTIES& all, EaxFxSlotIndexValue index); void eax_defer_send_all( const EAXSOURCEALLSENDPROPERTIES& all, EaxFxSlotIndexValue index); void eax_defer_send( const EaxEaxCall& eax_call); void eax_defer_send_exclusion_all( const EaxEaxCall& eax_call); void eax_defer_send_occlusion_all( const EaxEaxCall& eax_call); void eax_defer_send_all( const EaxEaxCall& eax_call); static void eax_validate_source_direct( long direct); static void eax_validate_source_direct_hf( long direct_hf); static void eax_validate_source_room( long room); static void eax_validate_source_room_hf( long room_hf); static void eax_validate_source_obstruction( long obstruction); static void eax_validate_source_obstruction_lf_ratio( float obstruction_lf_ratio); static void eax_validate_source_occlusion( long occlusion); static void eax_validate_source_occlusion_lf_ratio( float occlusion_lf_ratio); static void eax_validate_source_occlusion_room_ratio( float occlusion_room_ratio); static void eax_validate_source_occlusion_direct_ratio( float occlusion_direct_ratio); static void eax_validate_source_exclusion( long exclusion); static void eax_validate_source_exclusion_lf_ratio( float exclusion_lf_ratio); static void eax_validate_source_outside_volume_hf( long outside_volume_hf); static void eax_validate_source_doppler_factor( float doppler_factor); static void eax_validate_source_rolloff_factor( float rolloff_factor); static void eax_validate_source_room_rolloff_factor( float room_rolloff_factor); static void eax_validate_source_air_absorption_factor( float air_absorption_factor); static void eax_validate_source_flags( unsigned long flags, int eax_version); static void eax_validate_source_macro_fx_factor( float macro_fx_factor); static void eax_validate_source_2d_all( const EAXSOURCE2DPROPERTIES& all, int eax_version); static void eax_validate_source_obstruction_all( const EAXOBSTRUCTIONPROPERTIES& all); static void eax_validate_source_exclusion_all( const EAXEXCLUSIONPROPERTIES& all); static void eax_validate_source_occlusion_all( const EAXOCCLUSIONPROPERTIES& all); static void eax_validate_source_all( const EAX20BUFFERPROPERTIES& all, int eax_version); static void eax_validate_source_all( const EAX30SOURCEPROPERTIES& all, int eax_version); static void eax_validate_source_all( const EAX50SOURCEPROPERTIES& all, int eax_version); static void eax_validate_source_speaker_id( long speaker_id); static void eax_validate_source_speaker_level( long speaker_level); static void eax_validate_source_speaker_level_all( const EAXSPEAKERLEVELPROPERTIES& all); void eax_defer_source_direct( long lDirect); void eax_defer_source_direct_hf( long lDirectHF); void eax_defer_source_room( long lRoom); void eax_defer_source_room_hf( long lRoomHF); void eax_defer_source_obstruction( long lObstruction); void eax_defer_source_obstruction_lf_ratio( float flObstructionLFRatio); void eax_defer_source_occlusion( long lOcclusion); void eax_defer_source_occlusion_lf_ratio( float flOcclusionLFRatio); void eax_defer_source_occlusion_room_ratio( float flOcclusionRoomRatio); void eax_defer_source_occlusion_direct_ratio( float flOcclusionDirectRatio); void eax_defer_source_exclusion( long lExclusion); void eax_defer_source_exclusion_lf_ratio( float flExclusionLFRatio); void eax_defer_source_outside_volume_hf( long lOutsideVolumeHF); void eax_defer_source_doppler_factor( float flDopplerFactor); void eax_defer_source_rolloff_factor( float flRolloffFactor); void eax_defer_source_room_rolloff_factor( float flRoomRolloffFactor); void eax_defer_source_air_absorption_factor( float flAirAbsorptionFactor); void eax_defer_source_flags( unsigned long ulFlags); void eax_defer_source_macro_fx_factor( float flMacroFXFactor); void eax_defer_source_2d_all( const EAXSOURCE2DPROPERTIES& all); void eax_defer_source_obstruction_all( const EAXOBSTRUCTIONPROPERTIES& all); void eax_defer_source_exclusion_all( const EAXEXCLUSIONPROPERTIES& all); void eax_defer_source_occlusion_all( const EAXOCCLUSIONPROPERTIES& all); void eax_defer_source_all( const EAX20BUFFERPROPERTIES& all); void eax_defer_source_all( const EAX30SOURCEPROPERTIES& all); void eax_defer_source_all( const EAX50SOURCEPROPERTIES& all); void eax_defer_source_speaker_level_all( const EAXSPEAKERLEVELPROPERTIES& all); void eax_defer_source_direct( const EaxEaxCall& eax_call); void eax_defer_source_direct_hf( const EaxEaxCall& eax_call); void eax_defer_source_room( const EaxEaxCall& eax_call); void eax_defer_source_room_hf( const EaxEaxCall& eax_call); void eax_defer_source_obstruction( const EaxEaxCall& eax_call); void eax_defer_source_obstruction_lf_ratio( const EaxEaxCall& eax_call); void eax_defer_source_occlusion( const EaxEaxCall& eax_call); void eax_defer_source_occlusion_lf_ratio( const EaxEaxCall& eax_call); void eax_defer_source_occlusion_room_ratio( const EaxEaxCall& eax_call); void eax_defer_source_occlusion_direct_ratio( const EaxEaxCall& eax_call); void eax_defer_source_exclusion( const EaxEaxCall& eax_call); void eax_defer_source_exclusion_lf_ratio( const EaxEaxCall& eax_call); void eax_defer_source_outside_volume_hf( const EaxEaxCall& eax_call); void eax_defer_source_doppler_factor( const EaxEaxCall& eax_call); void eax_defer_source_rolloff_factor( const EaxEaxCall& eax_call); void eax_defer_source_room_rolloff_factor( const EaxEaxCall& eax_call); void eax_defer_source_air_absorption_factor( const EaxEaxCall& eax_call); void eax_defer_source_flags( const EaxEaxCall& eax_call); void eax_defer_source_macro_fx_factor( const EaxEaxCall& eax_call); void eax_defer_source_2d_all( const EaxEaxCall& eax_call); void eax_defer_source_obstruction_all( const EaxEaxCall& eax_call); void eax_defer_source_exclusion_all( const EaxEaxCall& eax_call); void eax_defer_source_occlusion_all( const EaxEaxCall& eax_call); void eax_defer_source_all( const EaxEaxCall& eax_call); void eax_defer_source_speaker_level_all( const EaxEaxCall& eax_call); void eax_set_outside_volume_hf(); void eax_set_doppler_factor(); void eax_set_rolloff_factor(); void eax_set_room_rolloff_factor(); void eax_set_air_absorption_factor(); void eax_set_direct_hf_auto_flag(); void eax_set_room_auto_flag(); void eax_set_room_hf_auto_flag(); void eax_set_flags(); void eax_set_macro_fx_factor(); void eax_set_speaker_levels(); void eax_apply_deferred(); void eax_set( const EaxEaxCall& eax_call); static const GUID& eax_get_send_fx_slot_guid( int eax_version, EaxFxSlotIndexValue fx_slot_index); static void eax_copy_send( const EAXSOURCEALLSENDPROPERTIES& src_send, EAXSOURCESENDPROPERTIES& dst_send); static void eax_copy_send( const EAXSOURCEALLSENDPROPERTIES& src_send, EAXSOURCEALLSENDPROPERTIES& dst_send); static void eax_copy_send( const EAXSOURCEALLSENDPROPERTIES& src_send, EAXSOURCEOCCLUSIONSENDPROPERTIES& dst_send); static void eax_copy_send( const EAXSOURCEALLSENDPROPERTIES& src_send, EAXSOURCEEXCLUSIONSENDPROPERTIES& dst_send); template< typename TException, typename TSrcSend > void eax_api_get_send_properties( const EaxEaxCall& eax_call) const { const auto eax_version = eax_call.get_version(); const auto dst_sends = eax_call.get_values(); const auto send_count = dst_sends.size(); for (auto fx_slot_index = EaxFxSlotIndexValue{}; fx_slot_index < send_count; ++fx_slot_index) { auto& dst_send = dst_sends[fx_slot_index]; const auto& src_send = eax_.sends[fx_slot_index]; eax_copy_send(src_send, dst_send); dst_send.guidReceivingFXSlotID = eax_get_send_fx_slot_guid(eax_version, fx_slot_index); } } void eax_api_get_source_all_v2( const EaxEaxCall& eax_call); void eax_api_get_source_all_v3( const EaxEaxCall& eax_call); void eax_api_get_source_all_v5( const EaxEaxCall& eax_call); void eax_api_get_source_all( const EaxEaxCall& eax_call); void eax_api_get_source_all_obstruction( const EaxEaxCall& eax_call); void eax_api_get_source_all_occlusion( const EaxEaxCall& eax_call); void eax_api_get_source_all_exclusion( const EaxEaxCall& eax_call); void eax_api_get_source_active_fx_slot_id( const EaxEaxCall& eax_call); void eax_api_get_source_all_2d( const EaxEaxCall& eax_call); void eax_api_get_source_speaker_level_all( const EaxEaxCall& eax_call); void eax_get( const EaxEaxCall& eax_call); // `alSource3i(source, AL_AUXILIARY_SEND_FILTER, ...)` void eax_set_al_source_send(ALeffectslot *slot, size_t sendidx, const EaxAlLowPassParam &filter); #endif // ALSOFT_EAX }; void UpdateAllSourceProps(ALCcontext *context); #endif