#ifndef ALU_H #define ALU_H #include #include #include #include #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" #include "al/buffer.h" #include "alcmain.h" #include "almalloc.h" #include "alspan.h" #include "ambidefs.h" #include "devformat.h" #include "filters/biquad.h" #include "filters/nfc.h" #include "filters/splitter.h" #include "hrtf.h" #include "logging.h" struct ALbufferlistitem; struct ALeffectslot; struct BSincTable; enum class DistanceModel; #define MAX_PITCH 255 #define MAX_SENDS 16 #define DITHER_RNG_SEED 22222 enum SpatializeMode { SpatializeOff = AL_FALSE, SpatializeOn = AL_TRUE, SpatializeAuto = AL_AUTO_SOFT }; enum Resampler { PointResampler, LinearResampler, FIR4Resampler, BSinc12Resampler, BSinc24Resampler, ResamplerMax = BSinc24Resampler }; extern Resampler ResamplerDefault; /* The number of distinct scale and phase intervals within the bsinc filter * table. */ #define BSINC_SCALE_BITS 4 #define BSINC_SCALE_COUNT (1< dst); void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table); extern const BSincTable bsinc12; extern const BSincTable bsinc24; enum { AF_None = 0, AF_LowPass = 1, AF_HighPass = 2, AF_BandPass = AF_LowPass | AF_HighPass }; struct MixHrtfFilter { const HrirArray *Coeffs; ALsizei Delay[2]; ALfloat Gain; ALfloat GainStep; }; struct DirectParams { BiquadFilter LowPass; BiquadFilter HighPass; NfcFilter NFCtrlFilter; struct { HrtfFilter Old; HrtfFilter Target; HrtfState State; } Hrtf; struct { ALfloat Current[MAX_OUTPUT_CHANNELS]; ALfloat Target[MAX_OUTPUT_CHANNELS]; } Gains; }; struct SendParams { BiquadFilter LowPass; BiquadFilter HighPass; struct { ALfloat Current[MAX_OUTPUT_CHANNELS]; ALfloat Target[MAX_OUTPUT_CHANNELS]; } Gains; }; struct ALvoicePropsBase { ALfloat Pitch; ALfloat Gain; ALfloat OuterGain; ALfloat MinGain; ALfloat MaxGain; ALfloat InnerAngle; ALfloat OuterAngle; ALfloat RefDistance; ALfloat MaxDistance; ALfloat RolloffFactor; std::array Position; std::array Velocity; std::array Direction; std::array OrientAt; std::array OrientUp; ALboolean HeadRelative; DistanceModel mDistanceModel; Resampler mResampler; ALboolean DirectChannels; SpatializeMode mSpatializeMode; ALboolean DryGainHFAuto; ALboolean WetGainAuto; ALboolean WetGainHFAuto; ALfloat OuterGainHF; ALfloat AirAbsorptionFactor; ALfloat RoomRolloffFactor; ALfloat DopplerFactor; std::array StereoPan; ALfloat Radius; /** Direct filter and auxiliary send info. */ struct { ALfloat Gain; ALfloat GainHF; ALfloat HFReference; ALfloat GainLF; ALfloat LFReference; } Direct; struct SendData { ALeffectslot *Slot; ALfloat Gain; ALfloat GainHF; ALfloat HFReference; ALfloat GainLF; ALfloat LFReference; } Send[MAX_SENDS]; }; struct ALvoiceProps : public ALvoicePropsBase { std::atomic next{nullptr}; DEF_NEWDEL(ALvoiceProps) }; #define VOICE_IS_STATIC (1u<<0) #define VOICE_IS_FADING (1u<<1) /* Fading sources use gain stepping for smooth transitions. */ #define VOICE_IS_AMBISONIC (1u<<2) /* Voice needs HF scaling for ambisonic upsampling. */ #define VOICE_HAS_HRTF (1u<<3) #define VOICE_HAS_NFC (1u<<4) struct ALvoice { enum State { Stopped = 0, Playing = 1, Stopping = 2 }; std::atomic mUpdate{nullptr}; std::atomic mSourceID{0u}; std::atomic mPlayState{Stopped}; ALvoicePropsBase mProps; /** * Source offset in samples, relative to the currently playing buffer, NOT * the whole queue. */ std::atomic mPosition; /** Fractional (fixed-point) offset to the next sample. */ std::atomic mPositionFrac; /* Current buffer queue item being played. */ std::atomic mCurrentBuffer; /* Buffer queue item to loop to at end of queue (will be NULL for non- * looping voices). */ std::atomic mLoopBuffer; /* Properties for the attached buffer(s). */ FmtChannels mFmtChannels; ALuint mFrequency; ALsizei mNumChannels; ALsizei mSampleSize; /** Current target parameters used for mixing. */ ALint mStep; ResamplerFunc mResampler; InterpState mResampleState; ALuint mFlags; struct DirectData { int FilterType; al::span Buffer; }; DirectData mDirect; struct SendData { int FilterType; al::span Buffer; }; std::array mSend; struct ChannelData { alignas(16) std::array mPrevSamples; ALfloat mAmbiScale; BandSplitter mAmbiSplitter; DirectParams mDryParams; std::array mWetParams; }; std::array mChans; ALvoice() = default; ALvoice(const ALvoice&) = delete; ALvoice(ALvoice&& rhs) noexcept { *this = std::move(rhs); } ~ALvoice() { delete mUpdate.exchange(nullptr, std::memory_order_acq_rel); } ALvoice& operator=(const ALvoice&) = delete; ALvoice& operator=(ALvoice&& rhs) noexcept { ALvoiceProps *old_update{mUpdate.load(std::memory_order_relaxed)}; mUpdate.store(rhs.mUpdate.exchange(old_update, std::memory_order_relaxed), std::memory_order_relaxed); mSourceID.store(rhs.mSourceID.load(std::memory_order_relaxed), std::memory_order_relaxed); mPlayState.store(rhs.mPlayState.load(std::memory_order_relaxed), std::memory_order_relaxed); mProps = rhs.mProps; mPosition.store(rhs.mPosition.load(std::memory_order_relaxed), std::memory_order_relaxed); mPositionFrac.store(rhs.mPositionFrac.load(std::memory_order_relaxed), std::memory_order_relaxed); mCurrentBuffer.store(rhs.mCurrentBuffer.load(std::memory_order_relaxed), std::memory_order_relaxed); mLoopBuffer.store(rhs.mLoopBuffer.load(std::memory_order_relaxed), std::memory_order_relaxed); mFmtChannels = rhs.mFmtChannels; mFrequency = rhs.mFrequency; mNumChannels = rhs.mNumChannels; mSampleSize = rhs.mSampleSize; mStep = rhs.mStep; mResampler = rhs.mResampler; mResampleState = rhs.mResampleState; mFlags = rhs.mFlags; mDirect = rhs.mDirect; mSend = rhs.mSend; mChans = rhs.mChans; return *this; } void mix(ALvoice::State vstate, ALCcontext *Context, const ALuint SamplesToDo); }; using MixerFunc = void(*)(const al::span InSamples, const al::span OutBuffer, float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos); using RowMixerFunc = void(*)(const al::span OutBuffer, const al::span Gains, const float *InSamples, const size_t InStride); using HrtfMixerFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, const ALfloat *InSamples, float2 *AccumSamples, const size_t OutPos, const ALsizei IrSize, MixHrtfFilter *hrtfparams, const size_t BufferSize); using HrtfMixerBlendFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, const ALfloat *InSamples, float2 *AccumSamples, const size_t OutPos, const ALsizei IrSize, const HrtfFilter *oldparams, MixHrtfFilter *newparams, const size_t BufferSize); using HrtfDirectMixerFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, const al::span InSamples, float2 *AccumSamples, DirectHrtfState *State, const size_t BufferSize); #define GAIN_MIX_MAX (1000.0f) /* +60dB */ #define GAIN_SILENCE_THRESHOLD (0.00001f) /* -100dB */ #define SPEEDOFSOUNDMETRESPERSEC (343.3f) #define AIRABSORBGAINHF (0.99426f) /* -0.05dB */ /* Target gain for the reverb decay feedback reaching the decay time. */ #define REVERB_DECAY_GAIN (0.001f) /* -60 dB */ #define FRACTIONBITS (12) #define FRACTIONONE (1< GetAmbiIdentityRow(size_t i) noexcept { std::array ret{}; ret[i] = 1.0f; return ret; } void aluMixData(ALCdevice *device, ALvoid *OutBuffer, const ALuint NumSamples); /* Caller must lock the device state, and the mixer must not be running. */ void aluHandleDisconnect(ALCdevice *device, const char *msg, ...) DECL_FORMAT(printf, 2, 3); extern MixerFunc MixSamples; extern RowMixerFunc MixRowSamples; extern const ALfloat ConeScale; extern const ALfloat ZScale; #endif