Implement sfizz oversampler using new code
This commit is contained in:
parent
090619cfa4
commit
4b6497272d
1 changed files with 24 additions and 145 deletions
|
|
@ -5,6 +5,7 @@
|
||||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
#include "Oversampler.h"
|
#include "Oversampler.h"
|
||||||
|
#include "OversamplerHelpers.h"
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
#include "AudioSpan.h"
|
#include "AudioSpan.h"
|
||||||
#include "AudioReader.h"
|
#include "AudioReader.h"
|
||||||
|
|
@ -14,52 +15,6 @@
|
||||||
template <class T, std::size_t A = sfz::config::defaultAlignment>
|
template <class T, std::size_t A = sfz::config::defaultAlignment>
|
||||||
using aligned_vector = std::vector<T, jsl::aligned_allocator<T, A>>;
|
using aligned_vector = std::vector<T, jsl::aligned_allocator<T, A>>;
|
||||||
|
|
||||||
constexpr std::array<double, 12> coeffsStage2x {
|
|
||||||
0.036681502163648017,
|
|
||||||
0.13654762463195771,
|
|
||||||
0.27463175937945411,
|
|
||||||
0.42313861743656667,
|
|
||||||
0.56109869787919475,
|
|
||||||
0.67754004997416162,
|
|
||||||
0.76974183386322659,
|
|
||||||
0.83988962484963803,
|
|
||||||
0.89226081800387891,
|
|
||||||
0.9315419599631839,
|
|
||||||
0.96209454837808395,
|
|
||||||
0.98781637073289708
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr std::array<double, 4> coeffsStage4x {
|
|
||||||
0.042448989488488006,
|
|
||||||
0.17072114107630679,
|
|
||||||
0.39329183835224008,
|
|
||||||
0.74569514831986694
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr std::array<double, 3> coeffsStage8x {
|
|
||||||
0.055748680811302048,
|
|
||||||
0.24305119574153092,
|
|
||||||
0.6466991311926823
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#if SFIZZ_HAVE_SSE
|
|
||||||
#include "hiir/Upsampler2xSse.h"
|
|
||||||
using Upsampler2x = hiir::Upsampler2xSse<coeffsStage2x.size()>;
|
|
||||||
using Upsampler4x = hiir::Upsampler2xSse<coeffsStage4x.size()>;
|
|
||||||
using Upsampler8x = hiir::Upsampler2xSse<coeffsStage8x.size()>;
|
|
||||||
#elif SFIZZ_HAVE_NEON
|
|
||||||
#include "hiir/Upsampler2xNeon.h"
|
|
||||||
using Upsampler2x = hiir::Upsampler2xNeon<coeffsStage2x.size()>;
|
|
||||||
using Upsampler4x = hiir::Upsampler2xNeon<coeffsStage4x.size()>;
|
|
||||||
using Upsampler8x = hiir::Upsampler2xNeon<coeffsStage8x.size()>;
|
|
||||||
#else
|
|
||||||
#include "hiir/Upsampler2xFpu.h"
|
|
||||||
using Upsampler2x = hiir::Upsampler2xFpu<coeffsStage2x.size()>;
|
|
||||||
using Upsampler4x = hiir::Upsampler2xFpu<coeffsStage4x.size()>;
|
|
||||||
using Upsampler8x = hiir::Upsampler2xFpu<coeffsStage8x.size()>;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
sfz::Oversampler::Oversampler(sfz::Oversampling factor, size_t chunkSize)
|
sfz::Oversampler::Oversampler(sfz::Oversampling factor, size_t chunkSize)
|
||||||
: factor(factor), chunkSize(chunkSize)
|
: factor(factor), chunkSize(chunkSize)
|
||||||
{
|
{
|
||||||
|
|
@ -74,36 +29,10 @@ void sfz::Oversampler::stream(AudioSpan<float> input, AudioSpan<float> output, s
|
||||||
const auto numFrames = input.getNumFrames();
|
const auto numFrames = input.getNumFrames();
|
||||||
const auto numChannels = input.getNumChannels();
|
const auto numChannels = input.getNumChannels();
|
||||||
|
|
||||||
aligned_vector<Upsampler2x> upsampler2x;
|
aligned_vector<Upsampler> upsampler(numChannels);
|
||||||
aligned_vector<Upsampler4x> upsampler4x;
|
|
||||||
aligned_vector<Upsampler8x> upsampler8x;
|
|
||||||
|
|
||||||
switch(factor)
|
// Intermediate buffer
|
||||||
{
|
sfz::Buffer<float> temp { std::max<size_t>(128, Upsampler::recommendedBuffer(16, chunkSize)) };
|
||||||
case Oversampling::x8:
|
|
||||||
upsampler8x.resize(numChannels);
|
|
||||||
for (auto& upsampler: upsampler8x)
|
|
||||||
upsampler.set_coefs(coeffsStage8x.data());
|
|
||||||
// fallthrough
|
|
||||||
case Oversampling::x4:
|
|
||||||
upsampler4x.resize(numChannels);
|
|
||||||
for (auto& upsampler: upsampler4x)
|
|
||||||
upsampler.set_coefs(coeffsStage4x.data());
|
|
||||||
// fallthrough
|
|
||||||
case Oversampling::x2:
|
|
||||||
upsampler2x.resize(numChannels);
|
|
||||||
for (auto& upsampler: upsampler2x)
|
|
||||||
upsampler.set_coefs(coeffsStage2x.data());
|
|
||||||
break;
|
|
||||||
case Oversampling::x1:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intermediate buffers
|
|
||||||
sfz::Buffer<float> buffer1 { chunkSize * 2 };
|
|
||||||
sfz::Buffer<float> buffer2 { chunkSize * 4 };
|
|
||||||
auto span1 = absl::MakeSpan(buffer1);
|
|
||||||
auto span2 = absl::MakeSpan(buffer2);
|
|
||||||
|
|
||||||
size_t inputFrameCounter { 0 };
|
size_t inputFrameCounter { 0 };
|
||||||
size_t outputFrameCounter { 0 };
|
size_t outputFrameCounter { 0 };
|
||||||
|
|
@ -115,23 +44,10 @@ void sfz::Oversampler::stream(AudioSpan<float> input, AudioSpan<float> output, s
|
||||||
for (size_t chanIdx = 0; chanIdx < numChannels; chanIdx++) {
|
for (size_t chanIdx = 0; chanIdx < numChannels; chanIdx++) {
|
||||||
const auto inputChunk = input.getSpan(chanIdx).subspan(inputFrameCounter, thisChunkSize);
|
const auto inputChunk = input.getSpan(chanIdx).subspan(inputFrameCounter, thisChunkSize);
|
||||||
const auto outputChunk = output.getSpan(chanIdx).subspan(outputFrameCounter, outputChunkSize);
|
const auto outputChunk = output.getSpan(chanIdx).subspan(outputFrameCounter, outputChunkSize);
|
||||||
switch (factor) {
|
upsampler[chanIdx].process(
|
||||||
case Oversampling::x1:
|
static_cast<int>(factor),
|
||||||
copy<float>(inputChunk, outputChunk);
|
inputChunk.data(), outputChunk.data(), static_cast<int>(inputChunk.size()),
|
||||||
break;
|
temp.data(), static_cast<int>(temp.size()));
|
||||||
case Oversampling::x2:
|
|
||||||
upsampler2x[chanIdx].process_block(outputChunk.data(), inputChunk.data(), static_cast<long>(thisChunkSize));
|
|
||||||
break;
|
|
||||||
case Oversampling::x4:
|
|
||||||
upsampler2x[chanIdx].process_block(span1.data(), inputChunk.data(), static_cast<long>(thisChunkSize));
|
|
||||||
upsampler4x[chanIdx].process_block(outputChunk.data(), span1.data(), static_cast<long>(thisChunkSize * 2));
|
|
||||||
break;
|
|
||||||
case Oversampling::x8:
|
|
||||||
upsampler2x[chanIdx].process_block(span1.data(), inputChunk.data(), static_cast<long>(thisChunkSize));
|
|
||||||
upsampler4x[chanIdx].process_block(span2.data(), span1.data(), static_cast<long>(thisChunkSize * 2));
|
|
||||||
upsampler8x[chanIdx].process_block(outputChunk.data(), span2.data(), static_cast<long>(thisChunkSize * 4));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
inputFrameCounter += thisChunkSize;
|
inputFrameCounter += thisChunkSize;
|
||||||
outputFrameCounter += outputChunkSize;
|
outputFrameCounter += outputChunkSize;
|
||||||
|
|
@ -149,47 +65,18 @@ void sfz::Oversampler::stream(AudioReader& input, AudioSpan<float> output, std::
|
||||||
const auto numFrames = static_cast<size_t>(input.frames());
|
const auto numFrames = static_cast<size_t>(input.frames());
|
||||||
const auto numChannels = input.channels();
|
const auto numChannels = input.channels();
|
||||||
|
|
||||||
aligned_vector<Upsampler2x> upsampler2x;
|
aligned_vector<Upsampler> upsampler(numChannels);
|
||||||
aligned_vector<Upsampler4x> upsampler4x;
|
|
||||||
aligned_vector<Upsampler8x> upsampler8x;
|
|
||||||
|
|
||||||
switch(factor)
|
|
||||||
{
|
|
||||||
case Oversampling::x8:
|
|
||||||
upsampler8x.resize(numChannels);
|
|
||||||
for (auto& upsampler: upsampler8x)
|
|
||||||
upsampler.set_coefs(coeffsStage8x.data());
|
|
||||||
// fallthrough
|
|
||||||
case Oversampling::x4:
|
|
||||||
upsampler4x.resize(numChannels);
|
|
||||||
for (auto& upsampler: upsampler4x)
|
|
||||||
upsampler.set_coefs(coeffsStage4x.data());
|
|
||||||
// fallthrough
|
|
||||||
case Oversampling::x2:
|
|
||||||
upsampler2x.resize(numChannels);
|
|
||||||
for (auto& upsampler: upsampler2x)
|
|
||||||
upsampler.set_coefs(coeffsStage2x.data());
|
|
||||||
break;
|
|
||||||
case Oversampling::x1:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intermediate buffers
|
// Intermediate buffers
|
||||||
sfz::Buffer<float> fileBlock { chunkSize * numChannels };
|
sfz::Buffer<float> fileBlock { chunkSize * numChannels };
|
||||||
sfz::Buffer<float> buffer1 { chunkSize * 2 };
|
sfz::Buffer<float> channelBlock { chunkSize };
|
||||||
sfz::Buffer<float> buffer2 { chunkSize * 4 };
|
sfz::Buffer<float> temp { std::max<size_t>(128, Upsampler::recommendedBuffer(16, chunkSize)) };
|
||||||
auto span1 = absl::MakeSpan(buffer1);
|
|
||||||
auto span2 = absl::MakeSpan(buffer2);
|
|
||||||
|
|
||||||
auto upsample2xFromInterleaved = [numChannels](
|
auto deinterleave = [numChannels](
|
||||||
Upsampler2x& upsampler, float* output, const float* input,
|
float* output, const float* input, size_t numFrames, unsigned chanIdx)
|
||||||
size_t numInputFrames, unsigned chanIdx)
|
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < numInputFrames; ++i) {
|
for (size_t i = 0; i < numFrames; ++i)
|
||||||
float* outp = &output[2 * i];
|
output[i] = input[i * numChannels + chanIdx];
|
||||||
const float* inp = &input[i * numChannels + chanIdx];
|
|
||||||
upsampler.process_sample(outp[0], outp[1], inp[0]);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t inputFrameCounter { 0 };
|
size_t inputFrameCounter { 0 };
|
||||||
|
|
@ -211,23 +98,15 @@ void sfz::Oversampler::stream(AudioReader& input, AudioSpan<float> output, std::
|
||||||
|
|
||||||
for (size_t chanIdx = 0; chanIdx < numChannels; chanIdx++) {
|
for (size_t chanIdx = 0; chanIdx < numChannels; chanIdx++) {
|
||||||
const auto outputChunk = output.getSpan(chanIdx).subspan(outputFrameCounter, outputChunkSize);
|
const auto outputChunk = output.getSpan(chanIdx).subspan(outputFrameCounter, outputChunkSize);
|
||||||
switch (factor) {
|
|
||||||
case Oversampling::x1:
|
if (factor == Oversampling::x1)
|
||||||
for (size_t i = 0; i < thisChunkSize; ++i)
|
deinterleave(outputChunk.data(), fileBlock.data(), thisChunkSize, chanIdx);
|
||||||
outputChunk[i] = fileBlock[i * numChannels + chanIdx];
|
else {
|
||||||
break;
|
deinterleave(channelBlock.data(), fileBlock.data(), thisChunkSize, chanIdx);
|
||||||
case Oversampling::x2:
|
upsampler[chanIdx].process(
|
||||||
upsample2xFromInterleaved(upsampler2x[chanIdx], outputChunk.data(), fileBlock.data(), thisChunkSize, chanIdx);
|
static_cast<int>(factor),
|
||||||
break;
|
channelBlock.data(), outputChunk.data(), static_cast<int>(thisChunkSize),
|
||||||
case Oversampling::x4:
|
temp.data(), static_cast<int>(temp.size()));
|
||||||
upsample2xFromInterleaved(upsampler2x[chanIdx], span1.data(), fileBlock.data(), thisChunkSize, chanIdx);
|
|
||||||
upsampler4x[chanIdx].process_block(outputChunk.data(), span1.data(), static_cast<long>(thisChunkSize * 2));
|
|
||||||
break;
|
|
||||||
case Oversampling::x8:
|
|
||||||
upsample2xFromInterleaved(upsampler2x[chanIdx], span1.data(), fileBlock.data(), thisChunkSize, chanIdx);
|
|
||||||
upsampler4x[chanIdx].process_block(span2.data(), span1.data(), static_cast<long>(thisChunkSize * 2));
|
|
||||||
upsampler8x[chanIdx].process_block(outputChunk.data(), span2.data(), static_cast<long>(thisChunkSize * 4));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inputFrameCounter += thisChunkSize;
|
inputFrameCounter += thisChunkSize;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue