Revert "Don't apply the HF scaling for "upsampling" ambisonics"
This reverts commit bf3f63fb4c5faa45784d7433d68b7013e29ee2c1.
This commit is contained in:
parent
02cf2bde7b
commit
e5c49d019f
@ -194,6 +194,8 @@ struct ConvolutionState final : public EffectState {
|
|||||||
|
|
||||||
struct ChannelData {
|
struct ChannelData {
|
||||||
alignas(16) FloatBufferLine mBuffer{};
|
alignas(16) FloatBufferLine mBuffer{};
|
||||||
|
float mHfScale{};
|
||||||
|
BandSplitter mFilter{};
|
||||||
float Current[MAX_OUTPUT_CHANNELS]{};
|
float Current[MAX_OUTPUT_CHANNELS]{};
|
||||||
float Target[MAX_OUTPUT_CHANNELS]{};
|
float Target[MAX_OUTPUT_CHANNELS]{};
|
||||||
};
|
};
|
||||||
@ -233,6 +235,7 @@ void ConvolutionState::UpsampleMix(const al::span<FloatBufferLine> samplesOut,
|
|||||||
for(auto &chan : *mChans)
|
for(auto &chan : *mChans)
|
||||||
{
|
{
|
||||||
const al::span<float> src{chan.mBuffer.data(), samplesToDo};
|
const al::span<float> src{chan.mBuffer.data(), samplesToDo};
|
||||||
|
chan.mFilter.processHfScale(src, chan.mHfScale);
|
||||||
MixSamples(src, samplesOut, chan.Current, chan.Target, samplesToDo, 0);
|
MixSamples(src, samplesOut, chan.Current, chan.Target, samplesToDo, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,6 +280,10 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const Buffer &buff
|
|||||||
(uint64_t{buffer.storage->mSampleLen}*device->Frequency+(buffer.storage->mSampleRate-1)) /
|
(uint64_t{buffer.storage->mSampleLen}*device->Frequency+(buffer.storage->mSampleRate-1)) /
|
||||||
buffer.storage->mSampleRate);
|
buffer.storage->mSampleRate);
|
||||||
|
|
||||||
|
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
|
||||||
|
for(auto &e : *mChans)
|
||||||
|
e.mFilter = splitter;
|
||||||
|
|
||||||
mFilter.resize(numChannels, {});
|
mFilter.resize(numChannels, {});
|
||||||
mOutput.resize(numChannels, {});
|
mOutput.resize(numChannels, {});
|
||||||
|
|
||||||
@ -411,7 +418,13 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot
|
|||||||
{
|
{
|
||||||
DeviceBase *device{context->mDevice};
|
DeviceBase *device{context->mDevice};
|
||||||
if(device->mAmbiOrder > mAmbiOrder)
|
if(device->mAmbiOrder > mAmbiOrder)
|
||||||
|
{
|
||||||
mMix = &ConvolutionState::UpsampleMix;
|
mMix = &ConvolutionState::UpsampleMix;
|
||||||
|
const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, true);
|
||||||
|
(*mChans)[0].mHfScale = scales[0];
|
||||||
|
for(size_t i{1};i < mChans->size();++i)
|
||||||
|
(*mChans)[i].mHfScale = scales[1];
|
||||||
|
}
|
||||||
mOutTarget = target.Main->Buffer;
|
mOutTarget = target.Main->Buffer;
|
||||||
|
|
||||||
auto&& scales = GetAmbiScales(mAmbiScaling);
|
auto&& scales = GetAmbiScales(mAmbiScaling);
|
||||||
|
@ -453,6 +453,8 @@ struct ReverbState final : public EffectState {
|
|||||||
|
|
||||||
|
|
||||||
bool mUpmixOutput{false};
|
bool mUpmixOutput{false};
|
||||||
|
std::array<float,MaxAmbiOrder+1> mOrderScales{};
|
||||||
|
std::array<std::array<BandSplitter,NUM_LINES>,2> mAmbiSplitter;
|
||||||
|
|
||||||
|
|
||||||
static void DoMixRow(const al::span<float> OutBuffer, const al::span<const float,4> Gains,
|
static void DoMixRow(const al::span<float> OutBuffer, const al::span<const float,4> Gains,
|
||||||
@ -499,19 +501,30 @@ struct ReverbState final : public EffectState {
|
|||||||
{
|
{
|
||||||
ASSUME(todo > 0);
|
ASSUME(todo > 0);
|
||||||
|
|
||||||
/* TODO: If HF scaling isn't needed for upsampling, the A-to-B-Format
|
/* When upsampling, the B-Format conversion needs to be done separately
|
||||||
* matrix can be included with the panning gains like non-upsampled
|
* so the proper HF scaling can be applied to each B-Format channel.
|
||||||
* output.
|
* The panning gains then pan and upsample the B-Format channels.
|
||||||
*/
|
*/
|
||||||
const al::span<float> tmpspan{al::assume_aligned<16>(mTempLine.data()), todo};
|
const al::span<float> tmpspan{al::assume_aligned<16>(mTempLine.data()), todo};
|
||||||
for(size_t c{0u};c < NUM_LINES;c++)
|
for(size_t c{0u};c < NUM_LINES;c++)
|
||||||
{
|
{
|
||||||
DoMixRow(tmpspan, EarlyA2B[c], mEarlySamples[0].data(), mEarlySamples[0].size());
|
DoMixRow(tmpspan, EarlyA2B[c], mEarlySamples[0].data(), mEarlySamples[0].size());
|
||||||
|
|
||||||
|
/* Apply scaling to the B-Format's HF response to "upsample" it to
|
||||||
|
* higher-order output.
|
||||||
|
*/
|
||||||
|
const float hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]};
|
||||||
|
mAmbiSplitter[0][c].processHfScale(tmpspan, hfscale);
|
||||||
|
|
||||||
MixSamples(tmpspan, samplesOut, mEarly.CurrentGain[c], mEarly.PanGain[c], todo, 0);
|
MixSamples(tmpspan, samplesOut, mEarly.CurrentGain[c], mEarly.PanGain[c], todo, 0);
|
||||||
}
|
}
|
||||||
for(size_t c{0u};c < NUM_LINES;c++)
|
for(size_t c{0u};c < NUM_LINES;c++)
|
||||||
{
|
{
|
||||||
DoMixRow(tmpspan, LateA2B[c], mLateSamples[0].data(), mLateSamples[0].size());
|
DoMixRow(tmpspan, LateA2B[c], mLateSamples[0].data(), mLateSamples[0].size());
|
||||||
|
|
||||||
|
const float hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]};
|
||||||
|
mAmbiSplitter[1][c].processHfScale(tmpspan, hfscale);
|
||||||
|
|
||||||
MixSamples(tmpspan, samplesOut, mLate.CurrentGain[c], mLate.PanGain[c], todo, 0);
|
MixSamples(tmpspan, samplesOut, mLate.CurrentGain[c], mLate.PanGain[c], todo, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -670,7 +683,19 @@ void ReverbState::deviceUpdate(const DeviceBase *device, const Buffer&)
|
|||||||
mDoFading = true;
|
mDoFading = true;
|
||||||
mOffset = 0;
|
mOffset = 0;
|
||||||
|
|
||||||
mUpmixOutput = (device->mAmbiOrder > 1);
|
if(device->mAmbiOrder > 1)
|
||||||
|
{
|
||||||
|
mUpmixOutput = true;
|
||||||
|
mOrderScales = AmbiScale::GetHFOrderScales(1, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mUpmixOutput = false;
|
||||||
|
mOrderScales.fill(1.0f);
|
||||||
|
}
|
||||||
|
mAmbiSplitter[0][0].init(device->mXOverFreq / frequency);
|
||||||
|
std::fill(mAmbiSplitter[0].begin()+1, mAmbiSplitter[0].end(), mAmbiSplitter[0][0]);
|
||||||
|
std::fill(mAmbiSplitter[1].begin(), mAmbiSplitter[1].end(), mAmbiSplitter[0][0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************
|
/**************************************
|
||||||
|
@ -366,3 +366,22 @@ const std::array<AmbiChannelFloatArray,9> AmbiScale::SecondOrder2DUp{CalcSecondO
|
|||||||
const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrderUp{CalcThirdOrderUp()};
|
const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrderUp{CalcThirdOrderUp()};
|
||||||
const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrder2DUp{CalcThirdOrder2DUp()};
|
const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrder2DUp{CalcThirdOrder2DUp()};
|
||||||
const std::array<AmbiChannelFloatArray,25> AmbiScale::FourthOrder2DUp{CalcFourthOrder2DUp()};
|
const std::array<AmbiChannelFloatArray,25> AmbiScale::FourthOrder2DUp{CalcFourthOrder2DUp()};
|
||||||
|
|
||||||
|
const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale1O{{
|
||||||
|
2.000000000e+00f, 1.154700538e+00f
|
||||||
|
}};
|
||||||
|
const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale1O2D{{
|
||||||
|
1.414213562e+00f, 1.000000000e+00f
|
||||||
|
}};
|
||||||
|
const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale2O{{
|
||||||
|
1.972026594e+00f, 1.527525232e+00f, 7.888106377e-01f
|
||||||
|
}};
|
||||||
|
const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale2O2D{{
|
||||||
|
1.414213562e+00f, 1.224744871e+00f, 7.071067812e-01f
|
||||||
|
}};
|
||||||
|
const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale3O{{
|
||||||
|
1.865086714e+00f, 1.606093894e+00f, 1.142055301e+00f, 5.683795528e-01f
|
||||||
|
}};
|
||||||
|
const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale3O2D{{
|
||||||
|
1.414213562e+00f, 1.306562965e+00f, 1.000000000e+00f, 5.411961001e-01f
|
||||||
|
}};
|
||||||
|
@ -113,6 +113,20 @@ struct AmbiScale {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */
|
||||||
|
static std::array<float,MaxAmbiOrder+1> GetHFOrderScales(const uint order, const bool is3D) noexcept
|
||||||
|
{
|
||||||
|
if(order >= 3) return is3D ? DecoderHFScale3O : DecoderHFScale3O2D;
|
||||||
|
if(order == 2) return is3D ? DecoderHFScale2O : DecoderHFScale2O2D;
|
||||||
|
return is3D ? DecoderHFScale1O : DecoderHFScale1O2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::array<float,MaxAmbiOrder+1> DecoderHFScale1O;
|
||||||
|
static const std::array<float,MaxAmbiOrder+1> DecoderHFScale1O2D;
|
||||||
|
static const std::array<float,MaxAmbiOrder+1> DecoderHFScale2O;
|
||||||
|
static const std::array<float,MaxAmbiOrder+1> DecoderHFScale2O2D;
|
||||||
|
static const std::array<float,MaxAmbiOrder+1> DecoderHFScale3O;
|
||||||
|
static const std::array<float,MaxAmbiOrder+1> DecoderHFScale3O2D;
|
||||||
|
|
||||||
static const std::array<std::array<float,MaxAmbiChannels>,4> FirstOrderUp;
|
static const std::array<std::array<float,MaxAmbiChannels>,4> FirstOrderUp;
|
||||||
static const std::array<std::array<float,MaxAmbiChannels>,4> FirstOrder2DUp;
|
static const std::array<std::array<float,MaxAmbiChannels>,4> FirstOrder2DUp;
|
||||||
|
@ -892,20 +892,52 @@ void Voice::prepare(DeviceBase *device)
|
|||||||
/* Make sure the sample history is cleared. */
|
/* Make sure the sample history is cleared. */
|
||||||
std::fill(mPrevSamples.begin(), mPrevSamples.end(), HistoryLine{});
|
std::fill(mPrevSamples.begin(), mPrevSamples.end(), HistoryLine{});
|
||||||
|
|
||||||
/* 2-channel UHJ needs different shelf filters. However, we can't just use
|
/* Don't need to set the VoiceIsAmbisonic flag if the device is not higher
|
||||||
* different shelf filters after mixing it, given any old speaker setup the
|
* order than the voice. No HF scaling is necessary to mix it.
|
||||||
* 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
|
|
||||||
* quad channels are encoded right back into B-Format.
|
|
||||||
*
|
|
||||||
* This isn't perfect, but without an entirely separate and limited UHJ2
|
|
||||||
* path, it's better than nothing.
|
|
||||||
*
|
|
||||||
* Do not apply the shelf filter with UHJ output. UHJ2->B-Format->UHJ2 is
|
|
||||||
* identity, so don't mess with it.
|
|
||||||
*/
|
*/
|
||||||
if(mFmtChannels == FmtUHJ2 && !device->mUhjEncoder)
|
if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder)
|
||||||
{
|
{
|
||||||
|
const uint8_t *OrderFromChan{Is2DAmbisonic(mFmtChannels) ?
|
||||||
|
AmbiIndex::OrderFrom2DChannel().data() : AmbiIndex::OrderFromChannel().data()};
|
||||||
|
const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, !Is2DAmbisonic(mFmtChannels));
|
||||||
|
|
||||||
|
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
|
||||||
|
for(auto &chandata : mChans)
|
||||||
|
{
|
||||||
|
chandata.mAmbiHFScale = scales[*(OrderFromChan++)];
|
||||||
|
chandata.mAmbiLFScale = 1.0f;
|
||||||
|
chandata.mAmbiSplitter = splitter;
|
||||||
|
chandata.mDryParams = DirectParams{};
|
||||||
|
chandata.mDryParams.NFCtrlFilter = device->mNFCtrlFilter;
|
||||||
|
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 quad channels are encoded right back onto higher-order
|
||||||
|
* B-Format.
|
||||||
|
*
|
||||||
|
* This isn't perfect, but without an entirely separate and limited
|
||||||
|
* UHJ2 path, it's better than nothing.
|
||||||
|
*/
|
||||||
|
if(mFmtChannels == FmtUHJ2)
|
||||||
|
{
|
||||||
|
mChans[0].mAmbiHFScale = 1.0f;
|
||||||
|
mChans[0].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sWLFScale;
|
||||||
|
mChans[1].mAmbiHFScale = 1.0f;
|
||||||
|
mChans[1].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sXYLFScale;
|
||||||
|
mChans[2].mAmbiHFScale = 1.0f;
|
||||||
|
mChans[2].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sXYLFScale;
|
||||||
|
}
|
||||||
|
mFlags.set(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)};
|
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
|
||||||
for(auto &chandata : mChans)
|
for(auto &chandata : mChans)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user