]> git.tdb.fi Git - ext/openal.git/blob - core/device.h
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / core / device.h
1 #ifndef CORE_DEVICE_H
2 #define CORE_DEVICE_H
3
4 #include <stddef.h>
5
6 #include <array>
7 #include <atomic>
8 #include <bitset>
9 #include <chrono>
10 #include <memory>
11 #include <mutex>
12 #include <string>
13
14 #include "almalloc.h"
15 #include "alspan.h"
16 #include "ambidefs.h"
17 #include "atomic.h"
18 #include "bufferline.h"
19 #include "devformat.h"
20 #include "filters/nfc.h"
21 #include "intrusive_ptr.h"
22 #include "mixer/hrtfdefs.h"
23 #include "opthelpers.h"
24 #include "resampler_limits.h"
25 #include "uhjfilter.h"
26 #include "vector.h"
27
28 class BFormatDec;
29 struct bs2b;
30 struct Compressor;
31 struct ContextBase;
32 struct DirectHrtfState;
33 struct HrtfStore;
34
35 using uint = unsigned int;
36
37
38 #define MIN_OUTPUT_RATE      8000
39 #define MAX_OUTPUT_RATE      192000
40 #define DEFAULT_OUTPUT_RATE  48000
41
42 #define DEFAULT_UPDATE_SIZE  960 /* 20ms */
43 #define DEFAULT_NUM_UPDATES  3
44
45
46 enum class DeviceType : unsigned char {
47     Playback,
48     Capture,
49     Loopback
50 };
51
52
53 enum class RenderMode : unsigned char {
54     Normal,
55     Pairwise,
56     Hrtf
57 };
58
59 enum class StereoEncoding : unsigned char {
60     Basic,
61     Uhj,
62     Hrtf,
63
64     Default = Basic
65 };
66
67
68 struct InputRemixMap {
69     struct TargetMix { Channel channel; float mix; };
70
71     Channel channel;
72     al::span<const TargetMix> targets;
73 };
74
75
76 struct DistanceComp {
77     /* Maximum delay in samples for speaker distance compensation. */
78     static constexpr uint MaxDelay{1024};
79
80     struct ChanData {
81         float Gain{1.0f};
82         uint Length{0u}; /* Valid range is [0...MaxDelay). */
83         float *Buffer{nullptr};
84     };
85
86     std::array<ChanData,MAX_OUTPUT_CHANNELS> mChannels;
87     al::FlexArray<float,16> mSamples;
88
89     DistanceComp(size_t count) : mSamples{count} { }
90
91     static std::unique_ptr<DistanceComp> Create(size_t numsamples)
92     { return std::unique_ptr<DistanceComp>{new(FamCount(numsamples)) DistanceComp{numsamples}}; }
93
94     DEF_FAM_NEWDEL(DistanceComp, mSamples)
95 };
96
97
98 constexpr uint InvalidChannelIndex{~0u};
99
100 struct BFChannelConfig {
101     float Scale;
102     uint Index;
103 };
104
105 struct MixParams {
106     /* Coefficient channel mapping for mixing to the buffer. */
107     std::array<BFChannelConfig,MaxAmbiChannels> AmbiMap{};
108
109     al::span<FloatBufferLine> Buffer;
110
111     /**
112      * Helper to set an identity/pass-through panning for ambisonic mixing. The
113      * source is expected to be a 3D ACN/N3D ambisonic buffer, and for each
114      * channel [0...count), the given functor is called with the source channel
115      * index, destination channel index, and the gain for that channel. If the
116      * destination channel is INVALID_CHANNEL_INDEX, the given source channel
117      * is not used for output.
118      */
119     template<typename F>
120     void setAmbiMixParams(const MixParams &inmix, const float gainbase, F func) const
121     {
122         const size_t numIn{inmix.Buffer.size()};
123         const size_t numOut{Buffer.size()};
124         for(size_t i{0};i < numIn;++i)
125         {
126             auto idx = InvalidChannelIndex;
127             auto gain = 0.0f;
128
129             for(size_t j{0};j < numOut;++j)
130             {
131                 if(AmbiMap[j].Index == inmix.AmbiMap[i].Index)
132                 {
133                     idx = static_cast<uint>(j);
134                     gain = AmbiMap[j].Scale * gainbase;
135                     break;
136                 }
137             }
138             func(i, idx, gain);
139         }
140     }
141 };
142
143 struct RealMixParams {
144     al::span<const InputRemixMap> RemixMap;
145     std::array<uint,MaxChannels> ChannelIndex{};
146
147     al::span<FloatBufferLine> Buffer;
148 };
149
150 using AmbiRotateMatrix = std::array<std::array<float,MaxAmbiChannels>,MaxAmbiChannels>;
151
152 enum {
153     // Frequency was requested by the app or config file
154     FrequencyRequest,
155     // Channel configuration was requested by the app or config file
156     ChannelsRequest,
157     // Sample type was requested by the config file
158     SampleTypeRequest,
159
160     // Specifies if the DSP is paused at user request
161     DevicePaused,
162     // Specifies if the device is currently running
163     DeviceRunning,
164
165     // Specifies if the output plays directly on/in ears (headphones, headset,
166     // ear buds, etc).
167     DirectEar,
168
169     DeviceFlagsCount
170 };
171
172 struct DeviceBase {
173     /* To avoid extraneous allocations, a 0-sized FlexArray<ContextBase*> is
174      * defined globally as a sharable object.
175      */
176     static al::FlexArray<ContextBase*> sEmptyContextArray;
177
178     std::atomic<bool> Connected{true};
179     const DeviceType Type{};
180
181     uint Frequency{};
182     uint UpdateSize{};
183     uint BufferSize{};
184
185     DevFmtChannels FmtChans{};
186     DevFmtType FmtType{};
187     uint mAmbiOrder{0};
188     float mXOverFreq{400.0f};
189     /* If the main device mix is horizontal/2D only. */
190     bool m2DMixing{false};
191     /* For DevFmtAmbi* output only, specifies the channel order and
192      * normalization.
193      */
194     DevAmbiLayout mAmbiLayout{DevAmbiLayout::Default};
195     DevAmbiScaling mAmbiScale{DevAmbiScaling::Default};
196
197     std::string DeviceName;
198
199     // Device flags
200     std::bitset<DeviceFlagsCount> Flags{};
201
202     uint NumAuxSends{};
203
204     /* Rendering mode. */
205     RenderMode mRenderMode{RenderMode::Normal};
206
207     /* The average speaker distance as determined by the ambdec configuration,
208      * HRTF data set, or the NFC-HOA reference delay. Only used for NFC.
209      */
210     float AvgSpeakerDist{0.0f};
211
212     /* The default NFC filter. Not used directly, but is pre-initialized with
213      * the control distance from AvgSpeakerDist.
214      */
215     NfcFilter mNFCtrlFilter{};
216
217     uint SamplesDone{0u};
218     std::chrono::nanoseconds ClockBase{0};
219     std::chrono::nanoseconds FixedLatency{0};
220
221     AmbiRotateMatrix mAmbiRotateMatrix{};
222     AmbiRotateMatrix mAmbiRotateMatrix2{};
223
224     /* Temp storage used for mixer processing. */
225     static constexpr size_t MixerLineSize{BufferLineSize + DecoderBase::sMaxPadding};
226     static constexpr size_t MixerChannelsMax{16};
227     using MixerBufferLine = std::array<float,MixerLineSize>;
228     alignas(16) std::array<MixerBufferLine,MixerChannelsMax> mSampleData;
229     alignas(16) std::array<float,MixerLineSize+MaxResamplerPadding> mResampleData;
230
231     alignas(16) float FilteredData[BufferLineSize];
232     union {
233         alignas(16) float HrtfSourceData[BufferLineSize + HrtfHistoryLength];
234         alignas(16) float NfcSampleData[BufferLineSize];
235     };
236
237     /* Persistent storage for HRTF mixing. */
238     alignas(16) float2 HrtfAccumData[BufferLineSize + HrirLength];
239
240     /* Mixing buffer used by the Dry mix and Real output. */
241     al::vector<FloatBufferLine, 16> MixBuffer;
242
243     /* The "dry" path corresponds to the main output. */
244     MixParams Dry;
245     uint NumChannelsPerOrder[MaxAmbiOrder+1]{};
246
247     /* "Real" output, which will be written to the device buffer. May alias the
248      * dry buffer.
249      */
250     RealMixParams RealOut;
251
252     /* HRTF state and info */
253     std::unique_ptr<DirectHrtfState> mHrtfState;
254     al::intrusive_ptr<HrtfStore> mHrtf;
255     uint mIrSize{0};
256
257     /* Ambisonic-to-UHJ encoder */
258     std::unique_ptr<UhjEncoderBase> mUhjEncoder;
259
260     /* Ambisonic decoder for speakers */
261     std::unique_ptr<BFormatDec> AmbiDecoder;
262
263     /* Stereo-to-binaural filter */
264     std::unique_ptr<bs2b> Bs2b;
265
266     using PostProc = void(DeviceBase::*)(const size_t SamplesToDo);
267     PostProc PostProcess{nullptr};
268
269     std::unique_ptr<Compressor> Limiter;
270
271     /* Delay buffers used to compensate for speaker distances. */
272     std::unique_ptr<DistanceComp> ChannelDelays;
273
274     /* Dithering control. */
275     float DitherDepth{0.0f};
276     uint DitherSeed{0u};
277
278     /* Running count of the mixer invocations, in 31.1 fixed point. This
279      * actually increments *twice* when mixing, first at the start and then at
280      * the end, so the bottom bit indicates if the device is currently mixing
281      * and the upper bits indicates how many mixes have been done.
282      */
283     RefCount MixCount{0u};
284
285     // Contexts created on this device
286     std::atomic<al::FlexArray<ContextBase*>*> mContexts{nullptr};
287
288
289     DeviceBase(DeviceType type);
290     DeviceBase(const DeviceBase&) = delete;
291     DeviceBase& operator=(const DeviceBase&) = delete;
292     ~DeviceBase();
293
294     uint bytesFromFmt() const noexcept { return BytesFromDevFmt(FmtType); }
295     uint channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); }
296     uint frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); }
297
298     uint waitForMix() const noexcept
299     {
300         uint refcount;
301         while((refcount=MixCount.load(std::memory_order_acquire))&1) {
302         }
303         return refcount;
304     }
305
306     void ProcessHrtf(const size_t SamplesToDo);
307     void ProcessAmbiDec(const size_t SamplesToDo);
308     void ProcessAmbiDecStablized(const size_t SamplesToDo);
309     void ProcessUhj(const size_t SamplesToDo);
310     void ProcessBs2b(const size_t SamplesToDo);
311
312     inline void postProcess(const size_t SamplesToDo)
313     { if(PostProcess) LIKELY (this->*PostProcess)(SamplesToDo); }
314
315     void renderSamples(const al::span<float*> outBuffers, const uint numSamples);
316     void renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep);
317
318     /* Caller must lock the device state, and the mixer must not be running. */
319 #ifdef __USE_MINGW_ANSI_STDIO
320     [[gnu::format(gnu_printf,2,3)]]
321 #else
322     [[gnu::format(printf,2,3)]]
323 #endif
324     void handleDisconnect(const char *msg, ...);
325
326     /**
327      * Returns the index for the given channel name (e.g. FrontCenter), or
328      * INVALID_CHANNEL_INDEX if it doesn't exist.
329      */
330     uint channelIdxByName(Channel chan) const noexcept
331     { return RealOut.ChannelIndex[chan]; }
332
333     DISABLE_ALLOC()
334
335 private:
336     uint renderSamples(const uint numSamples);
337 };
338
339 /* Must be less than 15 characters (16 including terminating null) for
340  * compatibility with pthread_setname_np limitations. */
341 #define MIXER_THREAD_NAME "alsoft-mixer"
342
343 #define RECORD_THREAD_NAME "alsoft-record"
344
345 #endif /* CORE_DEVICE_H */