82 void prepare(
int blockSize,
const T* irData,
int irLength)
84 assert(blockSize >= 2 && (blockSize & (blockSize - 1)) == 0);
86 assert(irData !=
nullptr);
87 if (irData ==
nullptr || irLength <= 0)
return;
93 while (bs < blockSize) bs <<= 1;
97 numBins_ = fftSize_ + 2;
100 fft_ = std::make_unique<FFTReal<T>>(fftSize_);
103 numPartitions_ = (irLength + bs - 1) / bs;
106 irPartitions_.resize(
static_cast<size_t>(numPartitions_));
107 std::vector<T> paddedBlock(
static_cast<size_t>(fftSize_), T(0));
109 for (
int p = 0; p < numPartitions_; ++p)
112 int remaining = std::min(bs, irLength - offset);
115 std::fill(paddedBlock.begin(), paddedBlock.end(), T(0));
116 std::copy_n(irData + offset, remaining, paddedBlock.begin());
119 irPartitions_[
static_cast<size_t>(p)].resize(
static_cast<size_t>(numBins_));
120 fft_->forward(paddedBlock.data(), irPartitions_[
static_cast<size_t>(p)].data());
124 inputBuffer_.assign(
static_cast<size_t>(fftSize_), T(0));
125 outputBuffer_.assign(
static_cast<size_t>(fftSize_), T(0));
126 overlapBuffer_.assign(
static_cast<size_t>(blockSize_), T(0));
127 fftAccum_.assign(
static_cast<size_t>(numBins_), T(0));
130 fdlBuffer_.resize(
static_cast<size_t>(numPartitions_));
131 for (
auto& fdl : fdlBuffer_)
132 fdl.assign(
static_cast<size_t>(numBins_), T(0));
141 std::fill(inputBuffer_.begin(), inputBuffer_.end(), T(0));
142 std::fill(outputBuffer_.begin(), outputBuffer_.end(), T(0));
143 std::fill(overlapBuffer_.begin(), overlapBuffer_.end(), T(0));
144 for (
auto& fdl : fdlBuffer_)
145 std::fill(fdl.begin(), fdl.end(), T(0));
162 void process(
const T* input, T* output,
int numSamples)
noexcept
167 std::copy_n(input, numSamples, output);
174 while (i < numSamples)
176 const int run = std::min(numSamples - i, blockSize_ - inputPos_);
181 std::copy_n(input + i, run,
182 inputBuffer_.begin() + blockSize_ + inputPos_);
183 std::copy_n(overlapBuffer_.begin() + inputPos_, run, output + i);
188 if (inputPos_ >= blockSize_)
190 processPartitionBlock();
193 std::copy_n(outputBuffer_.begin() + blockSize_,
static_cast<size_t>(blockSize_),
194 overlapBuffer_.begin());
207 process(data, data, numSamples);
240 if (buffer.getNumChannels() > 0 && buffer.getNumSamples() > 0)
245 [[nodiscard]]
int getLatency() const noexcept {
return blockSize_; }
263 void processPartitionBlock() noexcept
267 fft_->forward(inputBuffer_.data(),
268 fdlBuffer_[
static_cast<size_t>(fdlIndex_)].data());
271 std::fill(fftAccum_.begin(), fftAccum_.end(), T(0));
273 for (
int p = 0; p < numPartitions_; ++p)
275 int fdlIdx = (fdlIndex_ - p + numPartitions_) % numPartitions_;
276 const auto& irPart = irPartitions_[
static_cast<size_t>(p)];
277 const auto& fdlPart = fdlBuffer_[
static_cast<size_t>(fdlIdx)];
280 int bins = numBins_ / 2;
285 fft_->inverse(fftAccum_.data(), outputBuffer_.data());
288 std::copy_n(inputBuffer_.begin() + blockSize_,
static_cast<size_t>(blockSize_),
289 inputBuffer_.begin());
290 std::fill(inputBuffer_.begin() + blockSize_, inputBuffer_.end(), T(0));
293 fdlIndex_ = (fdlIndex_ + 1) % numPartitions_;
299 int numPartitions_ = 0;
303 std::unique_ptr<FFTReal<T>> fft_;
306 std::vector<std::vector<T>> irPartitions_;
309 std::vector<std::vector<T>> fdlBuffer_;
312 std::vector<T> inputBuffer_;
313 std::vector<T> outputBuffer_;
314 std::vector<T> overlapBuffer_;
315 std::vector<T> fftAccum_;
Owning audio buffer and non-owning view for real-time DSP processing.
Describes the audio processing environment (sample rate, block size, channels).
Fast Fourier Transform (Cooley-Tukey radix-2) with SIMD acceleration.
SIMD-accelerated buffer operations for real-time audio processing.
Non-owning view over audio channel data.
Real-time partitioned convolution using overlap-save with FFT.
int getNumPartitions() const noexcept
Returns the number of IR partitions.
void prepare(const AudioSpec &spec, const T *irData, int irLength)
Prepares with AudioSpec and IR data (unified API).
void process(const T *input, T *output, int numSamples) noexcept
Processes a block of audio through the convolver.
int getBlockSize() const noexcept
Returns the block size.
void reset() noexcept
Resets the convolution state (clears delay lines and buffers).
void processBlock(AudioBufferView< T > buffer) noexcept
Processes an audio buffer in-place (unified API).
int getLatency() const noexcept
Returns the processing latency in samples (= block size).
void prepare(int blockSize, const T *irData, int irLength)
Prepares the convolver with an impulse response.
void processInPlace(T *data, int numSamples) noexcept
Processes audio in-place (output overwrites input).
void complexMulAccum(float *DSPARK_RESTRICT accum, const float *DSPARK_RESTRICT a, const float *DSPARK_RESTRICT b, int bins) noexcept
Complex multiply-accumulate over interleaved [re, im, ...] spectra.
Main namespace for the DSPark framework.
Describes the audio environment for a DSP processor.
int maxBlockSize
Maximum number of samples per processing block.