diff options
Diffstat (limited to 'alc/alu.h')
-rw-r--r-- | alc/alu.h | 466 |
1 files changed, 466 insertions, 0 deletions
diff --git a/alc/alu.h b/alc/alu.h new file mode 100644 index 00000000..9acf904a --- /dev/null +++ b/alc/alu.h @@ -0,0 +1,466 @@ +#ifndef _ALU_H_ +#define _ALU_H_ + +#include <array> +#include <atomic> +#include <cmath> +#include <cstddef> + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "alBuffer.h" +#include "alcmain.h" +#include "almalloc.h" +#include "alspan.h" +#include "ambidefs.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<<BSINC_SCALE_BITS) +#define BSINC_PHASE_BITS 4 +#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 { + ALfloat sf; /* Scale interpolation factor. */ + ALsizei m; /* Coefficient count. */ + ALsizei l; /* Left coefficient offset. */ + /* Filter coefficients, followed by the scale, phase, and scale-phase + * delta coefficients. Starting at phase index 0, each subsequent phase + * index follows contiguously. + */ + const ALfloat *filter; +}; + +union InterpState { + BsincState bsinc; +}; + +using ResamplerFunc = const ALfloat*(*)(const InterpState *state, + const ALfloat *RESTRICT src, ALsizei frac, ALint increment, + ALfloat *RESTRICT dst, ALsizei dstlen); + +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<ALfloat> *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<ALfloat,3> Position; + std::array<ALfloat,3> Velocity; + std::array<ALfloat,3> Direction; + std::array<ALfloat,3> OrientAt; + std::array<ALfloat,3> 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<ALfloat,2> 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<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<ALsizei> 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; + ALsizei mNumChannels; + ALsizei mSampleSize; + + /** Current target parameters used for mixing. */ + ALint mStep; + + ResamplerFunc mResampler; + + InterpState mResampleState; + + ALuint mFlags; + + struct DirectData { + int FilterType; + al::span<FloatBufferLine> Buffer; + }; + DirectData mDirect; + + struct SendData { + int FilterType; + al::span<FloatBufferLine> Buffer; + }; + std::array<SendData,MAX_SENDS> mSend; + + struct ChannelData { + alignas(16) std::array<ALfloat,MAX_RESAMPLE_PADDING*2> mPrevSamples; + + ALfloat 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() { 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; + } +}; + + +using MixerFunc = void(*)(const ALfloat *data, const al::span<FloatBufferLine> OutBuffer, + ALfloat *CurrentGains, const ALfloat *TargetGains, const ALsizei Counter, const ALsizei OutPos, + const ALsizei BufferSize); +using RowMixerFunc = void(*)(FloatBufferLine &OutBuffer, const ALfloat *gains, + const al::span<const FloatBufferLine> InSamples, const ALsizei InPos, + const ALsizei BufferSize); +using HrtfMixerFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize, + MixHrtfFilter *hrtfparams, const ALsizei BufferSize); +using HrtfMixerBlendFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize, + const HrtfFilter *oldparams, MixHrtfFilter *newparams, const ALsizei BufferSize); +using HrtfDirectMixerFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, DirectHrtfState *State, + const ALsizei 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<<FRACTIONBITS) +#define FRACTIONMASK (FRACTIONONE-1) + + +inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu) noexcept +{ return val1 + (val2-val1)*mu; } +inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu) noexcept +{ + ALfloat mu2 = mu*mu, mu3 = mu2*mu; + ALfloat a0 = -0.5f*mu3 + mu2 + -0.5f*mu; + ALfloat a1 = 1.5f*mu3 + -2.5f*mu2 + 1.0f; + ALfloat a2 = -1.5f*mu3 + 2.0f*mu2 + 0.5f*mu; + ALfloat a3 = 0.5f*mu3 + -0.5f*mu2; + return val1*a0 + val2*a1 + val3*a2 + val4*a3; +} + + +enum HrtfRequestMode { + Hrtf_Default = 0, + Hrtf_Enable = 1, + Hrtf_Disable = 2, +}; + +void aluInit(void); + +void aluInitMixer(void); + +ResamplerFunc SelectResampler(Resampler resampler); + +/* aluInitRenderer + * + * Set up the appropriate panning method and mixing method given the device + * properties. + */ +void aluInitRenderer(ALCdevice *device, ALint hrtf_id, HrtfRequestMode hrtf_appreq, HrtfRequestMode hrtf_userreq); + +void aluInitEffectPanning(ALeffectslot *slot, ALCdevice *device); + +void ProcessHrtf(ALCdevice *device, const ALsizei SamplesToDo); +void ProcessAmbiDec(ALCdevice *device, const ALsizei SamplesToDo); +void ProcessUhj(ALCdevice *device, const ALsizei SamplesToDo); +void ProcessBs2b(ALCdevice *device, const ALsizei SamplesToDo); + +/** + * Calculates ambisonic encoder coefficients using the X, Y, and Z direction + * components, which must represent a normalized (unit length) vector, and the + * spread is the angular width of the sound (0...tau). + * + * NOTE: The components use ambisonic coordinates. As a result: + * + * Ambisonic Y = OpenAL -X + * Ambisonic Z = OpenAL Y + * Ambisonic X = OpenAL -Z + * + * The components are ordered such that OpenAL's X, Y, and Z are the first, + * second, and third parameters respectively -- simply negate X and Z. + */ +void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread, + ALfloat (&coeffs)[MAX_AMBI_CHANNELS]); + +/** + * CalcDirectionCoeffs + * + * Calculates ambisonic coefficients based on an OpenAL direction vector. The + * vector must be normalized (unit length), and the spread is the angular width + * of the sound (0...tau). + */ +inline void CalcDirectionCoeffs(const ALfloat (&dir)[3], ALfloat spread, ALfloat (&coeffs)[MAX_AMBI_CHANNELS]) +{ + /* Convert from OpenAL coords to Ambisonics. */ + CalcAmbiCoeffs(-dir[0], dir[1], -dir[2], spread, coeffs); +} + +/** + * CalcAngleCoeffs + * + * Calculates ambisonic coefficients based on azimuth and elevation. The + * azimuth and elevation parameters are in radians, going right and up + * respectively. + */ +inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat (&coeffs)[MAX_AMBI_CHANNELS]) +{ + ALfloat x = -std::sin(azimuth) * std::cos(elevation); + ALfloat y = std::sin(elevation); + ALfloat z = std::cos(azimuth) * std::cos(elevation); + + CalcAmbiCoeffs(x, y, z, spread, coeffs); +} + + +/** + * ComputePanGains + * + * Computes panning gains using the given channel decoder coefficients and the + * pre-calculated direction or angle coefficients. For B-Format sources, the + * coeffs are a 'slice' of a transform matrix for the input channel, used to + * scale and orient the sound samples. + */ +void ComputePanGains(const MixParams *mix, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat (&gains)[MAX_OUTPUT_CHANNELS]); + + +inline std::array<ALfloat,MAX_AMBI_CHANNELS> GetAmbiIdentityRow(size_t i) noexcept +{ + std::array<ALfloat,MAX_AMBI_CHANNELS> ret{}; + ret[i] = 1.0f; + return ret; +} + + +void MixVoice(ALvoice *voice, ALvoice::State vstate, const ALuint SourceID, ALCcontext *Context, const ALsizei SamplesToDo); + +void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei 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; +extern const ALboolean OverrideReverbSpeedOfSound; + +#endif |