diff options
Diffstat (limited to 'alc/voice.h')
-rw-r--r-- | alc/voice.h | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/alc/voice.h b/alc/voice.h new file mode 100644 index 00000000..d6b624f9 --- /dev/null +++ b/alc/voice.h @@ -0,0 +1,293 @@ +#ifndef VOICE_H +#define VOICE_H + +#include <array> + +#include "AL/al.h" +#include "AL/alext.h" + +#include "al/buffer.h" +#include "alspan.h" +#include "alu.h" +#include "filters/biquad.h" +#include "filters/nfc.h" +#include "filters/splitter.h" +#include "hrtf.h" + +enum class DistanceModel; + + +enum SpatializeMode { + SpatializeOff = AL_FALSE, + SpatializeOn = AL_TRUE, + SpatializeAuto = AL_AUTO_SOFT +}; + +enum class Resampler { + Point, + Linear, + Cubic, + FastBSinc12, + BSinc12, + FastBSinc24, + BSinc24, + + Max = BSinc24 +}; +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<<BSINC_SCALE_BITS) +#define BSINC_PHASE_BITS 5 +#define BSINC_PHASE_COUNT (1<<BSINC_PHASE_BITS) + +/* Interpolator state. Kind of a misnomer since the interpolator itself is + * stateless. This just keeps it from having to recompute scale-related + * mappings for every sample. + */ +struct BsincState { + float sf; /* Scale interpolation factor. */ + ALuint m; /* Coefficient count. */ + ALuint l; /* Left coefficient offset. */ + /* Filter coefficients, followed by the phase, scale, and scale-phase + * delta coefficients. Starting at phase index 0, each subsequent phase + * index follows contiguously. + */ + const float *filter; +}; + +union InterpState { + BsincState bsinc; +}; + +using ResamplerFunc = const float*(*)(const InterpState *state, const float *RESTRICT src, + ALuint frac, ALuint increment, const al::span<float> dst); + +ResamplerFunc PrepareResampler(Resampler resampler, ALuint increment, InterpState *state); + + +enum { + AF_None = 0, + AF_LowPass = 1, + AF_HighPass = 2, + AF_BandPass = AF_LowPass | AF_HighPass +}; + + +struct MixHrtfFilter { + const HrirArray *Coeffs; + ALsizei Delay[2]; + float Gain; + float GainStep; +}; + + +struct DirectParams { + BiquadFilter LowPass; + BiquadFilter HighPass; + + NfcFilter NFCtrlFilter; + + struct { + HrtfFilter Old; + HrtfFilter Target; + HrtfState State; + } Hrtf; + + struct { + std::array<float,MAX_OUTPUT_CHANNELS> Current; + std::array<float,MAX_OUTPUT_CHANNELS> Target; + } Gains; +}; + +struct SendParams { + BiquadFilter LowPass; + BiquadFilter HighPass; + + struct { + std::array<float,MAX_OUTPUT_CHANNELS> Current; + std::array<float,MAX_OUTPUT_CHANNELS> Target; + } Gains; +}; + + +struct ALvoicePropsBase { + float Pitch; + float Gain; + float OuterGain; + float MinGain; + float MaxGain; + float InnerAngle; + float OuterAngle; + float RefDistance; + float MaxDistance; + float RolloffFactor; + std::array<float,3> Position; + std::array<float,3> Velocity; + std::array<float,3> Direction; + std::array<float,3> OrientAt; + std::array<float,3> OrientUp; + bool HeadRelative; + DistanceModel mDistanceModel; + Resampler mResampler; + bool DirectChannels; + SpatializeMode mSpatializeMode; + + bool DryGainHFAuto; + bool WetGainAuto; + bool WetGainHFAuto; + float OuterGainHF; + + float AirAbsorptionFactor; + float RoomRolloffFactor; + float DopplerFactor; + + std::array<float,2> StereoPan; + + float Radius; + + /** 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; + } Send[MAX_SENDS]; +}; + +struct ALvoiceProps : public ALvoicePropsBase { + std::atomic<ALvoiceProps*> 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<ALvoiceProps*> mUpdate{nullptr}; + + std::atomic<ALuint> mSourceID{0u}; + std::atomic<State> mPlayState{Stopped}; + + ALvoicePropsBase mProps; + + /** + * Source offset in samples, relative to the currently playing buffer, NOT + * the whole queue. + */ + std::atomic<ALuint> mPosition; + /** Fractional (fixed-point) offset to the next sample. */ + std::atomic<ALuint> mPositionFrac; + + /* Current buffer queue item being played. */ + std::atomic<ALbufferlistitem*> mCurrentBuffer; + + /* Buffer queue item to loop to at end of queue (will be NULL for non- + * looping voices). + */ + std::atomic<ALbufferlistitem*> mLoopBuffer; + + /* Properties for the attached buffer(s). */ + FmtChannels mFmtChannels; + ALuint mFrequency; + ALuint mNumChannels; + ALuint mSampleSize; + + /** Current target parameters used for mixing. */ + ALuint mStep; + + ResamplerFunc mResampler; + + InterpState mResampleState; + + ALuint mFlags; + + struct TargetData { + int FilterType; + al::span<FloatBufferLine> Buffer; + }; + TargetData mDirect; + std::array<TargetData,MAX_SENDS> mSend; + + struct ChannelData { + alignas(16) std::array<float,MAX_RESAMPLER_PADDING> mPrevSamples; + + float mAmbiScale; + BandSplitter mAmbiSplitter; + + DirectParams mDryParams; + std::array<SendParams,MAX_SENDS> mWetParams; + }; + std::array<ChannelData,MAX_INPUT_CHANNELS> 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(const State vstate, ALCcontext *Context, const ALuint SamplesToDo); +}; + +#endif /* VOICE_H */ |