diff options
Diffstat (limited to 'core/device.h')
-rw-r--r-- | core/device.h | 111 |
1 files changed, 75 insertions, 36 deletions
diff --git a/core/device.h b/core/device.h index b1ffc9ce..1da08727 100644 --- a/core/device.h +++ b/core/device.h @@ -17,6 +17,7 @@ #include "bufferline.h" #include "devformat.h" #include "filters/nfc.h" +#include "flexarray.h" #include "intrusive_ptr.h" #include "mixer/hrtfdefs.h" #include "opthelpers.h" @@ -25,8 +26,10 @@ #include "vector.h" class BFormatDec; +namespace Bs2b { struct bs2b; -struct Compressor; +} // namespace Bs2b +class Compressor; struct ContextBase; struct DirectHrtfState; struct HrtfStore; @@ -34,12 +37,12 @@ struct HrtfStore; using uint = unsigned int; -#define MIN_OUTPUT_RATE 8000 -#define MAX_OUTPUT_RATE 192000 -#define DEFAULT_OUTPUT_RATE 48000 +inline constexpr size_t MinOutputRate{8000}; +inline constexpr size_t MaxOutputRate{192000}; +inline constexpr size_t DefaultOutputRate{48000}; -#define DEFAULT_UPDATE_SIZE 960 /* 20ms */ -#define DEFAULT_NUM_UPDATES 3 +inline constexpr size_t DefaultUpdateSize{960}; /* 20ms */ +inline constexpr size_t DefaultNumUpdates{3}; enum class DeviceType : uint8_t { @@ -82,7 +85,7 @@ struct DistanceComp { float *Buffer{nullptr}; }; - std::array<ChanData,MAX_OUTPUT_CHANNELS> mChannels; + std::array<ChanData,MaxOutputChannels> mChannels; al::FlexArray<float,16> mSamples; DistanceComp(size_t count) : mSamples{count} { } @@ -158,8 +161,6 @@ enum { // Specifies if the DSP is paused at user request DevicePaused, - // Specifies if the device is currently running - DeviceRunning, // Specifies if the output plays directly on/in ears (headphones, headset, // ear buds, etc). @@ -173,12 +174,13 @@ enum { DeviceFlagsCount }; -struct DeviceBase { - /* To avoid extraneous allocations, a 0-sized FlexArray<ContextBase*> is - * defined globally as a sharable object. - */ - static al::FlexArray<ContextBase*> sEmptyContextArray; +enum class DeviceState : uint8_t { + Unprepared, + Configured, + Playing +}; +struct DeviceBase { std::atomic<bool> Connected{true}; const DeviceType Type{}; @@ -202,6 +204,7 @@ struct DeviceBase { // Device flags std::bitset<DeviceFlagsCount> Flags{}; + DeviceState mDeviceState{DeviceState::Unprepared}; uint NumAuxSends{}; @@ -218,8 +221,8 @@ struct DeviceBase { */ NfcFilter mNFCtrlFilter{}; - uint SamplesDone{0u}; - std::chrono::nanoseconds ClockBase{0}; + std::atomic<uint> mSamplesDone{0u}; + std::atomic<std::chrono::nanoseconds> mClockBase{std::chrono::nanoseconds{}}; std::chrono::nanoseconds FixedLatency{0}; AmbiRotateMatrix mAmbiRotateMatrix{}; @@ -229,24 +232,24 @@ struct DeviceBase { static constexpr size_t MixerLineSize{BufferLineSize + DecoderBase::sMaxPadding}; static constexpr size_t MixerChannelsMax{16}; using MixerBufferLine = std::array<float,MixerLineSize>; - alignas(16) std::array<MixerBufferLine,MixerChannelsMax> mSampleData; - alignas(16) std::array<float,MixerLineSize+MaxResamplerPadding> mResampleData; + alignas(16) std::array<MixerBufferLine,MixerChannelsMax> mSampleData{}; + alignas(16) std::array<float,MixerLineSize+MaxResamplerPadding> mResampleData{}; - alignas(16) float FilteredData[BufferLineSize]; + alignas(16) std::array<float,BufferLineSize> FilteredData{}; union { - alignas(16) float HrtfSourceData[BufferLineSize + HrtfHistoryLength]; - alignas(16) float NfcSampleData[BufferLineSize]; + alignas(16) std::array<float,BufferLineSize+HrtfHistoryLength> HrtfSourceData{}; + alignas(16) std::array<float,BufferLineSize> NfcSampleData; }; /* Persistent storage for HRTF mixing. */ - alignas(16) float2 HrtfAccumData[BufferLineSize + HrirLength]; + alignas(16) std::array<float2,BufferLineSize+HrirLength> HrtfAccumData{}; /* 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; - uint NumChannelsPerOrder[MaxAmbiOrder+1]{}; + std::array<uint,MaxAmbiOrder+1> NumChannelsPerOrder{}; /* "Real" output, which will be written to the device buffer. May alias the * dry buffer. @@ -265,7 +268,7 @@ struct DeviceBase { std::unique_ptr<BFormatDec> AmbiDecoder; /* Stereo-to-binaural filter */ - std::unique_ptr<bs2b> Bs2b; + std::unique_ptr<Bs2b::bs2b> Bs2b; using PostProc = void(DeviceBase::*)(const size_t SamplesToDo); PostProc PostProcess{nullptr}; @@ -284,10 +287,10 @@ struct DeviceBase { * 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}; + std::atomic<uint> mMixCount{0u}; // Contexts created on this device - std::atomic<al::FlexArray<ContextBase*>*> mContexts{nullptr}; + al::atomic_unique_ptr<al::FlexArray<ContextBase*>> mContexts; DeviceBase(DeviceType type); @@ -295,18 +298,56 @@ struct DeviceBase { DeviceBase& operator=(const DeviceBase&) = delete; ~DeviceBase(); - uint bytesFromFmt() const noexcept { return BytesFromDevFmt(FmtType); } - uint channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); } - uint frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); } + [[nodiscard]] auto bytesFromFmt() const noexcept -> uint { return BytesFromDevFmt(FmtType); } + [[nodiscard]] auto channelsFromFmt() const noexcept -> uint { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); } + [[nodiscard]] auto frameSizeFromFmt() const noexcept -> uint { return bytesFromFmt() * channelsFromFmt(); } + + struct MixLock { + std::atomic<uint> &mCount; + const uint mLastVal; + + MixLock(std::atomic<uint> &count, const uint last_val) noexcept + : mCount{count}, mLastVal{last_val} + { } + /* Increment the mix count when the lock goes out of scope to "release" + * it (lsb should be 0). + */ + ~MixLock() { mCount.store(mLastVal+2, std::memory_order_release); } + }; + auto getWriteMixLock() noexcept + { + /* Increment the mix count at the start of mixing and writing clock + * info (lsb should be 1). + */ + const auto mixCount = mMixCount.load(std::memory_order_relaxed); + mMixCount.store(mixCount+1, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_release); + return MixLock{mMixCount, mixCount}; + } - uint waitForMix() const noexcept + /** Waits for the mixer to not be mixing or updating the clock. */ + [[nodiscard]] auto waitForMix() const noexcept -> uint { uint refcount; - while((refcount=MixCount.load(std::memory_order_acquire))&1) { + while((refcount=mMixCount.load(std::memory_order_acquire))&1) { } return refcount; } + /** + * Helper to get the current clock time from the device's ClockBase, and + * SamplesDone converted from the sample rate. Should only be called while + * watching the MixCount. + */ + [[nodiscard]] auto getClockTime() const noexcept -> std::chrono::nanoseconds + { + using std::chrono::seconds; + using std::chrono::nanoseconds; + + auto ns = nanoseconds{seconds{mSamplesDone.load(std::memory_order_relaxed)}} / Frequency; + return mClockBase.load(std::memory_order_relaxed) + ns; + } + void ProcessHrtf(const size_t SamplesToDo); void ProcessAmbiDec(const size_t SamplesToDo); void ProcessAmbiDecStablized(const size_t SamplesToDo); @@ -320,8 +361,8 @@ struct DeviceBase { void renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep); /* Caller must lock the device state, and the mixer must not be running. */ -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf,2,3)]] +#ifdef __MINGW32__ + [[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]] #else [[gnu::format(printf,2,3)]] #endif @@ -331,11 +372,9 @@ struct DeviceBase { * Returns the index for the given channel name (e.g. FrontCenter), or * InvalidChannelIndex if it doesn't exist. */ - uint8_t channelIdxByName(Channel chan) const noexcept + [[nodiscard]] auto channelIdxByName(Channel chan) const noexcept -> uint8_t { return RealOut.ChannelIndex[chan]; } - DISABLE_ALLOC() - private: uint renderSamples(const uint numSamples); }; |