aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2022-08-07 13:09:12 -0700
committerChris Robinson <[email protected]>2022-08-07 13:09:12 -0700
commit250f1624964c2c53e00d18fd1ec2bbc77c860298 (patch)
treec126c955a8871031d5da6d3c79fa4e7d380cc3e1 /core
parentb77a556d7b7d1a66db4dcde0b77ea5d704254c13 (diff)
Parameterize the UHJ filter length
Diffstat (limited to 'core')
-rw-r--r--core/device.h4
-rw-r--r--core/uhjfilter.cpp37
-rw-r--r--core/uhjfilter.h72
-rw-r--r--core/voice.cpp20
-rw-r--r--core/voice.h2
5 files changed, 98 insertions, 37 deletions
diff --git a/core/device.h b/core/device.h
index e52d015f..bf995e5b 100644
--- a/core/device.h
+++ b/core/device.h
@@ -187,7 +187,7 @@ struct DeviceBase {
/* Temp storage used for mixer processing. */
static constexpr size_t MixerLineSize{BufferLineSize + MaxResamplerPadding +
- UhjDecoder::sFilterDelay};
+ DecoderBase::sMaxDelay};
static constexpr size_t MixerChannelsMax{16};
using MixerBufferLine = std::array<float,MixerLineSize>;
alignas(16) std::array<MixerBufferLine,MixerChannelsMax> mSampleData;
@@ -220,7 +220,7 @@ struct DeviceBase {
uint mIrSize{0};
/* Ambisonic-to-UHJ encoder */
- std::unique_ptr<UhjEncoder> mUhjEncoder;
+ std::unique_ptr<UhjEncoderBase> mUhjEncoder;
/* Ambisonic decoder for speakers */
std::unique_ptr<BFormatDec> AmbiDecoder;
diff --git a/core/uhjfilter.cpp b/core/uhjfilter.cpp
index 52e7f964..b327a627 100644
--- a/core/uhjfilter.cpp
+++ b/core/uhjfilter.cpp
@@ -14,7 +14,15 @@
namespace {
-const PhaseShifterT<UhjFilterBase::sFilterDelay*2> PShift{};
+const PhaseShifterT<UhjLengthLq> PShiftLq{};
+const PhaseShifterT<UhjLengthHq> PShiftHq{};
+
+template<size_t N>
+struct GetPhaseShifter;
+template<>
+struct GetPhaseShifter<UhjLengthLq> { static auto& Get() noexcept { return PShiftLq; } };
+template<>
+struct GetPhaseShifter<UhjLengthHq> { static auto& Get() noexcept { return PShiftHq; } };
} // namespace
@@ -36,9 +44,12 @@ const PhaseShifterT<UhjFilterBase::sFilterDelay*2> PShift{};
* impulse with the desired shift.
*/
-void UhjEncoder::encode(float *LeftOut, float *RightOut,
+template<size_t N>
+void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
const al::span<const float*const,3> InSamples, const size_t SamplesToDo)
{
+ const auto &PShift = GetPhaseShifter<N>::Get();
+
ASSUME(SamplesToDo > 0);
float *RESTRICT left{al::assume_aligned<16>(LeftOut)};
@@ -101,9 +112,14 @@ void UhjEncoder::encode(float *LeftOut, float *RightOut,
* where j is a +90 degree phase shift. 3-channel UHJ excludes Q, while 2-
* channel excludes Q and T.
*/
-void UhjDecoder::decode(const al::span<float*> samples, const size_t samplesToDo,
+template<size_t N>
+void UhjDecoder<N>::decode(const al::span<float*> samples, const size_t samplesToDo,
const size_t forwardSamples)
{
+ static_assert(sFilterDelay <= sMaxDelay, "Filter delay is too large");
+
+ const auto &PShift = GetPhaseShifter<N>::Get();
+
ASSUME(samplesToDo > 0);
{
@@ -174,9 +190,14 @@ void UhjDecoder::decode(const al::span<float*> samples, const size_t samplesToDo
* where j is a +90 degree phase shift. w is a variable control for the
* resulting stereo width, with the range 0 <= w <= 0.7.
*/
-void UhjStereoDecoder::decode(const al::span<float*> samples, const size_t samplesToDo,
+template<size_t N>
+void UhjStereoDecoder<N>::decode(const al::span<float*> samples, const size_t samplesToDo,
const size_t forwardSamples)
{
+ static_assert(sFilterDelay <= sMaxDelay, "Filter delay is too large");
+
+ const auto &PShift = GetPhaseShifter<N>::Get();
+
ASSUME(samplesToDo > 0);
{
@@ -240,3 +261,11 @@ void UhjStereoDecoder::decode(const al::span<float*> samples, const size_t sampl
for(size_t i{0};i < samplesToDo;++i)
youtput[i] = 1.6822415f*mD[i] - 0.2156194f*youtput[i];
}
+
+template struct UhjEncoder<UhjLengthLq>;
+template struct UhjDecoder<UhjLengthLq>;
+template struct UhjStereoDecoder<UhjLengthLq>;
+
+template struct UhjEncoder<UhjLengthHq>;
+template struct UhjDecoder<UhjLengthHq>;
+template struct UhjStereoDecoder<UhjLengthHq>;
diff --git a/core/uhjfilter.h b/core/uhjfilter.h
index eeabb6d2..c14ed5bf 100644
--- a/core/uhjfilter.h
+++ b/core/uhjfilter.h
@@ -9,30 +9,29 @@
#include "resampler_limits.h"
-struct DecoderBase {
- virtual ~DecoderBase() = default;
+static constexpr size_t UhjLengthLq{256};
+static constexpr size_t UhjLengthHq{512};
+static constexpr size_t UhjLengthStd{UhjLengthLq};
- virtual void decode(const al::span<float*> samples, const size_t samplesToDo,
- const size_t forwardSamples) = 0;
- /**
- * The width factor for Super Stereo processing. Can be changed in between
- * calls to decode, with valid values being between 0...0.7.
- */
- float mWidthControl{0.593f};
-
- float mCurrentWidth{-1.0f};
-};
+struct UhjEncoderBase {
+ virtual ~UhjEncoderBase() = default;
+ virtual size_t getDelay() noexcept = 0;
-struct UhjFilterBase {
- /* The filter delay is half it's effective size, so a delay of 128 has a
- * FIR length of 256.
+ /**
+ * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
+ * signal. The input must use FuMa channel ordering and UHJ scaling (FuMa
+ * with an additional +3dB boost).
*/
- static constexpr size_t sFilterDelay{128};
+ virtual void encode(float *LeftOut, float *RightOut,
+ const al::span<const float*const,3> InSamples, const size_t SamplesToDo) = 0;
};
-struct UhjEncoder : public UhjFilterBase {
+template<size_t N>
+struct UhjEncoder final : public UhjEncoderBase {
+ static constexpr size_t sFilterDelay{N/2};
+
/* Delays and processing storage for the unfiltered signal. */
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mS{};
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mD{};
@@ -42,19 +41,41 @@ struct UhjEncoder : public UhjFilterBase {
alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mTemp{};
+ size_t getDelay() noexcept override { return sFilterDelay; }
+
/**
* Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
* signal. The input must use FuMa channel ordering and UHJ scaling (FuMa
* with an additional +3dB boost).
*/
void encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples,
- const size_t SamplesToDo);
+ const size_t SamplesToDo) override;
DEF_NEWDEL(UhjEncoder)
};
-struct UhjDecoder : public DecoderBase, public UhjFilterBase {
+struct DecoderBase {
+ static constexpr size_t sMaxDelay{256};
+
+ virtual ~DecoderBase() = default;
+
+ virtual void decode(const al::span<float*> samples, const size_t samplesToDo,
+ const size_t forwardSamples) = 0;
+
+ /**
+ * The width factor for Super Stereo processing. Can be changed in between
+ * calls to decode, with valid values being between 0...0.7.
+ */
+ float mWidthControl{0.593f};
+
+ float mCurrentWidth{-1.0f};
+};
+
+template<size_t N>
+struct UhjDecoder final : public DecoderBase {
+ static constexpr size_t sFilterDelay{N/2};
+
/* For 2-channel UHJ, shelf filters should use these LF responses. */
static constexpr float sWLFScale{0.661f};
static constexpr float sXYLFScale{1.293f};
@@ -82,7 +103,18 @@ struct UhjDecoder : public DecoderBase, public UhjFilterBase {
DEF_NEWDEL(UhjDecoder)
};
-struct UhjStereoDecoder : public UhjDecoder {
+template<size_t N>
+struct UhjStereoDecoder final : public DecoderBase {
+ static constexpr size_t sFilterDelay{N/2};
+
+ alignas(16) std::array<float,BufferLineSize+MaxResamplerEdge+sFilterDelay> mS{};
+ alignas(16) std::array<float,BufferLineSize+MaxResamplerEdge+sFilterDelay> mD{};
+
+ alignas(16) std::array<float,sFilterDelay-1> mDTHistory{};
+ alignas(16) std::array<float,sFilterDelay-1> mSHistory{};
+
+ alignas(16) std::array<float,BufferLineSize+MaxResamplerEdge + sFilterDelay*2> mTemp{};
+
/**
* Applies Super Stereo processing on a stereo signal to create a B-Format
* signal with FuMa channel ordering and UHJ scaling. The samples span
diff --git a/core/voice.cpp b/core/voice.cpp
index ed6c9bf8..15230726 100644
--- a/core/voice.cpp
+++ b/core/voice.cpp
@@ -854,13 +854,13 @@ void Voice::prepare(DeviceBase *device)
if(mFmtChannels == FmtSuperStereo)
{
- mDecoder = std::make_unique<UhjStereoDecoder>();
- mDecoderPadding = UhjStereoDecoder::sFilterDelay;
+ mDecoder = std::make_unique<UhjStereoDecoder<UhjLengthStd>>();
+ mDecoderPadding = UhjStereoDecoder<UhjLengthStd>::sFilterDelay;
}
else if(IsUHJ(mFmtChannels))
{
- mDecoder = std::make_unique<UhjDecoder>();
- mDecoderPadding = UhjDecoder::sFilterDelay;
+ mDecoder = std::make_unique<UhjDecoder<UhjLengthStd>>();
+ mDecoderPadding = UhjDecoder<UhjLengthStd>::sFilterDelay;
}
else
{
@@ -908,9 +908,9 @@ void Voice::prepare(DeviceBase *device)
*/
if(mFmtChannels == FmtUHJ2)
{
- mChans[0].mAmbiLFScale = UhjDecoder::sWLFScale;
- mChans[1].mAmbiLFScale = UhjDecoder::sXYLFScale;
- mChans[2].mAmbiLFScale = UhjDecoder::sXYLFScale;
+ mChans[0].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sWLFScale;
+ mChans[1].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sXYLFScale;
+ mChans[2].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sXYLFScale;
}
mFlags.set(VoiceIsAmbisonic);
}
@@ -930,9 +930,9 @@ void Voice::prepare(DeviceBase *device)
chandata.mDryParams.NFCtrlFilter = device->mNFCtrlFilter;
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
}
- mChans[0].mAmbiLFScale = UhjDecoder::sWLFScale;
- mChans[1].mAmbiLFScale = UhjDecoder::sXYLFScale;
- mChans[2].mAmbiLFScale = UhjDecoder::sXYLFScale;
+ mChans[0].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sWLFScale;
+ mChans[1].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sXYLFScale;
+ mChans[2].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sXYLFScale;
mFlags.set(VoiceIsAmbisonic);
}
else
diff --git a/core/voice.h b/core/voice.h
index 25560cb4..42ad7704 100644
--- a/core/voice.h
+++ b/core/voice.h
@@ -51,7 +51,7 @@ enum class DirectMode : unsigned char {
/* Maximum number of extra source samples that may need to be loaded, for
* resampling or conversion purposes.
*/
-constexpr uint MaxPostVoiceLoad{MaxResamplerEdge + UhjDecoder::sFilterDelay};
+constexpr uint MaxPostVoiceLoad{MaxResamplerEdge + DecoderBase::sMaxDelay};
enum {