Dynamically allocate the convolution channel mixing data
This commit is contained in:
parent
ebe0765ce3
commit
82873486b7
@ -111,8 +111,20 @@ struct ConvolutionFilter final : public EffectBufferBase {
|
||||
AmbiScaling mAmbiScaling{};
|
||||
ALuint mAmbiOrder{};
|
||||
|
||||
struct ChannelData {
|
||||
alignas(16) FloatBufferLine mBuffer{};
|
||||
float mHfScale{};
|
||||
BandSplitter mFilter{};
|
||||
float Current[MAX_OUTPUT_CHANNELS]{};
|
||||
float Target[MAX_OUTPUT_CHANNELS]{};
|
||||
};
|
||||
using ChannelDataArray = al::FlexArray<ChannelData>;
|
||||
std::unique_ptr<ChannelDataArray> mChans;
|
||||
std::unique_ptr<complex_d[]> mComplexData;
|
||||
|
||||
ConvolutionFilter(size_t numChannels) : mChans{ChannelDataArray::Create(numChannels)}
|
||||
{ }
|
||||
|
||||
DEF_NEWDEL(ConvolutionFilter)
|
||||
};
|
||||
|
||||
@ -123,15 +135,6 @@ struct ConvolutionState final : public EffectState {
|
||||
alignas(16) std::array<double,ConvolveUpdateSamples*2> mOutput[MAX_FILTER_CHANNELS]{};
|
||||
alignas(16) std::array<complex_d,ConvolveUpdateSize> mFftBuffer{};
|
||||
|
||||
ALuint mNumChannels;
|
||||
struct {
|
||||
alignas(16) FloatBufferLine mBuffer{};
|
||||
float mHfScale{};
|
||||
BandSplitter mFilter{};
|
||||
float Current[MAX_OUTPUT_CHANNELS]{};
|
||||
float Target[MAX_OUTPUT_CHANNELS]{};
|
||||
} mChans[MAX_FILTER_CHANNELS];
|
||||
|
||||
ConvolutionState() = default;
|
||||
~ConvolutionState() override = default;
|
||||
|
||||
@ -151,38 +154,30 @@ struct ConvolutionState final : public EffectState {
|
||||
void ConvolutionState::NormalMix(const al::span<FloatBufferLine> samplesOut,
|
||||
const size_t samplesToDo)
|
||||
{
|
||||
for(size_t c{0};c < mNumChannels;++c)
|
||||
MixSamples({mChans[c].mBuffer.data(), samplesToDo}, samplesOut, mChans[c].Current,
|
||||
mChans[c].Target, samplesToDo, 0);
|
||||
auto &chans = *mFilter->mChans;
|
||||
for(size_t c{0};c < chans.size();++c)
|
||||
MixSamples({chans[c].mBuffer.data(), samplesToDo}, samplesOut, chans[c].Current,
|
||||
chans[c].Target, samplesToDo, 0);
|
||||
}
|
||||
|
||||
void ConvolutionState::UpsampleMix(const al::span<FloatBufferLine> samplesOut,
|
||||
const size_t samplesToDo)
|
||||
{
|
||||
for(size_t c{0};c < mNumChannels;++c)
|
||||
auto &chans = *mFilter->mChans;
|
||||
for(size_t c{0};c < chans.size();++c)
|
||||
{
|
||||
const al::span<float> src{mChans[c].mBuffer.data(), samplesToDo};
|
||||
mChans[c].mFilter.processHfScale(src, mChans[c].mHfScale);
|
||||
MixSamples(src, samplesOut, mChans[c].Current, mChans[c].Target, samplesToDo, 0);
|
||||
const al::span<float> src{chans[c].mBuffer.data(), samplesToDo};
|
||||
chans[c].mFilter.processHfScale(src, chans[c].mHfScale);
|
||||
MixSamples(src, samplesOut, chans[c].Current, chans[c].Target, samplesToDo, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionState::deviceUpdate(const ALCdevice *device)
|
||||
void ConvolutionState::deviceUpdate(const ALCdevice* /*device*/)
|
||||
{
|
||||
mFifoPos = 0;
|
||||
for(auto &buffer : mOutput)
|
||||
buffer.fill(0.0f);
|
||||
mFftBuffer.fill(complex_d{});
|
||||
|
||||
const BandSplitter splitter{400.0f / static_cast<float>(device->Frequency)};
|
||||
for(auto &e : mChans)
|
||||
{
|
||||
e.mBuffer.fill(0.0f);
|
||||
e.mHfScale = 1.0f;
|
||||
e.mFilter = splitter;
|
||||
std::fill(std::begin(e.Current), std::end(e.Current), 0.0f);
|
||||
std::fill(std::begin(e.Target), std::end(e.Target), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
EffectBufferBase *ConvolutionState::createBuffer(const ALCdevice *device,
|
||||
@ -211,12 +206,17 @@ EffectBufferBase *ConvolutionState::createBuffer(const ALCdevice *device,
|
||||
(uint64_t{buffer.mSampleLen}*device->Frequency + (buffer.mSampleRate-1)) /
|
||||
buffer.mSampleRate);
|
||||
|
||||
al::intrusive_ptr<ConvolutionFilter> filter{new ConvolutionFilter{}};
|
||||
|
||||
auto bytesPerSample = BytesFromFmt(buffer.mType);
|
||||
auto numChannels = ChannelsFromFmt(buffer.mChannels, buffer.mAmbiOrder);
|
||||
auto realChannels = ChannelsFromFmt(buffer.mChannels, buffer.mAmbiOrder);
|
||||
auto numChannels = ChannelsFromFmt(buffer.mChannels,
|
||||
minu(buffer.mAmbiOrder, device->mAmbiOrder));
|
||||
constexpr size_t m{ConvolveUpdateSize/2 + 1};
|
||||
|
||||
const BandSplitter splitter{400.0f / static_cast<float>(device->Frequency)};
|
||||
al::intrusive_ptr<ConvolutionFilter> filter{new ConvolutionFilter{numChannels}};
|
||||
for(auto &e : *filter->mChans)
|
||||
e.mFilter = splitter;
|
||||
|
||||
/* Calculate the number of segments needed to hold the impulse response and
|
||||
* the input history (rounded up), and allocate them.
|
||||
*/
|
||||
@ -242,7 +242,7 @@ EffectBufferBase *ConvolutionState::createBuffer(const ALCdevice *device,
|
||||
for(size_t c{0};c < numChannels;++c)
|
||||
{
|
||||
/* Load the samples from the buffer, and resample to match the device. */
|
||||
LoadSamples(srcsamples.get(), buffer.mData.data() + bytesPerSample*c, numChannels,
|
||||
LoadSamples(srcsamples.get(), buffer.mData.data() + bytesPerSample*c, realChannels,
|
||||
buffer.mType, buffer.mSampleLen);
|
||||
if(device->Frequency != buffer.mSampleRate)
|
||||
resampler.process(buffer.mSampleLen, srcsamples.get(), resampledCount,
|
||||
@ -273,8 +273,6 @@ void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slo
|
||||
if(!mFilter) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
const ALuint min_order{minu(mFilter->mAmbiOrder, device->mAmbiOrder)};
|
||||
mNumChannels = mFilter ? ChannelsFromFmt(mFilter->mChannels, min_order) : 0u;
|
||||
mMix = &ConvolutionState::NormalMix;
|
||||
|
||||
/* The iFFT'd response is scaled up by the number of bins, so apply the
|
||||
@ -282,6 +280,7 @@ void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slo
|
||||
*/
|
||||
constexpr size_t m{ConvolveUpdateSize/2 + 1};
|
||||
const float gain{slot->Params.Gain * (1.0f/m)};
|
||||
auto &chans = *mFilter->mChans;
|
||||
if(mFilter->mChannels == FmtBFormat3D || mFilter->mChannels == FmtBFormat2D)
|
||||
{
|
||||
if(device->mAmbiOrder > mFilter->mAmbiOrder)
|
||||
@ -289,9 +288,9 @@ void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slo
|
||||
mMix = &ConvolutionState::UpsampleMix;
|
||||
const auto scales = BFormatDec::GetHFOrderScales(mFilter->mAmbiOrder,
|
||||
device->mAmbiOrder);
|
||||
mChans[0].mHfScale = scales[0];
|
||||
for(size_t i{1};i < mNumChannels;++i)
|
||||
mChans[i].mHfScale = scales[1];
|
||||
chans[0].mHfScale = scales[0];
|
||||
for(size_t i{1};i < chans.size();++i)
|
||||
chans[i].mHfScale = scales[1];
|
||||
}
|
||||
mOutTarget = target.Main->Buffer;
|
||||
|
||||
@ -301,11 +300,11 @@ void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slo
|
||||
GetAmbiLayout(mFilter->mAmbiLayout).data()};
|
||||
|
||||
std::array<float,MAX_AMBI_CHANNELS> coeffs{};
|
||||
for(size_t c{0u};c < mNumChannels;++c)
|
||||
for(size_t c{0u};c < chans.size();++c)
|
||||
{
|
||||
const size_t acn{index_map[c]};
|
||||
coeffs[acn] = scales[acn];
|
||||
ComputePanGains(target.Main, coeffs.data(), gain, mChans[c].Target);
|
||||
ComputePanGains(target.Main, coeffs.data(), gain, chans[c].Target);
|
||||
coeffs[acn] = 0.0f;
|
||||
}
|
||||
}
|
||||
@ -319,8 +318,8 @@ void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slo
|
||||
if(lidx != INVALID_CHANNEL_INDEX && ridx != INVALID_CHANNEL_INDEX)
|
||||
{
|
||||
mOutTarget = target.RealOut->Buffer;
|
||||
mChans[0].Target[lidx] = gain;
|
||||
mChans[1].Target[ridx] = gain;
|
||||
chans[0].Target[lidx] = gain;
|
||||
chans[1].Target[ridx] = gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -328,8 +327,8 @@ void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slo
|
||||
const auto rcoeffs = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}, 0.0f);
|
||||
|
||||
mOutTarget = target.Main->Buffer;
|
||||
ComputePanGains(target.Main, lcoeffs.data(), gain, mChans[0].Target);
|
||||
ComputePanGains(target.Main, rcoeffs.data(), gain, mChans[1].Target);
|
||||
ComputePanGains(target.Main, lcoeffs.data(), gain, chans[0].Target);
|
||||
ComputePanGains(target.Main, rcoeffs.data(), gain, chans[1].Target);
|
||||
}
|
||||
}
|
||||
else if(mFilter->mChannels == FmtMono)
|
||||
@ -337,7 +336,7 @@ void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slo
|
||||
const auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f);
|
||||
|
||||
mOutTarget = target.Main->Buffer;
|
||||
ComputePanGains(target.Main, coeffs.data(), gain, mChans[0].Target);
|
||||
ComputePanGains(target.Main, coeffs.data(), gain, chans[0].Target);
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,6 +348,7 @@ void ConvolutionState::process(const size_t samplesToDo,
|
||||
|
||||
constexpr size_t m{ConvolveUpdateSize/2 + 1};
|
||||
size_t curseg{mFilter->mCurrentSegment};
|
||||
auto &chans = *mFilter->mChans;
|
||||
|
||||
for(size_t base{0u};base < samplesToDo;)
|
||||
{
|
||||
@ -357,10 +357,10 @@ void ConvolutionState::process(const size_t samplesToDo,
|
||||
/* Retrieve the output samples from the FIFO and fill in the new input
|
||||
* samples.
|
||||
*/
|
||||
for(size_t c{0};c < mNumChannels;++c)
|
||||
for(size_t c{0};c < chans.size();++c)
|
||||
{
|
||||
auto fifo_iter = mOutput[c].begin() + mFifoPos;
|
||||
std::transform(fifo_iter, fifo_iter+todo, mChans[c].mBuffer.begin()+base,
|
||||
std::transform(fifo_iter, fifo_iter+todo, chans[c].mBuffer.begin()+base,
|
||||
[](double d) noexcept -> float { return static_cast<float>(d); });
|
||||
}
|
||||
|
||||
@ -380,7 +380,7 @@ void ConvolutionState::process(const size_t samplesToDo,
|
||||
std::copy_n(mFftBuffer.begin(), m, &mFilter->mInputHistory[curseg*m]);
|
||||
mFftBuffer.fill(complex_d{});
|
||||
|
||||
for(size_t c{0};c < mNumChannels;++c)
|
||||
for(size_t c{0};c < chans.size();++c)
|
||||
{
|
||||
/* Convolve each input segment with its IR filter counterpart
|
||||
* (aligned in time).
|
||||
|
Loading…
x
Reference in New Issue
Block a user