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