diff options
Diffstat (limited to 'alc/alcmain.h')
-rw-r--r-- | alc/alcmain.h | 534 |
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 |