aboutsummaryrefslogtreecommitdiffstats
path: root/alc/effects/convolution.cpp
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2020-09-06 00:16:50 -0700
committerChris Robinson <[email protected]>2020-09-06 00:16:50 -0700
commit29566b995cfd80f00f8c4c7e2c3d4c798bcffdfe (patch)
tree9c116c646f36825e1bd638ee7f04e72135477a15 /alc/effects/convolution.cpp
parent7eb5dfb3f82b6ecd3dd23f8d3747c4f09cc19561 (diff)
Avoid an extraneous ConvolutionFilter sub-object
Diffstat (limited to 'alc/effects/convolution.cpp')
-rw-r--r--alc/effects/convolution.cpp202
1 files changed, 90 insertions, 112 deletions
diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp
index 2ba0bde8..102bed59 100644
--- a/alc/effects/convolution.cpp
+++ b/alc/effects/convolution.cpp
@@ -97,7 +97,8 @@ using complex_d = std::complex<double>;
constexpr size_t ConvolveUpdateSize{1024};
constexpr size_t ConvolveUpdateSamples{ConvolveUpdateSize / 2};
-struct ConvolutionFilter {
+
+struct ConvolutionState final : public EffectState {
FmtChannels mChannels{};
AmbiLayout mAmbiLayout{};
AmbiScaling mAmbiScaling{};
@@ -121,51 +122,89 @@ struct ConvolutionFilter {
std::unique_ptr<ChannelDataArray> mChans;
std::unique_ptr<complex_d[]> mComplexData;
- ConvolutionFilter(size_t numChannels) : mChans{ChannelDataArray::Create(numChannels)}
- { }
- bool init(const ALCdevice *device, const BufferStorage &buffer);
+ ConvolutionState() = default;
+ ~ConvolutionState() override = default;
void NormalMix(const al::span<FloatBufferLine> samplesOut, const size_t samplesToDo);
void UpsampleMix(const al::span<FloatBufferLine> samplesOut, const size_t samplesToDo);
- void (ConvolutionFilter::*mMix)(const al::span<FloatBufferLine>,const size_t)
- {&ConvolutionFilter::NormalMix};
+ void (ConvolutionState::*mMix)(const al::span<FloatBufferLine>,const size_t)
+ {&ConvolutionState::NormalMix};
- void update(al::span<FloatBufferLine> &outTarget, const ALCcontext *context,
- const ALeffectslot *slot, const EffectProps *props, const EffectTarget target);
- void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
- const al::span<FloatBufferLine> samplesOut);
+ void deviceUpdate(const ALCdevice *device) override;
+ void setBuffer(const ALCdevice *device, const BufferStorage *buffer) override;
+ void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
+ void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override;
- DEF_NEWDEL(ConvolutionFilter)
+ DEF_NEWDEL(ConvolutionState)
};
-bool ConvolutionFilter::init(const ALCdevice *device, const BufferStorage &buffer)
+void ConvolutionState::NormalMix(const al::span<FloatBufferLine> samplesOut,
+ const size_t samplesToDo)
{
- constexpr size_t m{ConvolveUpdateSize/2 + 1};
+ for(auto &chan : *mChans)
+ MixSamples({chan.mBuffer.data(), samplesToDo}, samplesOut, chan.Current, chan.Target,
+ samplesToDo, 0);
+}
+
+void ConvolutionState::UpsampleMix(const al::span<FloatBufferLine> samplesOut,
+ const size_t samplesToDo)
+{
+ for(auto &chan : *mChans)
+ {
+ const al::span<float> src{chan.mBuffer.data(), samplesToDo};
+ chan.mFilter.processHfScale(src, chan.mHfScale);
+ MixSamples(src, samplesOut, chan.Current, chan.Target, samplesToDo, 0);
+ }
+}
+
+
+void ConvolutionState::deviceUpdate(const ALCdevice* /*device*/)
+{
+}
+
+void ConvolutionState::setBuffer(const ALCdevice *device, const BufferStorage *buffer)
+{
+ mFifoPos = 0;
+ decltype(mOutput){}.swap(mOutput);
+ mFftBuffer.fill(complex_d{});
+
+ mCurrentSegment = 0;
+ mNumConvolveSegs = 0;
+
+ mChans = nullptr;
+ mComplexData = nullptr;
+
+ /* An empty buffer doesn't need a convolution filter. */
+ if(!buffer || buffer->mSampleLen < 1) return;
/* FIXME: Support anything. */
- if(buffer.mChannels != FmtMono && buffer.mChannels != FmtStereo
- && buffer.mChannels != FmtBFormat2D && buffer.mChannels != FmtBFormat3D)
- return false;
- if((buffer.mChannels == FmtBFormat2D || buffer.mChannels == FmtBFormat3D)
- && buffer.mAmbiOrder > 1)
- return false;
+ if(buffer->mChannels != FmtMono && buffer->mChannels != FmtStereo
+ && buffer->mChannels != FmtBFormat2D && buffer->mChannels != FmtBFormat3D)
+ return;
+ if((buffer->mChannels == FmtBFormat2D || buffer->mChannels == FmtBFormat3D)
+ && buffer->mAmbiOrder > 1)
+ return;
+
+ constexpr size_t m{ConvolveUpdateSize/2 + 1};
+ auto bytesPerSample = BytesFromFmt(buffer->mType);
+ auto realChannels = ChannelsFromFmt(buffer->mChannels, buffer->mAmbiOrder);
+ auto numChannels = ChannelsFromFmt(buffer->mChannels,
+ minu(buffer->mAmbiOrder, device->mAmbiOrder));
+
+ mChans = ChannelDataArray::Create(numChannels);
/* The impulse response needs to have the same sample rate as the input and
* output. The bsinc24 resampler is decent, but there is high-frequency
* attenation that some people may be able to pick up on. Since this is
- * very infrequent called, go ahead and use the polyphase resampler.
+ * called very infrequently, go ahead and use the polyphase resampler.
*/
PPhaseResampler resampler;
- if(device->Frequency != buffer.mSampleRate)
- resampler.init(buffer.mSampleRate, device->Frequency);
+ if(device->Frequency != buffer->mSampleRate)
+ resampler.init(buffer->mSampleRate, device->Frequency);
const auto resampledCount = static_cast<ALuint>(
- (uint64_t{buffer.mSampleLen}*device->Frequency + (buffer.mSampleRate-1)) /
- buffer.mSampleRate);
-
- auto bytesPerSample = BytesFromFmt(buffer.mType);
- auto realChannels = ChannelsFromFmt(buffer.mChannels, buffer.mAmbiOrder);
- auto numChannels = mChans->size();
+ (uint64_t{buffer->mSampleLen}*device->Frequency + (buffer->mSampleRate-1)) /
+ buffer->mSampleRate);
const BandSplitter splitter{400.0f / static_cast<float>(device->Frequency)};
for(auto &e : *mChans)
@@ -182,21 +221,21 @@ bool ConvolutionFilter::init(const ALCdevice *device, const BufferStorage &buffe
mComplexData = std::make_unique<complex_d[]>(complex_length);
std::fill_n(mComplexData.get(), complex_length, complex_d{});
- mChannels = buffer.mChannels;
- mAmbiLayout = buffer.mAmbiLayout;
- mAmbiScaling = buffer.mAmbiScaling;
- mAmbiOrder = buffer.mAmbiOrder;
+ mChannels = buffer->mChannels;
+ mAmbiLayout = buffer->mAmbiLayout;
+ mAmbiScaling = buffer->mAmbiScaling;
+ mAmbiOrder = buffer->mAmbiOrder;
auto fftbuffer = std::make_unique<std::array<complex_d,ConvolveUpdateSize>>();
- auto srcsamples = std::make_unique<double[]>(maxz(buffer.mSampleLen, resampledCount));
+ auto srcsamples = std::make_unique<double[]>(maxz(buffer->mSampleLen, resampledCount));
complex_d *filteriter = mComplexData.get() + mNumConvolveSegs*m;
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, realChannels,
- buffer.mType, buffer.mSampleLen);
- if(device->Frequency != buffer.mSampleRate)
- resampler.process(buffer.mSampleLen, srcsamples.get(), resampledCount,
+ 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,
srcsamples.get());
size_t done{0};
@@ -212,33 +251,17 @@ bool ConvolutionFilter::init(const ALCdevice *device, const BufferStorage &buffe
filteriter = std::copy_n(fftbuffer->cbegin(), m, filteriter);
}
}
- return true;
}
-void ConvolutionFilter::NormalMix(const al::span<FloatBufferLine> samplesOut,
- const size_t samplesToDo)
-{
- for(auto &chan : *mChans)
- MixSamples({chan.mBuffer.data(), samplesToDo}, samplesOut, chan.Current, chan.Target,
- samplesToDo, 0);
-}
-void ConvolutionFilter::UpsampleMix(const al::span<FloatBufferLine> samplesOut,
- const size_t samplesToDo)
+void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slot,
+ const EffectProps* /*props*/, const EffectTarget target)
{
- for(auto &chan : *mChans)
- {
- const al::span<float> src{chan.mBuffer.data(), samplesToDo};
- chan.mFilter.processHfScale(src, chan.mHfScale);
- MixSamples(src, samplesOut, chan.Current, chan.Target, samplesToDo, 0);
- }
-}
+ if(mNumConvolveSegs < 1)
+ return;
-void ConvolutionFilter::update(al::span<FloatBufferLine> &outTarget, const ALCcontext *context,
- const ALeffectslot *slot, const EffectProps* /*props*/, const EffectTarget target)
-{
ALCdevice *device{context->mDevice.get()};
- mMix = &ConvolutionFilter::NormalMix;
+ mMix = &ConvolutionState::NormalMix;
/* The iFFT'd response is scaled up by the number of bins, so apply the
* inverse to the output mixing gain.
@@ -250,13 +273,13 @@ void ConvolutionFilter::update(al::span<FloatBufferLine> &outTarget, const ALCco
{
if(device->mAmbiOrder > mAmbiOrder)
{
- mMix = &ConvolutionFilter::UpsampleMix;
+ mMix = &ConvolutionState::UpsampleMix;
const auto scales = BFormatDec::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder);
chans[0].mHfScale = scales[0];
for(size_t i{1};i < chans.size();++i)
chans[i].mHfScale = scales[1];
}
- outTarget = target.Main->Buffer;
+ mOutTarget = target.Main->Buffer;
const auto &scales = GetAmbiScales(mAmbiScaling);
const uint8_t *index_map{(mChannels == FmtBFormat2D) ?
@@ -281,7 +304,7 @@ void ConvolutionFilter::update(al::span<FloatBufferLine> &outTarget, const ALCco
GetChannelIdxByName(*target.RealOut, FrontRight)};
if(lidx != INVALID_CHANNEL_INDEX && ridx != INVALID_CHANNEL_INDEX)
{
- outTarget = target.RealOut->Buffer;
+ mOutTarget = target.RealOut->Buffer;
chans[0].Target[lidx] = gain;
chans[1].Target[ridx] = gain;
}
@@ -290,7 +313,7 @@ void ConvolutionFilter::update(al::span<FloatBufferLine> &outTarget, const ALCco
const auto lcoeffs = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f}, 0.0f);
const auto rcoeffs = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}, 0.0f);
- outTarget = target.Main->Buffer;
+ mOutTarget = target.Main->Buffer;
ComputePanGains(target.Main, lcoeffs.data(), gain, chans[0].Target);
ComputePanGains(target.Main, rcoeffs.data(), gain, chans[1].Target);
}
@@ -299,14 +322,17 @@ void ConvolutionFilter::update(al::span<FloatBufferLine> &outTarget, const ALCco
{
const auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f);
- outTarget = target.Main->Buffer;
+ mOutTarget = target.Main->Buffer;
ComputePanGains(target.Main, coeffs.data(), gain, chans[0].Target);
}
}
-void ConvolutionFilter::process(const size_t samplesToDo,
+void ConvolutionState::process(const size_t samplesToDo,
const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
{
+ if(mNumConvolveSegs < 1)
+ return;
+
constexpr size_t m{ConvolveUpdateSize/2 + 1};
size_t curseg{mCurrentSegment};
auto &chans = *mChans;
@@ -384,54 +410,6 @@ void ConvolutionFilter::process(const size_t samplesToDo,
}
-struct ConvolutionState final : public EffectState {
- std::unique_ptr<ConvolutionFilter> mFilter;
-
- ConvolutionState() = default;
- ~ConvolutionState() override = default;
-
- void deviceUpdate(const ALCdevice *device) override;
- void setBuffer(const ALCdevice *device, const BufferStorage *buffer) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(ConvolutionState)
-};
-
-void ConvolutionState::deviceUpdate(const ALCdevice* /*device*/)
-{
-}
-
-void ConvolutionState::setBuffer(const ALCdevice *device, const BufferStorage *buffer)
-{
- mFilter = nullptr;
- /* An empty buffer doesn't need a convolution filter. */
- if(!buffer || buffer->mSampleLen < 1) return;
-
- auto numChannels = ChannelsFromFmt(buffer->mChannels,
- minu(buffer->mAmbiOrder, device->mAmbiOrder));
-
- mFilter.reset(new ConvolutionFilter{numChannels});
- if(!mFilter->init(device, *buffer))
- mFilter = nullptr;
-}
-
-
-void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slot,
- const EffectProps *props, const EffectTarget target)
-{
- if(mFilter)
- mFilter->update(mOutTarget, context, slot, props, target);
-}
-
-void ConvolutionState::process(const size_t samplesToDo,
- const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
-{
- if(mFilter)
- mFilter->process(samplesToDo, samplesIn, samplesOut);
-}
-
-
void ConvolutionEffect_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
{
switch(param)