aboutsummaryrefslogtreecommitdiffstats
path: root/alc/alcmain.h
diff options
context:
space:
mode:
Diffstat (limited to 'alc/alcmain.h')
-rw-r--r--alc/alcmain.h534
1 files changed, 534 insertions, 0 deletions
diff --git a/alc/alcmain.h b/alc/alcmain.h
new file mode 100644
index 00000000..a22e0e81
--- /dev/null
+++ b/alc/alcmain.h
@@ -0,0 +1,534 @@
+#ifndef ALC_MAIN_H
+#define ALC_MAIN_H
+
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <chrono>
+#include <cstdint>
+#include <cstddef>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/alext.h"
+
+#include "albyte.h"
+#include "almalloc.h"
+#include "alnumeric.h"
+#include "alspan.h"
+#include "ambidefs.h"
+#include "atomic.h"
+#include "hrtf.h"
+#include "inprogext.h"
+#include "vector.h"
+
+class BFormatDec;
+struct ALbuffer;
+struct ALeffect;
+struct ALfilter;
+struct BackendBase;
+struct Compressor;
+struct EffectState;
+struct FrontStablizer;
+struct Uhj2Encoder;
+struct bs2b;
+
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
+#define IS_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#else
+static const union {
+ ALuint u;
+ ALubyte b[sizeof(ALuint)];
+} EndianTest = { 1 };
+#define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1)
+#endif
+
+
+#define MIN_OUTPUT_RATE 8000
+#define DEFAULT_OUTPUT_RATE 44100
+#define DEFAULT_UPDATE_SIZE 882 /* 20ms */
+#define DEFAULT_NUM_UPDATES 3
+
+
+enum Channel {
+ FrontLeft = 0,
+ FrontRight,
+ FrontCenter,
+ LFE,
+ BackLeft,
+ BackRight,
+ BackCenter,
+ SideLeft,
+ SideRight,
+
+ UpperFrontLeft,
+ UpperFrontRight,
+ UpperBackLeft,
+ UpperBackRight,
+ LowerFrontLeft,
+ LowerFrontRight,
+ LowerBackLeft,
+ LowerBackRight,
+
+ Aux0,
+ Aux1,
+ Aux2,
+ Aux3,
+ Aux4,
+ Aux5,
+ Aux6,
+ Aux7,
+ Aux8,
+ Aux9,
+ Aux10,
+ Aux11,
+ Aux12,
+ Aux13,
+ Aux14,
+ Aux15,
+
+ MaxChannels
+};
+
+
+/* Device formats */
+enum DevFmtType : ALenum {
+ DevFmtByte = ALC_BYTE_SOFT,
+ DevFmtUByte = ALC_UNSIGNED_BYTE_SOFT,
+ DevFmtShort = ALC_SHORT_SOFT,
+ DevFmtUShort = ALC_UNSIGNED_SHORT_SOFT,
+ DevFmtInt = ALC_INT_SOFT,
+ DevFmtUInt = ALC_UNSIGNED_INT_SOFT,
+ DevFmtFloat = ALC_FLOAT_SOFT,
+
+ DevFmtTypeDefault = DevFmtFloat
+};
+enum DevFmtChannels : ALenum {
+ DevFmtMono = ALC_MONO_SOFT,
+ DevFmtStereo = ALC_STEREO_SOFT,
+ DevFmtQuad = ALC_QUAD_SOFT,
+ DevFmtX51 = ALC_5POINT1_SOFT,
+ DevFmtX61 = ALC_6POINT1_SOFT,
+ DevFmtX71 = ALC_7POINT1_SOFT,
+ DevFmtAmbi3D = ALC_BFORMAT3D_SOFT,
+
+ /* Similar to 5.1, except using rear channels instead of sides */
+ DevFmtX51Rear = 0x70000000,
+
+ DevFmtChannelsDefault = DevFmtStereo
+};
+#define MAX_OUTPUT_CHANNELS (16)
+
+/* DevFmtType traits, providing the type, etc given a DevFmtType. */
+template<DevFmtType T>
+struct DevFmtTypeTraits { };
+
+template<>
+struct DevFmtTypeTraits<DevFmtByte> { using Type = ALbyte; };
+template<>
+struct DevFmtTypeTraits<DevFmtUByte> { using Type = ALubyte; };
+template<>
+struct DevFmtTypeTraits<DevFmtShort> { using Type = ALshort; };
+template<>
+struct DevFmtTypeTraits<DevFmtUShort> { using Type = ALushort; };
+template<>
+struct DevFmtTypeTraits<DevFmtInt> { using Type = ALint; };
+template<>
+struct DevFmtTypeTraits<DevFmtUInt> { using Type = ALuint; };
+template<>
+struct DevFmtTypeTraits<DevFmtFloat> { using Type = ALfloat; };
+
+
+ALsizei BytesFromDevFmt(DevFmtType type) noexcept;
+ALsizei ChannelsFromDevFmt(DevFmtChannels chans, ALsizei ambiorder) noexcept;
+inline ALsizei FrameSizeFromDevFmt(DevFmtChannels chans, DevFmtType type, ALsizei ambiorder) noexcept
+{ return ChannelsFromDevFmt(chans, ambiorder) * BytesFromDevFmt(type); }
+
+enum class AmbiLayout {
+ FuMa = ALC_FUMA_SOFT, /* FuMa channel order */
+ ACN = ALC_ACN_SOFT, /* ACN channel order */
+
+ Default = ACN
+};
+
+enum class AmbiNorm {
+ FuMa = ALC_FUMA_SOFT, /* FuMa normalization */
+ SN3D = ALC_SN3D_SOFT, /* SN3D normalization */
+ N3D = ALC_N3D_SOFT, /* N3D normalization */
+
+ Default = SN3D
+};
+
+
+enum DeviceType {
+ Playback,
+ Capture,
+ Loopback
+};
+
+
+enum RenderMode {
+ NormalRender,
+ StereoPair,
+ HrtfRender
+};
+
+
+struct BufferSubList {
+ uint64_t FreeMask{~0_u64};
+ ALbuffer *Buffers{nullptr}; /* 64 */
+
+ BufferSubList() noexcept = default;
+ BufferSubList(const BufferSubList&) = delete;
+ BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers}
+ { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; }
+ ~BufferSubList();
+
+ BufferSubList& operator=(const BufferSubList&) = delete;
+ BufferSubList& operator=(BufferSubList&& rhs) noexcept
+ { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; }
+};
+
+struct EffectSubList {
+ uint64_t FreeMask{~0_u64};
+ ALeffect *Effects{nullptr}; /* 64 */
+
+ EffectSubList() noexcept = default;
+ EffectSubList(const EffectSubList&) = delete;
+ EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects}
+ { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; }
+ ~EffectSubList();
+
+ EffectSubList& operator=(const EffectSubList&) = delete;
+ EffectSubList& operator=(EffectSubList&& rhs) noexcept
+ { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; }
+};
+
+struct FilterSubList {
+ uint64_t FreeMask{~0_u64};
+ ALfilter *Filters{nullptr}; /* 64 */
+
+ FilterSubList() noexcept = default;
+ FilterSubList(const FilterSubList&) = delete;
+ FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters}
+ { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; }
+ ~FilterSubList();
+
+ FilterSubList& operator=(const FilterSubList&) = delete;
+ FilterSubList& operator=(FilterSubList&& rhs) noexcept
+ { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; }
+};
+
+
+/* Maximum delay in samples for speaker distance compensation. */
+#define MAX_DELAY_LENGTH 1024
+
+class DistanceComp {
+public:
+ struct DistData {
+ ALfloat Gain{1.0f};
+ ALsizei Length{0}; /* Valid range is [0...MAX_DELAY_LENGTH). */
+ ALfloat *Buffer{nullptr};
+ };
+
+private:
+ std::array<DistData,MAX_OUTPUT_CHANNELS> mChannels;
+ al::vector<ALfloat,16> mSamples;
+
+public:
+ void setSampleCount(size_t new_size) { mSamples.resize(new_size); }
+ void clear() noexcept
+ {
+ for(auto &chan : mChannels)
+ {
+ chan.Gain = 1.0f;
+ chan.Length = 0;
+ chan.Buffer = nullptr;
+ }
+ using SampleVecT = decltype(mSamples);
+ SampleVecT{}.swap(mSamples);
+ }
+
+ ALfloat *getSamples() noexcept { return mSamples.data(); }
+
+ al::span<DistData,MAX_OUTPUT_CHANNELS> as_span() { return mChannels; }
+};
+
+struct BFChannelConfig {
+ ALfloat Scale;
+ ALsizei Index;
+};
+
+/* Size for temporary storage of buffer data, in ALfloats. Larger values need
+ * more memory, while smaller values may need more iterations. The value needs
+ * to be a sensible size, however, as it constrains the max stepping value used
+ * for mixing, as well as the maximum number of samples per mixing iteration.
+ */
+#define BUFFERSIZE 1024
+
+using FloatBufferLine = std::array<float,BUFFERSIZE>;
+
+/* Maximum number of samples to pad on either end of a buffer for resampling.
+ * Note that both the beginning and end need padding!
+ */
+#define MAX_RESAMPLE_PADDING 24
+
+
+struct MixParams {
+ /* Coefficient channel mapping for mixing to the buffer. */
+ std::array<BFChannelConfig,MAX_OUTPUT_CHANNELS> AmbiMap{};
+
+ al::span<FloatBufferLine> Buffer;
+};
+
+struct RealMixParams {
+ std::array<ALint,MaxChannels> ChannelIndex{};
+
+ al::span<FloatBufferLine> Buffer;
+};
+
+using POSTPROCESS = void(*)(ALCdevice *device, const ALsizei SamplesToDo);
+
+enum {
+ // Frequency was requested by the app or config file
+ FrequencyRequest,
+ // Channel configuration was requested by the config file
+ ChannelsRequest,
+ // Sample type was requested by the config file
+ SampleTypeRequest,
+
+ // Specifies if the DSP is paused at user request
+ DevicePaused,
+ // Specifies if the device is currently running
+ DeviceRunning,
+
+ DeviceFlagsCount
+};
+
+struct ALCdevice {
+ RefCount ref{1u};
+
+ std::atomic<bool> Connected{true};
+ const DeviceType Type{};
+
+ ALuint Frequency{};
+ ALuint UpdateSize{};
+ ALuint BufferSize{};
+
+ DevFmtChannels FmtChans{};
+ DevFmtType FmtType{};
+ ALboolean IsHeadphones{AL_FALSE};
+ ALsizei mAmbiOrder{0};
+ /* For DevFmtAmbi* output only, specifies the channel order and
+ * normalization.
+ */
+ AmbiLayout mAmbiLayout{AmbiLayout::Default};
+ AmbiNorm mAmbiScale{AmbiNorm::Default};
+
+ ALCenum LimiterState{ALC_DONT_CARE_SOFT};
+
+ std::string DeviceName;
+
+ // Device flags
+ al::bitfield<DeviceFlagsCount> Flags{};
+
+ std::string HrtfName;
+ al::vector<EnumeratedHrtf> HrtfList;
+ ALCenum HrtfStatus{ALC_FALSE};
+
+ std::atomic<ALCenum> LastError{ALC_NO_ERROR};
+
+ // Maximum number of sources that can be created
+ ALuint SourcesMax{};
+ // Maximum number of slots that can be created
+ ALuint AuxiliaryEffectSlotMax{};
+
+ ALCuint NumMonoSources{};
+ ALCuint NumStereoSources{};
+ ALsizei NumAuxSends{};
+
+ // Map of Buffers for this device
+ std::mutex BufferLock;
+ al::vector<BufferSubList> BufferList;
+
+ // Map of Effects for this device
+ std::mutex EffectLock;
+ al::vector<EffectSubList> EffectList;
+
+ // Map of Filters for this device
+ std::mutex FilterLock;
+ al::vector<FilterSubList> FilterList;
+
+ /* Rendering mode. */
+ RenderMode mRenderMode{NormalRender};
+
+ /* The average speaker distance as determined by the ambdec configuration,
+ * HRTF data set, or the NFC-HOA reference delay. Only used for NFC.
+ */
+ ALfloat AvgSpeakerDist{0.0f};
+
+ ALuint SamplesDone{0u};
+ std::chrono::nanoseconds ClockBase{0};
+ std::chrono::nanoseconds FixedLatency{0};
+
+ /* Temp storage used for mixer processing. */
+ alignas(16) ALfloat SourceData[BUFFERSIZE + MAX_RESAMPLE_PADDING*2];
+ alignas(16) ALfloat ResampledData[BUFFERSIZE];
+ alignas(16) ALfloat FilteredData[BUFFERSIZE];
+ union {
+ alignas(16) ALfloat HrtfSourceData[BUFFERSIZE + HRTF_HISTORY_LENGTH];
+ alignas(16) ALfloat NfcSampleData[BUFFERSIZE];
+ };
+ alignas(16) float2 HrtfAccumData[BUFFERSIZE + HRIR_LENGTH];
+
+ /* Mixing buffer used by the Dry mix and Real output. */
+ al::vector<FloatBufferLine, 16> MixBuffer;
+
+ /* The "dry" path corresponds to the main output. */
+ MixParams Dry;
+ ALuint NumChannelsPerOrder[MAX_AMBI_ORDER+1]{};
+
+ /* "Real" output, which will be written to the device buffer. May alias the
+ * dry buffer.
+ */
+ RealMixParams RealOut;
+
+ /* HRTF state and info */
+ std::unique_ptr<DirectHrtfState> mHrtfState;
+ HrtfEntry *mHrtf{nullptr};
+
+ /* Ambisonic-to-UHJ encoder */
+ std::unique_ptr<Uhj2Encoder> Uhj_Encoder;
+
+ /* Ambisonic decoder for speakers */
+ std::unique_ptr<BFormatDec> AmbiDecoder;
+
+ /* Stereo-to-binaural filter */
+ std::unique_ptr<bs2b> Bs2b;
+
+ POSTPROCESS PostProcess{};
+
+ std::unique_ptr<FrontStablizer> Stablizer;
+
+ std::unique_ptr<Compressor> Limiter;
+
+ /* Delay buffers used to compensate for speaker distances. */
+ DistanceComp ChannelDelay;
+
+ /* Dithering control. */
+ ALfloat DitherDepth{0.0f};
+ ALuint DitherSeed{0u};
+
+ /* Running count of the mixer invocations, in 31.1 fixed point. This
+ * actually increments *twice* when mixing, first at the start and then at
+ * the end, so the bottom bit indicates if the device is currently mixing
+ * and the upper bits indicates how many mixes have been done.
+ */
+ RefCount MixCount{0u};
+
+ // Contexts created on this device
+ std::atomic<al::FlexArray<ALCcontext*>*> mContexts{nullptr};
+
+ /* This lock protects the device state (format, update size, etc) from
+ * being from being changed in multiple threads, or being accessed while
+ * being changed. It's also used to serialize calls to the backend.
+ */
+ std::mutex StateLock;
+ std::unique_ptr<BackendBase> Backend;
+
+
+ ALCdevice(DeviceType type);
+ ALCdevice(const ALCdevice&) = delete;
+ ALCdevice& operator=(const ALCdevice&) = delete;
+ ~ALCdevice();
+
+ ALsizei bytesFromFmt() const noexcept { return BytesFromDevFmt(FmtType); }
+ ALsizei channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); }
+ ALsizei frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); }
+
+ DEF_NEWDEL(ALCdevice)
+};
+
+/* Must be less than 15 characters (16 including terminating null) for
+ * compatibility with pthread_setname_np limitations. */
+#define MIXER_THREAD_NAME "alsoft-mixer"
+
+#define RECORD_THREAD_NAME "alsoft-record"
+
+
+enum {
+ /* End event thread processing. */
+ EventType_KillThread = 0,
+
+ /* User event types. */
+ EventType_SourceStateChange = 1<<0,
+ EventType_BufferCompleted = 1<<1,
+ EventType_Error = 1<<2,
+ EventType_Performance = 1<<3,
+ EventType_Deprecated = 1<<4,
+ EventType_Disconnected = 1<<5,
+
+ /* Internal events. */
+ EventType_ReleaseEffectState = 65536,
+};
+
+struct AsyncEvent {
+ unsigned int EnumType{0u};
+ union {
+ char dummy;
+ struct {
+ ALuint id;
+ ALenum state;
+ } srcstate;
+ struct {
+ ALuint id;
+ ALsizei count;
+ } bufcomp;
+ struct {
+ ALenum type;
+ ALuint id;
+ ALuint param;
+ ALchar msg[1008];
+ } user;
+ EffectState *mEffectState;
+ } u{};
+
+ AsyncEvent() noexcept = default;
+ constexpr AsyncEvent(unsigned int type) noexcept : EnumType{type} { }
+};
+
+
+void AllocateVoices(ALCcontext *context, size_t num_voices);
+
+
+extern ALint RTPrioLevel;
+void SetRTPriority(void);
+
+void SetDefaultChannelOrder(ALCdevice *device);
+void SetDefaultWFXChannelOrder(ALCdevice *device);
+
+const ALCchar *DevFmtTypeString(DevFmtType type) noexcept;
+const ALCchar *DevFmtChannelsString(DevFmtChannels chans) noexcept;
+
+/**
+ * GetChannelIdxByName
+ *
+ * Returns the index for the given channel name (e.g. FrontCenter), or -1 if it
+ * doesn't exist.
+ */
+inline ALint GetChannelIdxByName(const RealMixParams &real, Channel chan) noexcept
+{ return real.ChannelIndex[chan]; }
+
+
+void StartEventThrd(ALCcontext *ctx);
+void StopEventThrd(ALCcontext *ctx);
+
+
+al::vector<std::string> SearchDataFiles(const char *match, const char *subdir);
+
+#endif