aboutsummaryrefslogtreecommitdiffstats
path: root/core/device.h
diff options
context:
space:
mode:
Diffstat (limited to 'core/device.h')
-rw-r--r--core/device.h111
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);
};