Improve the 2-channel UHJ response

This attempts to correct for the differences needed for 2-channel UHJ's shelf
filters given the output shelf filters. It's far from ideal, but better than
nothing.
master
Chris Robinson 2021-12-09 22:00:35 -08:00
parent b72402d3e7
commit a97dba6f41
4 changed files with 73 additions and 4 deletions

View File

@ -94,6 +94,36 @@ void BandSplitterR<Real>::processHfScale(const al::span<Real> samples, const Rea
mApZ1 = ap_z1;
}
template<typename Real>
void BandSplitterR<Real>::processScale(const al::span<Real> samples, const Real hfscale, const Real lfscale)
{
const Real ap_coeff{mCoeff};
const Real lp_coeff{mCoeff*0.5f + 0.5f};
Real lp_z1{mLpZ1};
Real lp_z2{mLpZ2};
Real ap_z1{mApZ1};
auto proc_sample = [hfscale,lfscale,ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1](const Real in) noexcept -> Real
{
Real d{(in - lp_z1) * lp_coeff};
Real lp_y{lp_z1 + d};
lp_z1 = lp_y + d;
d = (lp_y - lp_z2) * lp_coeff;
lp_y = lp_z2 + d;
lp_z2 = lp_y + d;
Real ap_y{in*ap_coeff + ap_z1};
ap_z1 = in - ap_y*ap_coeff;
/* Apply separate factors to the high and low frequencies. */
return (ap_y-lp_y)*hfscale + lp_y*lfscale;
};
std::transform(samples.begin(), samples.end(), samples.begin(), proc_sample);
mLpZ1 = lp_z1;
mLpZ2 = lp_z2;
mApZ1 = ap_z1;
}
template<typename Real>
void BandSplitterR<Real>::applyAllpass(const al::span<Real> samples) const
{

View File

@ -24,6 +24,7 @@ public:
void process(const al::span<const Real> input, Real *hpout, Real *lpout);
void processHfScale(const al::span<Real> samples, const Real hfscale);
void processScale(const al::span<Real> samples, const Real hfscale, const Real lfscale);
/* The all-pass portion of the band splitter. Applies the same phase shift
* without splitting the signal. Note that each use of this method is

View File

@ -647,8 +647,8 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo
{Device->ResampledData, DstBufferSize})};
++voiceSamples;
if((mFlags&VoiceIsAmbisonic))
chandata.mAmbiSplitter.processHfScale({ResampledData, DstBufferSize},
chandata.mAmbiScale);
chandata.mAmbiSplitter.processScale({ResampledData, DstBufferSize},
chandata.mAmbiHFScale, chandata.mAmbiLFScale);
/* Now filter and mix to the appropriate outputs. */
const al::span<float,BufferLineSize> FilterBuf{Device->FilteredData};
@ -840,11 +840,49 @@ void Voice::prepare(DeviceBase *device)
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
for(auto &chandata : mChans)
{
chandata.mAmbiScale = scales[*(OrderFromChan++)];
chandata.mAmbiHFScale = scales[*(OrderFromChan++)];
chandata.mAmbiLFScale = 1.0f;
chandata.mAmbiSplitter = splitter;
chandata.mDryParams = DirectParams{};
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
}
/* 2-channel UHJ needs different shelf filters. However, we can't just
* use different shelf filters after mixing it and with any old speaker
* setup the user has. To make this work, we apply the expected shelf
* filters for decoding UHJ2 to quad (only needs LF scaling), and act
* as if those 4 channels are encoded back onto first-order B-Format,
* which then upsamples to higher order as normal (only needs HF
* scaling).
*
* This isn't perfect, but without an entirely separate and limited
* UHJ2 path, it's better than nothing.
*/
if(mFmtChannels == FmtUHJ2)
{
mChans[0].mAmbiLFScale = 0.661f;
mChans[1].mAmbiLFScale = 1.293f;
mChans[2].mAmbiLFScale = 1.293f;
}
mFlags |= VoiceIsAmbisonic;
}
else if(mFmtChannels == FmtUHJ2 && !device->mUhjEncoder)
{
/* 2-channel UHJ with first-order output also needs the shelf filter
* correction applied, except with UHJ output (UHJ2->B-Format->UHJ2 is
* identity, so don't mess with it).
*/
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
for(auto &chandata : mChans)
{
chandata.mAmbiHFScale = 1.0f;
chandata.mAmbiLFScale = 1.0f;
chandata.mAmbiSplitter = splitter;
chandata.mDryParams = DirectParams{};
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
}
mChans[0].mAmbiLFScale = 0.661f;
mChans[1].mAmbiLFScale = 1.293f;
mChans[2].mAmbiLFScale = 1.293f;
mFlags |= VoiceIsAmbisonic;
}
else

View File

@ -241,7 +241,7 @@ struct Voice {
al::vector<HistoryLine,16> mPrevSamples{2};
struct ChannelData {
float mAmbiScale;
float mAmbiHFScale, mAmbiLFScale;
BandSplitter mAmbiSplitter;
DirectParams mDryParams;