aboutsummaryrefslogtreecommitdiffstats
path: root/alc/alu.h
diff options
context:
space:
mode:
Diffstat (limited to 'alc/alu.h')
-rw-r--r--alc/alu.h466
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