Parameterize the UHJ filter length
parent
b77a556d7b
commit
250f162496
|
@ -2104,8 +2104,8 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
|
|||
}
|
||||
|
||||
nanoseconds::rep sample_delay{0};
|
||||
if(device->mUhjEncoder)
|
||||
sample_delay += UhjEncoder::sFilterDelay;
|
||||
if(auto *encoder{device->mUhjEncoder.get()})
|
||||
sample_delay += encoder->getDelay();
|
||||
if(auto *ambidec = device->AmbiDecoder.get())
|
||||
{
|
||||
if(ambidec->hasStablizer())
|
||||
|
|
|
@ -1093,7 +1093,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding
|
|||
|
||||
if(stereomode.value_or(StereoEncoding::Default) == StereoEncoding::Uhj)
|
||||
{
|
||||
device->mUhjEncoder = std::make_unique<UhjEncoder>();
|
||||
device->mUhjEncoder = std::make_unique<UhjEncoder<UhjLengthStd>>();
|
||||
TRACE("UHJ enabled\n");
|
||||
InitUhjPanning(device);
|
||||
device->PostProcess = &ALCdevice::ProcessUhj;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -9,7 +9,55 @@
|
|||
#include "resampler_limits.h"
|
||||
|
||||
|
||||
static constexpr size_t UhjLengthLq{256};
|
||||
static constexpr size_t UhjLengthHq{512};
|
||||
static constexpr size_t UhjLengthStd{UhjLengthLq};
|
||||
|
||||
|
||||
struct UhjEncoderBase {
|
||||
virtual ~UhjEncoderBase() = default;
|
||||
|
||||
virtual size_t getDelay() noexcept = 0;
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
virtual void encode(float *LeftOut, float *RightOut,
|
||||
const al::span<const float*const,3> InSamples, const size_t SamplesToDo) = 0;
|
||||
};
|
||||
|
||||
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{};
|
||||
|
||||
/* History for the FIR filter. */
|
||||
alignas(16) std::array<float,sFilterDelay*2 - 1> mWXHistory{};
|
||||
|
||||
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) override;
|
||||
|
||||
DEF_NEWDEL(UhjEncoder)
|
||||
};
|
||||
|
||||
|
||||
struct DecoderBase {
|
||||
static constexpr size_t sMaxDelay{256};
|
||||
|
||||
virtual ~DecoderBase() = default;
|
||||
|
||||
virtual void decode(const al::span<float*> samples, const size_t samplesToDo,
|
||||
|
@ -24,37 +72,10 @@ struct DecoderBase {
|
|||
float mCurrentWidth{-1.0f};
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
struct UhjDecoder final : public DecoderBase {
|
||||
static constexpr size_t sFilterDelay{N/2};
|
||||
|
||||
struct UhjFilterBase {
|
||||
/* The filter delay is half it's effective size, so a delay of 128 has a
|
||||
* FIR length of 256.
|
||||
*/
|
||||
static constexpr size_t sFilterDelay{128};
|
||||
};
|
||||
|
||||
struct UhjEncoder : public UhjFilterBase {
|
||||
/* Delays and processing storage for the unfiltered signal. */
|
||||
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mS{};
|
||||
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mD{};
|
||||
|
||||
/* History for the FIR filter. */
|
||||
alignas(16) std::array<float,sFilterDelay*2 - 1> mWXHistory{};
|
||||
|
||||
alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mTemp{};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
DEF_NEWDEL(UhjEncoder)
|
||||
};
|
||||
|
||||
|
||||
struct UhjDecoder : public DecoderBase, public UhjFilterBase {
|
||||
/* 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue