17 #include "bufferline.h"
18 #include "devformat.h"
19 #include "filters/nfc.h"
20 #include "flexarray.h"
21 #include "intrusive_ptr.h"
22 #include "mixer/hrtfdefs.h"
23 #include "opthelpers.h"
24 #include "resampler_limits.h"
25 #include "uhjfilter.h"
34 struct DirectHrtfState
;
37 using uint
= unsigned int;
40 inline constexpr std::size_t MinOutputRate
{8000};
41 inline constexpr std::size_t MaxOutputRate
{192000};
42 inline constexpr std::size_t DefaultOutputRate
{48000};
44 inline constexpr std::size_t DefaultUpdateSize
{960}; /* 20ms */
45 inline constexpr std::size_t DefaultNumUpdates
{3};
48 enum class DeviceType
: std::uint8_t {
55 enum class RenderMode
: std::uint8_t {
61 enum class StereoEncoding
: std::uint8_t {
70 struct InputRemixMap
{
71 struct TargetMix
{ Channel channel
; float mix
; };
74 al::span
<const TargetMix
> targets
;
79 /* Maximum delay in samples for speaker distance compensation. */
80 static constexpr uint MaxDelay
{1024};
83 al::span
<float> Buffer
{}; /* Valid size is [0...MaxDelay). */
87 std::array
<ChanData
,MaxOutputChannels
> mChannels
;
88 al::FlexArray
<float,16> mSamples
;
90 DistanceComp(std::size_t count
) : mSamples
{count
} { }
92 static std::unique_ptr
<DistanceComp
> Create(std::size_t numsamples
)
93 { return std::unique_ptr
<DistanceComp
>{new(FamCount(numsamples
)) DistanceComp
{numsamples
}}; }
95 DEF_FAM_NEWDEL(DistanceComp
, mSamples
)
99 constexpr auto InvalidChannelIndex
= static_cast<std::uint8_t>(~0u);
101 struct BFChannelConfig
{
107 /* Coefficient channel mapping for mixing to the buffer. */
108 std::array
<BFChannelConfig
,MaxAmbiChannels
> AmbiMap
{};
110 al::span
<FloatBufferLine
> Buffer
;
113 * Helper to set an identity/pass-through panning for ambisonic mixing. The
114 * source is expected to be a 3D ACN/N3D ambisonic buffer, and for each
115 * channel [0...count), the given functor is called with the source channel
116 * index, destination channel index, and the gain for that channel. If the
117 * destination channel is InvalidChannelIndex, the given source channel is
118 * not used for output.
121 void setAmbiMixParams(const MixParams
&inmix
, const float gainbase
, F func
) const
123 const std::size_t numIn
{inmix
.Buffer
.size()};
124 const std::size_t numOut
{Buffer
.size()};
125 for(std::size_t i
{0};i
< numIn
;++i
)
127 std::uint8_t idx
{InvalidChannelIndex
};
130 for(std::size_t j
{0};j
< numOut
;++j
)
132 if(AmbiMap
[j
].Index
== inmix
.AmbiMap
[i
].Index
)
134 idx
= static_cast<std::uint8_t>(j
);
135 gain
= AmbiMap
[j
].Scale
* gainbase
;
144 struct RealMixParams
{
145 al::span
<const InputRemixMap
> RemixMap
;
146 std::array
<std::uint8_t,MaxChannels
> ChannelIndex
{};
148 al::span
<FloatBufferLine
> Buffer
;
151 using AmbiRotateMatrix
= std::array
<std::array
<float,MaxAmbiChannels
>,MaxAmbiChannels
>;
154 // Frequency was requested by the app or config file
156 // Channel configuration was requested by the app or config file
158 // Sample type was requested by the config file
161 // Specifies if the DSP is paused at user request
164 // Specifies if the output plays directly on/in ears (headphones, headset,
168 /* Specifies if output is using speaker virtualization (e.g. Windows
176 enum class DeviceState
: std::uint8_t {
183 std::atomic
<bool> Connected
{true};
184 const DeviceType Type
{};
190 DevFmtChannels FmtChans
{};
191 DevFmtType FmtType
{};
193 float mXOverFreq
{400.0f
};
194 /* If the main device mix is horizontal/2D only. */
195 bool m2DMixing
{false};
196 /* For DevFmtAmbi* output only, specifies the channel order and
199 DevAmbiLayout mAmbiLayout
{DevAmbiLayout::Default
};
200 DevAmbiScaling mAmbiScale
{DevAmbiScaling::Default
};
202 std::string DeviceName
;
205 std::bitset
<DeviceFlagsCount
> Flags
{};
206 DeviceState mDeviceState
{DeviceState::Unprepared
};
210 /* Rendering mode. */
211 RenderMode mRenderMode
{RenderMode::Normal
};
213 /* The average speaker distance as determined by the ambdec configuration,
214 * HRTF data set, or the NFC-HOA reference delay. Only used for NFC.
216 float AvgSpeakerDist
{0.0f
};
218 /* The default NFC filter. Not used directly, but is pre-initialized with
219 * the control distance from AvgSpeakerDist.
221 NfcFilter mNFCtrlFilter
{};
223 std::atomic
<uint
> mSamplesDone
{0u};
224 std::atomic
<std::chrono::nanoseconds
> mClockBase
{std::chrono::nanoseconds
{}};
225 std::chrono::nanoseconds FixedLatency
{0};
227 AmbiRotateMatrix mAmbiRotateMatrix
{};
228 AmbiRotateMatrix mAmbiRotateMatrix2
{};
230 /* Temp storage used for mixer processing. */
231 static constexpr std::size_t MixerLineSize
{BufferLineSize
+ DecoderBase::sMaxPadding
};
232 static constexpr std::size_t MixerChannelsMax
{16};
233 alignas(16) std::array
<float,MixerLineSize
*MixerChannelsMax
> mSampleData
{};
234 alignas(16) std::array
<float,MixerLineSize
+MaxResamplerPadding
> mResampleData
{};
236 alignas(16) std::array
<float,BufferLineSize
> FilteredData
{};
237 alignas(16) std::array
<float,BufferLineSize
+HrtfHistoryLength
> ExtraSampleData
{};
239 /* Persistent storage for HRTF mixing. */
240 alignas(16) std::array
<float2
,BufferLineSize
+HrirLength
> HrtfAccumData
{};
242 /* Mixing buffer used by the Dry mix and Real output. */
243 al::vector
<FloatBufferLine
, 16> MixBuffer
;
245 /* The "dry" path corresponds to the main output. */
247 std::array
<uint
,MaxAmbiOrder
+1> NumChannelsPerOrder
{};
249 /* "Real" output, which will be written to the device buffer. May alias the
252 RealMixParams RealOut
;
254 /* HRTF state and info */
255 std::unique_ptr
<DirectHrtfState
> mHrtfState
;
256 al::intrusive_ptr
<HrtfStore
> mHrtf
;
259 /* Ambisonic-to-UHJ encoder */
260 std::unique_ptr
<UhjEncoderBase
> mUhjEncoder
;
262 /* Ambisonic decoder for speakers */
263 std::unique_ptr
<BFormatDec
> AmbiDecoder
;
265 /* Stereo-to-binaural filter */
266 std::unique_ptr
<Bs2b::bs2b
> Bs2b
;
268 using PostProc
= void(DeviceBase::*)(const size_t SamplesToDo
);
269 PostProc PostProcess
{nullptr};
271 std::unique_ptr
<Compressor
> Limiter
;
273 /* Delay buffers used to compensate for speaker distances. */
274 std::unique_ptr
<DistanceComp
> ChannelDelays
;
276 /* Dithering control. */
277 float DitherDepth
{0.0f
};
280 /* Running count of the mixer invocations, in 31.1 fixed point. This
281 * actually increments *twice* when mixing, first at the start and then at
282 * the end, so the bottom bit indicates if the device is currently mixing
283 * and the upper bits indicates how many mixes have been done.
285 std::atomic
<uint
> mMixCount
{0u};
287 // Contexts created on this device
288 al::atomic_unique_ptr
<al::FlexArray
<ContextBase
*>> mContexts
;
291 DeviceBase(DeviceType type
);
292 DeviceBase(const DeviceBase
&) = delete;
293 DeviceBase
& operator=(const DeviceBase
&) = delete;
296 [[nodiscard
]] auto bytesFromFmt() const noexcept
-> uint
{ return BytesFromDevFmt(FmtType
); }
297 [[nodiscard
]] auto channelsFromFmt() const noexcept
-> uint
{ return ChannelsFromDevFmt(FmtChans
, mAmbiOrder
); }
298 [[nodiscard
]] auto frameSizeFromFmt() const noexcept
-> uint
{ return bytesFromFmt() * channelsFromFmt(); }
301 DeviceBase
*const self
;
304 MixLock(DeviceBase
*device
, const uint endval
) noexcept
: self
{device
}, mEndVal
{endval
} { }
305 MixLock(const MixLock
&) = delete;
306 void operator=(const MixLock
&) = delete;
307 /* Update the mix count when the lock goes out of scope to "release" it
310 ~MixLock() { self
->mMixCount
.store(mEndVal
, std::memory_order_release
); }
312 auto getWriteMixLock() noexcept
-> MixLock
314 /* Increment the mix count at the start of mixing and writing clock
315 * info (lsb should be 1).
317 auto mixCount
= mMixCount
.load(std::memory_order_relaxed
);
318 mMixCount
.store(++mixCount
, std::memory_order_release
);
319 return MixLock
{this, ++mixCount
};
322 /** Waits for the mixer to not be mixing or updating the clock. */
323 [[nodiscard
]] auto waitForMix() const noexcept
-> uint
325 uint refcount
{mMixCount
.load(std::memory_order_acquire
)};
326 while((refcount
&1)) refcount
= mMixCount
.load(std::memory_order_acquire
);
331 * Helper to get the current clock time from the device's ClockBase, and
332 * SamplesDone converted from the sample rate. Should only be called while
333 * watching the MixCount.
335 [[nodiscard
]] auto getClockTime() const noexcept
-> std::chrono::nanoseconds
337 using std::chrono::seconds
;
338 using std::chrono::nanoseconds
;
340 auto ns
= nanoseconds
{seconds
{mSamplesDone
.load(std::memory_order_relaxed
)}} / Frequency
;
341 return mClockBase
.load(std::memory_order_relaxed
) + ns
;
344 void ProcessHrtf(const std::size_t SamplesToDo
);
345 void ProcessAmbiDec(const std::size_t SamplesToDo
);
346 void ProcessAmbiDecStablized(const std::size_t SamplesToDo
);
347 void ProcessUhj(const std::size_t SamplesToDo
);
348 void ProcessBs2b(const std::size_t SamplesToDo
);
350 inline void postProcess(const std::size_t SamplesToDo
)
351 { if(PostProcess
) LIKELY (this->*PostProcess
)(SamplesToDo
); }
353 void renderSamples(const al::span
<float*> outBuffers
, const uint numSamples
);
354 void renderSamples(void *outBuffer
, const uint numSamples
, const std::size_t frameStep
);
356 /* Caller must lock the device state, and the mixer must not be running. */
358 [[gnu::format(__MINGW_PRINTF_FORMAT
,2,3)]]
360 [[gnu::format(printf
,2,3)]]
362 void handleDisconnect(const char *msg
, ...);
365 * Returns the index for the given channel name (e.g. FrontCenter), or
366 * InvalidChannelIndex if it doesn't exist.
368 [[nodiscard
]] auto channelIdxByName(Channel chan
) const noexcept
-> std::uint8_t
369 { return RealOut
.ChannelIndex
[chan
]; }
372 uint
renderSamples(const uint numSamples
);
375 /* Must be less than 15 characters (16 including terminating null) for
376 * compatibility with pthread_setname_np limitations. */
377 [[nodiscard
]] constexpr
378 auto GetMixerThreadName() noexcept
-> const char* { return "alsoft-mixer"; }
380 [[nodiscard
]] constexpr
381 auto GetRecordThreadName() noexcept
-> const char* { return "alsoft-record"; }
383 #endif /* CORE_DEVICE_H */