29 #include "inprogext.h"
35 #include "opthelpers.h"
38 template<typename T
, size_t N
>
39 constexpr inline size_t countof(const T(&)[N
]) noexcept
41 #define COUNTOF countof
45 #if defined(__cplusplus)
47 #elif defined(__GNUC__)
48 #define UNUSED(x) UNUSED_##x __attribute__((unused))
49 #elif defined(__LCLINT__)
50 #define UNUSED(x) /*@unused@*/ x
56 /* Calculates the size of a struct with N elements of a flexible array member.
57 * GCC and Clang allow offsetof(Type, fam[N]) for this, but MSVC seems to have
58 * trouble, so a bit more verbose workaround is needed.
60 #define FAM_SIZE(T, M, N) (offsetof(T, M) + sizeof(((T*)NULL)->M[0])*(N))
63 using ALint64
= ALint64SOFT
;
64 using ALuint64
= ALuint64SOFT
;
66 inline constexpr int64_t operator "" _i64(unsigned long long int n
) noexcept
{ return static_cast<int64_t>(n
); }
67 inline constexpr uint64_t operator "" _u64(unsigned long long int n
) noexcept
{ return static_cast<uint64_t>(n
); }
69 /* Define CTZ macros (count trailing zeros), and POPCNT macros (population
70 * count/count 1 bits), for 32- and 64-bit integers. The CTZ macros' results
71 * are *UNDEFINED* if the value is 0.
75 #define POPCNT32 __builtin_popcount
76 #define CTZ32 __builtin_ctz
78 #define POPCNT64 __builtin_popcountl
79 #define CTZ64 __builtin_ctzl
81 #define POPCNT64 __builtin_popcountll
82 #define CTZ64 __builtin_ctzll
85 #elif defined(HAVE_BITSCANFORWARD64_INTRINSIC)
87 inline int msvc64_popcnt32(ALuint v
)
88 { return (int)__popcnt(v
); }
89 #define POPCNT32 msvc64_popcnt32
90 inline int msvc64_ctz32(ALuint v
)
92 unsigned long idx
= 32;
93 _BitScanForward(&idx
, v
);
96 #define CTZ32 msvc64_ctz32
98 inline int msvc64_popcnt64(ALuint64 v
)
99 { return (int)__popcnt64(v
); }
100 #define POPCNT64 msvc64_popcnt64
101 inline int msvc64_ctz64(ALuint64 v
)
103 unsigned long idx
= 64;
104 _BitScanForward64(&idx
, v
);
107 #define CTZ64 msvc64_ctz64
109 #elif defined(HAVE_BITSCANFORWARD_INTRINSIC)
111 inline int msvc_popcnt32(ALuint v
)
112 { return (int)__popcnt(v
); }
113 #define POPCNT32 msvc_popcnt32
114 inline int msvc_ctz32(ALuint v
)
116 unsigned long idx
= 32;
117 _BitScanForward(&idx
, v
);
120 #define CTZ32 msvc_ctz32
122 inline int msvc_popcnt64(ALuint64 v
)
123 { return (int)(__popcnt((ALuint
)v
) + __popcnt((ALuint
)(v
>>32))); }
124 #define POPCNT64 msvc_popcnt64
125 inline int msvc_ctz64(ALuint64 v
)
127 unsigned long idx
= 64;
128 if(!_BitScanForward(&idx
, v
&0xffffffff))
130 if(_BitScanForward(&idx
, v
>>32))
135 #define CTZ64 msvc_ctz64
139 /* There be black magics here. The popcnt method is derived from
140 * https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
141 * while the ctz-utilizing-popcnt algorithm is shown here
142 * http://www.hackersdelight.org/hdcodetxt/ntz.c.txt
143 * as the ntz2 variant. These likely aren't the most efficient methods, but
144 * they're good enough if the GCC or MSVC intrinsics aren't available.
146 inline int fallback_popcnt32(ALuint v
)
148 v
= v
- ((v
>> 1) & 0x55555555u
);
149 v
= (v
& 0x33333333u
) + ((v
>> 2) & 0x33333333u
);
150 v
= (v
+ (v
>> 4)) & 0x0f0f0f0fu
;
151 return (int)((v
* 0x01010101u
) >> 24);
153 #define POPCNT32 fallback_popcnt32
154 inline int fallback_ctz32(ALuint value
)
155 { return fallback_popcnt32(~value
& (value
- 1)); }
156 #define CTZ32 fallback_ctz32
158 inline int fallback_popcnt64(ALuint64 v
)
160 v
= v
- ((v
>> 1) & 0x5555555555555555_u
64);
161 v
= (v
& 0x3333333333333333_u
64) + ((v
>> 2) & 0x3333333333333333_u
64);
162 v
= (v
+ (v
>> 4)) & 0x0f0f0f0f0f0f0f0f_u
64;
163 return (int)((v
* 0x0101010101010101_u
64) >> 56);
165 #define POPCNT64 fallback_popcnt64
166 inline int fallback_ctz64(ALuint64 value
)
167 { return fallback_popcnt64(~value
& (value
- 1)); }
168 #define CTZ64 fallback_ctz64
171 #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
172 #define IS_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
176 ALubyte b
[sizeof(ALuint
)];
177 } EndianTest
= { 1 };
178 #define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1)
184 struct EnumeratedHrtf
;
185 struct DirectHrtfState
;
186 struct FrontStablizer
;
199 #define DEFAULT_UPDATE_SIZE (1024)
200 #define DEFAULT_NUM_UPDATES (3)
201 #define DEFAULT_OUTPUT_RATE (44100)
202 #define MIN_OUTPUT_RATE (8000)
205 /* Find the next power-of-2 for non-power-of-2 numbers. */
206 inline ALuint
NextPowerOf2(ALuint value
) noexcept
220 /** Round up a value to the next multiple. */
221 inline size_t RoundUp(size_t value
, size_t r
) noexcept
224 return value
- (value
%r
);
227 /* Fast float-to-int conversion. No particular rounding mode is assumed; the
228 * IEEE-754 default is round-to-nearest with ties-to-even, though an app could
229 * change it on its own threads. On some systems, a truncating conversion may
230 * always be the fastest method.
232 inline ALint
fastf2i(ALfloat f
) noexcept
234 #if defined(HAVE_INTRIN_H) && ((defined(_M_IX86_FP) && (_M_IX86_FP > 0)) || defined(_M_X64))
235 return _mm_cvt_ss2si(_mm_set1_ps(f
));
237 #elif defined(_MSC_VER) && defined(_M_IX86_FP)
244 #elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
248 __asm__("cvtss2si %1, %0" : "=r"(i
) : "x"(f
));
250 __asm__
__volatile__("fistpl %0" : "=m"(i
) : "t"(f
) : "st");
254 /* On GCC when compiling with -fno-math-errno, lrintf can be inlined to
255 * some simple instructions. Clang does not inline it, always generating a
256 * libc call, while MSVC's implementation is horribly slow, so always fall
257 * back to a normal integer conversion for them.
259 #elif !defined(_MSC_VER) && !defined(__clang__)
269 /* Converts float-to-int using standard behavior (truncation). */
270 inline int float2int(float f
) noexcept
272 #if ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
273 !defined(__SSE_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0)
274 ALint sign
, shift
, mant
;
281 sign
= (conv
.i
>>31) | 1;
282 shift
= ((conv
.i
>>23)&0xff) - (127+23);
285 if(UNLIKELY(shift
>= 31 || shift
< -23))
288 mant
= (conv
.i
&0x7fffff) | 0x800000;
289 if(LIKELY(shift
< 0))
290 return (mant
>> -shift
) * sign
;
291 return (mant
<< shift
) * sign
;
295 return static_cast<ALint
>(f
);
299 /* Rounds a float to the nearest integral value, according to the current
300 * rounding mode. This is essentially an inlined version of rintf, although
301 * makes fewer promises (e.g. -0 or -0.25 rounded to 0 may result in +0).
303 inline float fast_roundf(float f
) noexcept
305 #if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
306 !defined(__SSE_MATH__)
309 __asm__
__volatile__("frndint" : "=t"(out
) : "0"(f
));
314 /* Integral limit, where sub-integral precision is not available for
317 static const float ilim
[2] = {
318 8388608.0f
/* 0x1.0p+23 */,
319 -8388608.0f
/* -0x1.0p+23 */
328 sign
= (conv
.i
>>31)&0x01;
329 expo
= (conv
.i
>>23)&0xff;
331 if(UNLIKELY(expo
>= 150/*+23*/))
333 /* An exponent (base-2) of 23 or higher is incapable of sub-integral
334 * precision, so it's already an integral value. We don't need to worry
335 * about infinity or NaN here.
339 /* Adding the integral limit to the value (with a matching sign) forces a
340 * result that has no sub-integral precision, and is consequently forced to
341 * round to an integral value. Removing the integral limit then restores
342 * the initial value rounded to the integral. The compiler should not
343 * optimize this out because of non-associative rules on floating-point
344 * math (as long as you don't use -fassociative-math,
345 * -funsafe-math-optimizations, -ffast-math, or -Ofast, in which case this
349 return f
- ilim
[sign
];
403 DevFmtByte
= ALC_BYTE_SOFT
,
404 DevFmtUByte
= ALC_UNSIGNED_BYTE_SOFT
,
405 DevFmtShort
= ALC_SHORT_SOFT
,
406 DevFmtUShort
= ALC_UNSIGNED_SHORT_SOFT
,
407 DevFmtInt
= ALC_INT_SOFT
,
408 DevFmtUInt
= ALC_UNSIGNED_INT_SOFT
,
409 DevFmtFloat
= ALC_FLOAT_SOFT
,
411 DevFmtTypeDefault
= DevFmtFloat
413 enum DevFmtChannels
{
414 DevFmtMono
= ALC_MONO_SOFT
,
415 DevFmtStereo
= ALC_STEREO_SOFT
,
416 DevFmtQuad
= ALC_QUAD_SOFT
,
417 DevFmtX51
= ALC_5POINT1_SOFT
,
418 DevFmtX61
= ALC_6POINT1_SOFT
,
419 DevFmtX71
= ALC_7POINT1_SOFT
,
420 DevFmtAmbi3D
= ALC_BFORMAT3D_SOFT
,
422 /* Similar to 5.1, except using rear channels instead of sides */
423 DevFmtX51Rear
= 0x80000000,
425 DevFmtChannelsDefault
= DevFmtStereo
427 #define MAX_OUTPUT_CHANNELS (16)
429 /* DevFmtType traits, providing the type, etc given a DevFmtType. */
430 template<DevFmtType T
>
431 struct DevFmtTypeTraits
{ };
434 struct DevFmtTypeTraits
<DevFmtByte
> { using Type
= ALbyte
; };
436 struct DevFmtTypeTraits
<DevFmtUByte
> { using Type
= ALubyte
; };
438 struct DevFmtTypeTraits
<DevFmtShort
> { using Type
= ALshort
; };
440 struct DevFmtTypeTraits
<DevFmtUShort
> { using Type
= ALushort
; };
442 struct DevFmtTypeTraits
<DevFmtInt
> { using Type
= ALint
; };
444 struct DevFmtTypeTraits
<DevFmtUInt
> { using Type
= ALuint
; };
446 struct DevFmtTypeTraits
<DevFmtFloat
> { using Type
= ALfloat
; };
449 ALsizei
BytesFromDevFmt(DevFmtType type
) noexcept
;
450 ALsizei
ChannelsFromDevFmt(DevFmtChannels chans
, ALsizei ambiorder
) noexcept
;
451 inline ALsizei
FrameSizeFromDevFmt(DevFmtChannels chans
, DevFmtType type
, ALsizei ambiorder
) noexcept
452 { return ChannelsFromDevFmt(chans
, ambiorder
) * BytesFromDevFmt(type
); }
454 enum class AmbiLayout
{
455 FuMa
= ALC_FUMA_SOFT
, /* FuMa channel order */
456 ACN
= ALC_ACN_SOFT
, /* ACN channel order */
461 enum class AmbiNorm
{
462 FuMa
= ALC_FUMA_SOFT
, /* FuMa normalization */
463 SN3D
= ALC_SN3D_SOFT
, /* SN3D normalization */
464 N3D
= ALC_N3D_SOFT
, /* N3D normalization */
484 struct BufferSubList
{
485 uint64_t FreeMask
{~uint64_t{}};
486 ALbuffer
*Buffers
{nullptr}; /* 64 */
488 BufferSubList() noexcept
= default;
489 BufferSubList(const BufferSubList
&) = delete;
490 BufferSubList(BufferSubList
&& rhs
) noexcept
: FreeMask
{rhs
.FreeMask
}, Buffers
{rhs
.Buffers
}
491 { rhs
.FreeMask
= ~uint64_t{}; rhs
.Buffers
= nullptr; }
494 BufferSubList
& operator=(const BufferSubList
&) = delete;
495 BufferSubList
& operator=(BufferSubList
&& rhs
) noexcept
496 { std::swap(FreeMask
, rhs
.FreeMask
); std::swap(Buffers
, rhs
.Buffers
); return *this; }
499 struct EffectSubList
{
500 uint64_t FreeMask
{~uint64_t{}};
501 ALeffect
*Effects
{nullptr}; /* 64 */
503 EffectSubList() noexcept
= default;
504 EffectSubList(const EffectSubList
&) = delete;
505 EffectSubList(EffectSubList
&& rhs
) noexcept
: FreeMask
{rhs
.FreeMask
}, Effects
{rhs
.Effects
}
506 { rhs
.FreeMask
= ~uint64_t{}; rhs
.Effects
= nullptr; }
509 EffectSubList
& operator=(const EffectSubList
&) = delete;
510 EffectSubList
& operator=(EffectSubList
&& rhs
) noexcept
511 { std::swap(FreeMask
, rhs
.FreeMask
); std::swap(Effects
, rhs
.Effects
); return *this; }
514 struct FilterSubList
{
515 uint64_t FreeMask
{~uint64_t{}};
516 ALfilter
*Filters
{nullptr}; /* 64 */
518 FilterSubList() noexcept
= default;
519 FilterSubList(const FilterSubList
&) = delete;
520 FilterSubList(FilterSubList
&& rhs
) noexcept
: FreeMask
{rhs
.FreeMask
}, Filters
{rhs
.Filters
}
521 { rhs
.FreeMask
= ~uint64_t{}; rhs
.Filters
= nullptr; }
524 FilterSubList
& operator=(const FilterSubList
&) = delete;
525 FilterSubList
& operator=(FilterSubList
&& rhs
) noexcept
526 { std::swap(FreeMask
, rhs
.FreeMask
); std::swap(Filters
, rhs
.Filters
); return *this; }
530 /* Maximum delay in samples for speaker distance compensation. */
531 #define MAX_DELAY_LENGTH 1024
537 ALsizei Length
{0}; /* Valid range is [0...MAX_DELAY_LENGTH). */
538 ALfloat
*Buffer
{nullptr};
542 DistData mChannel
[MAX_OUTPUT_CHANNELS
];
543 al::vector
<ALfloat
,16> mSamples
;
546 void resize(size_t new_size
) { mSamples
.resize(new_size
); }
547 void shrink_to_fit() { mSamples
.shrink_to_fit(); }
548 void clear() noexcept
550 for(auto &chan
: mChannel
)
554 chan
.Buffer
= nullptr;
559 DistData
*begin() noexcept
{ return std::begin(mChannel
); }
560 const DistData
*begin() const noexcept
{ return std::begin(mChannel
); }
561 const DistData
*cbegin() const noexcept
{ return std::begin(mChannel
); }
562 DistData
*end() noexcept
{ return std::end(mChannel
); }
563 const DistData
*end() const noexcept
{ return std::end(mChannel
); }
564 const DistData
*cend() const noexcept
{ return std::end(mChannel
); }
566 ALfloat
*data() noexcept
{ return mSamples
.data(); }
567 const ALfloat
*data() const noexcept
{ return mSamples
.data(); }
569 DistData
& operator[](size_t o
) noexcept
{ return mChannel
[o
]; }
570 const DistData
& operator[](size_t o
) const noexcept
{ return mChannel
[o
]; }
573 struct BFChannelConfig
{
578 /* Size for temporary storage of buffer data, in ALfloats. Larger values need
579 * more memory, while smaller values may need more iterations. The value needs
580 * to be a sensible size, however, as it constrains the max stepping value used
581 * for mixing, as well as the maximum number of samples per mixing iteration.
583 #define BUFFERSIZE 2048
586 /* Coefficient channel mapping for mixing to the buffer. */
587 std::array
<BFChannelConfig
,MAX_OUTPUT_CHANNELS
> AmbiMap
;
589 ALfloat (*Buffer
)[BUFFERSIZE
]{nullptr};
590 ALsizei NumChannels
{0};
593 struct RealMixParams
{
594 Channel ChannelName
[MAX_OUTPUT_CHANNELS
]{};
596 ALfloat (*Buffer
)[BUFFERSIZE
]{nullptr};
597 ALsizei NumChannels
{0};
600 using POSTPROCESS
= void(*)(ALCdevice
*device
, ALsizei SamplesToDo
);
605 std::atomic
<bool> Connected
{true};
606 const DeviceType Type
{};
611 DevFmtChannels FmtChans
{};
612 DevFmtType FmtType
{};
613 ALboolean IsHeadphones
{AL_FALSE
};
614 ALsizei mAmbiOrder
{0};
615 /* For DevFmtAmbi* output only, specifies the channel order and
618 AmbiLayout mAmbiLayout
{AmbiLayout::Default
};
619 AmbiNorm mAmbiScale
{AmbiNorm::Default
};
621 ALCenum LimiterState
{ALC_DONT_CARE_SOFT
};
623 std::string DeviceName
;
628 std::string HrtfName
;
629 al::vector
<EnumeratedHrtf
> HrtfList
;
630 ALCenum HrtfStatus
{ALC_FALSE
};
632 std::atomic
<ALCenum
> LastError
{ALC_NO_ERROR
};
634 // Maximum number of sources that can be created
636 // Maximum number of slots that can be created
637 ALuint AuxiliaryEffectSlotMax
{};
639 ALCuint NumMonoSources
{};
640 ALCuint NumStereoSources
{};
641 ALsizei NumAuxSends
{};
643 // Map of Buffers for this device
644 std::mutex BufferLock
;
645 al::vector
<BufferSubList
> BufferList
;
647 // Map of Effects for this device
648 std::mutex EffectLock
;
649 al::vector
<EffectSubList
> EffectList
;
651 // Map of Filters for this device
652 std::mutex FilterLock
;
653 al::vector
<FilterSubList
> FilterList
;
655 /* Rendering mode. */
656 RenderMode mRenderMode
{NormalRender
};
658 /* The average speaker distance as determined by the ambdec configuration
659 * (or alternatively, by the NFC-HOA reference delay). Only used for NFC.
661 ALfloat AvgSpeakerDist
{0.0f
};
663 ALuint SamplesDone
{0u};
664 std::chrono::nanoseconds ClockBase
{0};
665 std::chrono::nanoseconds FixedLatency
{0};
667 /* Temp storage used for mixer processing. */
668 alignas(16) ALfloat TempBuffer
[4][BUFFERSIZE
];
670 /* Mixing buffer used by the Dry mix, FOAOut, and Real out. */
671 al::vector
<std::array
<ALfloat
,BUFFERSIZE
>, 16> MixBuffer
;
673 /* The "dry" path corresponds to the main output. */
675 ALsizei NumChannelsPerOrder
[MAX_AMBI_ORDER
+1]{};
677 /* First-order ambisonics output, to be upsampled to the dry buffer if different. */
680 /* "Real" output, which will be written to the device buffer. May alias the
683 RealMixParams RealOut
;
685 /* HRTF state and info */
686 std::unique_ptr
<DirectHrtfState
> mHrtfState
;
687 HrtfEntry
*mHrtf
{nullptr};
689 /* UHJ encoder state */
690 std::unique_ptr
<Uhj2Encoder
> Uhj_Encoder
;
692 /* High quality Ambisonic decoder */
693 std::unique_ptr
<BFormatDec
> AmbiDecoder
;
695 /* Stereo-to-binaural filter */
696 std::unique_ptr
<bs2b
> Bs2b
;
698 /* First-order ambisonic upsampler for higher-order output */
699 std::unique_ptr
<AmbiUpsampler
> AmbiUp
;
701 POSTPROCESS PostProcess
{};
703 std::unique_ptr
<FrontStablizer
> Stablizer
;
705 std::unique_ptr
<Compressor
> Limiter
;
707 /* Delay buffers used to compensate for speaker distances. */
708 DistanceComp ChannelDelay
;
710 /* Dithering control. */
711 ALfloat DitherDepth
{0.0f
};
712 ALuint DitherSeed
{0u};
714 /* Running count of the mixer invocations, in 31.1 fixed point. This
715 * actually increments *twice* when mixing, first at the start and then at
716 * the end, so the bottom bit indicates if the device is currently mixing
717 * and the upper bits indicates how many mixes have been done.
719 RefCount MixCount
{0u};
721 // Contexts created on this device
722 std::atomic
<ALCcontext
*> ContextList
{nullptr};
724 /* This lock protects the device state (format, update size, etc) from
725 * being from being changed in multiple threads, or being accessed while
726 * being changed. It's also used to serialize calls to the backend.
728 std::mutex StateLock
;
729 std::unique_ptr
<BackendBase
> Backend
;
731 std::atomic
<ALCdevice
*> next
{nullptr};
734 ALCdevice(DeviceType type
);
735 ALCdevice(const ALCdevice
&) = delete;
736 ALCdevice
& operator=(const ALCdevice
&) = delete;
739 ALsizei
bytesFromFmt() const noexcept
{ return BytesFromDevFmt(FmtType
); }
740 ALsizei
channelsFromFmt() const noexcept
{ return ChannelsFromDevFmt(FmtChans
, mAmbiOrder
); }
741 ALsizei
frameSizeFromFmt() const noexcept
{ return bytesFromFmt() * channelsFromFmt(); }
743 static constexpr inline const char *CurrentPrefix() noexcept
{ return "ALCdevice::"; }
744 DEF_NEWDEL(ALCdevice
)
747 // Frequency was requested by the app or config file
748 #define DEVICE_FREQUENCY_REQUEST (1u<<1)
749 // Channel configuration was requested by the config file
750 #define DEVICE_CHANNELS_REQUEST (1u<<2)
751 // Sample type was requested by the config file
752 #define DEVICE_SAMPLE_TYPE_REQUEST (1u<<3)
754 // Specifies if the DSP is paused at user request
755 #define DEVICE_PAUSED (1u<<30)
757 // Specifies if the device is currently running
758 #define DEVICE_RUNNING (1u<<31)
761 /* Must be less than 15 characters (16 including terminating null) for
762 * compatibility with pthread_setname_np limitations. */
763 #define MIXER_THREAD_NAME "alsoft-mixer"
765 #define RECORD_THREAD_NAME "alsoft-record"
769 /* End event thread processing. */
770 EventType_KillThread
= 0,
772 /* User event types. */
773 EventType_SourceStateChange
= 1<<0,
774 EventType_BufferCompleted
= 1<<1,
775 EventType_Error
= 1<<2,
776 EventType_Performance
= 1<<3,
777 EventType_Deprecated
= 1<<4,
778 EventType_Disconnected
= 1<<5,
780 /* Internal events. */
781 EventType_ReleaseEffectState
= 65536,
785 unsigned int EnumType
{0u};
802 EffectState
*mEffectState
;
805 AsyncEvent() noexcept
= default;
806 constexpr AsyncEvent(unsigned int type
) noexcept
: EnumType
{type
} { }
810 void AllocateVoices(ALCcontext
*context
, ALsizei num_voices
, ALsizei old_sends
);
813 extern ALint RTPrioLevel
;
814 void SetRTPriority(void);
816 void SetDefaultChannelOrder(ALCdevice
*device
);
817 void SetDefaultWFXChannelOrder(ALCdevice
*device
);
819 const ALCchar
*DevFmtTypeString(DevFmtType type
) noexcept
;
820 const ALCchar
*DevFmtChannelsString(DevFmtChannels chans
) noexcept
;
822 inline ALint
GetChannelIndex(const Channel (&names
)[MAX_OUTPUT_CHANNELS
], Channel chan
)
824 auto iter
= std::find(std::begin(names
), std::end(names
), chan
);
825 if(iter
== std::end(names
)) return -1;
826 return static_cast<ALint
>(std::distance(std::begin(names
), iter
));
829 * GetChannelIdxByName
831 * Returns the index for the given channel name (e.g. FrontCenter), or -1 if it
834 inline ALint
GetChannelIdxByName(const RealMixParams
&real
, Channel chan
)
835 { return GetChannelIndex(real
.ChannelName
, chan
); }
838 void StartEventThrd(ALCcontext
*ctx
);
839 void StopEventThrd(ALCcontext
*ctx
);
842 al::vector
<std::string
> SearchDataFiles(const char *match
, const char *subdir
);