commit
21294e7d4a
19 changed files with 1575 additions and 227 deletions
|
|
@ -105,6 +105,12 @@ add_library(sfizz_hiir INTERFACE)
|
|||
add_library(sfizz::hiir ALIAS sfizz_hiir)
|
||||
target_include_directories(sfizz_hiir INTERFACE "src/external/hiir")
|
||||
|
||||
# The hiir filter designer
|
||||
add_library(sfizz_hiir_polyphase_iir2designer STATIC
|
||||
"src/external/hiir/hiir/PolyphaseIir2Designer.cpp")
|
||||
add_library(sfizz::hiir_polyphase_iir2designer ALIAS sfizz_hiir_polyphase_iir2designer)
|
||||
target_link_libraries(sfizz_hiir_polyphase_iir2designer PUBLIC sfizz::hiir)
|
||||
|
||||
# The kissfft library
|
||||
add_library(sfizz_kissfft STATIC
|
||||
"src/external/kiss_fft/kiss_fft.c"
|
||||
|
|
|
|||
|
|
@ -10,3 +10,6 @@ endif()
|
|||
|
||||
add_executable(sfizz_preprocessor Preprocessor.cpp)
|
||||
target_link_libraries(sfizz_preprocessor PRIVATE sfizz::parser sfizz::pugixml sfizz::cxxopts)
|
||||
|
||||
add_executable(sfizz_hiir_designer HIIRDesigner.cpp)
|
||||
target_link_libraries(sfizz_hiir_designer PRIVATE sfizz::hiir_polyphase_iir2designer)
|
||||
|
|
|
|||
403
devtools/HIIRDesigner.cpp
Normal file
403
devtools/HIIRDesigner.cpp
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include <hiir/PolyphaseIir2Designer.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
using FD = hiir::PolyphaseIir2Designer;
|
||||
|
||||
struct Stage {
|
||||
int factor;
|
||||
double tbw;
|
||||
int nbr_coefs;
|
||||
std::unique_ptr<double[]> coefs;
|
||||
};
|
||||
|
||||
static std::vector<Stage> calculate_stages(int oversampling, double attenuation, double transition);
|
||||
static void generate_cpp_prologue(int argc, char *argv[]);
|
||||
static void generate_cpp_epilogue();
|
||||
static void generate_cpp_coefs(const Stage *stages, int num_stages);
|
||||
static void generate_cpp_upsampler(const Stage *stages, int num_stages);
|
||||
static void generate_cpp_downsampler(const Stage *stages, int num_stages);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
double attenuation = 0.0;
|
||||
double transition = 0.0;
|
||||
int oversampling = 16;
|
||||
bool have_a = false;
|
||||
bool have_t = false;
|
||||
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
const char *arg = argv[argi];
|
||||
if (!strcmp(arg, "-a")) {
|
||||
if (++argi >= argc) {
|
||||
fprintf(stderr, "The option %s expects a value.\n", arg);
|
||||
return 1;
|
||||
}
|
||||
arg = argv[argi];
|
||||
attenuation = atof(arg);
|
||||
have_a = true;
|
||||
}
|
||||
else if (!strcmp(arg, "-t")) {
|
||||
if (++argi >= argc) {
|
||||
fprintf(stderr, "The option %s expects a value.\n", arg);
|
||||
return 1;
|
||||
}
|
||||
arg = argv[argi];
|
||||
transition = atof(arg);
|
||||
have_t = true;
|
||||
}
|
||||
else if (!strcmp(arg, "-o")) {
|
||||
if (++argi >= argc) {
|
||||
fprintf(stderr, "The option %s expects a value.\n", arg);
|
||||
return 1;
|
||||
}
|
||||
arg = argv[argi];
|
||||
oversampling = atoi(arg);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Unrecognized argument: %s\n", arg);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!have_a) {
|
||||
fprintf(stderr, "No attenuation given (-a)\n");
|
||||
return 1;
|
||||
}
|
||||
if (!have_t) {
|
||||
fprintf(stderr, "No transition bandwidth given (-t)\n");
|
||||
return 1;
|
||||
}
|
||||
else if (attenuation < 0.0) {
|
||||
fprintf(stderr, "Invalid attenuation\n");
|
||||
return 1;
|
||||
}
|
||||
else if (transition <= 0.0 || transition >= 0.5) {
|
||||
fprintf(stderr, "Invalid transition bandwidth\n");
|
||||
return 1;
|
||||
}
|
||||
else if (oversampling < 2) {
|
||||
fprintf(stderr, "Invalid oversampling\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<Stage> stages = calculate_stages(oversampling, attenuation, transition);
|
||||
int num_stages = (int)stages.size();
|
||||
|
||||
// generate the coeffs
|
||||
generate_cpp_prologue(argc, argv);
|
||||
printf("\n");
|
||||
generate_cpp_coefs(stages.data(), num_stages);
|
||||
printf("\n");
|
||||
generate_cpp_upsampler(stages.data(), num_stages);
|
||||
printf("\n");
|
||||
generate_cpp_downsampler(stages.data(), num_stages);
|
||||
printf("\n");
|
||||
generate_cpp_epilogue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::vector<Stage> calculate_stages(int oversampling, double attenuation, double transition)
|
||||
{
|
||||
std::vector<Stage> stages;
|
||||
stages.reserve(8);
|
||||
|
||||
bool done = false;
|
||||
for (int num_stage = 0; !done; ++num_stage) {
|
||||
if (num_stage > 0)
|
||||
printf("\n");
|
||||
|
||||
Stage stage;
|
||||
stage.factor = 2 << num_stage;
|
||||
stage.tbw = transition *
|
||||
std::pow(0.5, num_stage) + 0.5 * (1 - std::pow(0.5, num_stage));
|
||||
|
||||
stage.nbr_coefs = FD::compute_nbr_coefs_from_proto(attenuation, stage.tbw);
|
||||
double *coefs = new double[stage.nbr_coefs]{};
|
||||
stage.coefs.reset(coefs);
|
||||
|
||||
FD::compute_coefs(coefs, attenuation, stage.tbw);
|
||||
|
||||
done = stage.factor >= oversampling;
|
||||
|
||||
stages.push_back(std::move(stage));
|
||||
}
|
||||
|
||||
return stages;
|
||||
}
|
||||
|
||||
static void generate_cpp_prologue(int argc, char *argv[])
|
||||
{
|
||||
printf("//------------------------------------------------------------------------------\n");
|
||||
printf("// This is generated by the Sfizz HIIR designer\n");
|
||||
printf("// Using options:");
|
||||
for (int i = 1; i < argc; ++i)
|
||||
printf(" %s", argv[i]);
|
||||
printf("\n");
|
||||
printf("//------------------------------------------------------------------------------\n");
|
||||
|
||||
printf("\n");
|
||||
|
||||
printf(
|
||||
"#pragma once\n"
|
||||
"#include \"OversamplerHelpers.h\"\n"
|
||||
"\n"
|
||||
"namespace sfz {\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void generate_cpp_epilogue()
|
||||
{
|
||||
printf("} // namespace sfz\n");
|
||||
}
|
||||
|
||||
static void generate_cpp_coefs(const Stage *stages, int num_stages)
|
||||
{
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[i];
|
||||
const double *coefs = stage.coefs.get();
|
||||
printf("// %dx <-> %dx: TBW = %g\n", stage.factor, stage.factor / 2, stage.tbw);
|
||||
printf("static constexpr double OSCoeffs%dx[%d] = {\n", stage.factor, stage.nbr_coefs);
|
||||
for (int i = 0; i < stage.nbr_coefs; ++i) {
|
||||
printf("\t" "%.18f,\n", coefs[i]);
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_cpp_upsampler(const Stage *stages, int num_stages)
|
||||
{
|
||||
printf("class Upsampler {\n");
|
||||
printf("public:\n");
|
||||
|
||||
printf("\t" "Upsampler()\n");
|
||||
printf("\t" "{\n");
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[i];
|
||||
printf("\t\t" "up%d_.set_coefs(OSCoeffs%dx);\n", stage.factor, stage.factor);
|
||||
}
|
||||
printf("\t" "}\n");
|
||||
|
||||
printf("\t" "void clear()\n");
|
||||
printf("\t" "{\n");
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[i];
|
||||
printf("\t\t" "up%d_.clear_buffers();\n", stage.factor);
|
||||
}
|
||||
printf("\t" "}\n");
|
||||
|
||||
printf("\t" "static int recommendedBuffer(int factor, int spl)\n");
|
||||
printf("\t" "{\n");
|
||||
printf("\t\t" "switch (factor) {\n");
|
||||
printf("\t\t" "case 2:\n");
|
||||
printf("\t\t\t" "return 0;\n");
|
||||
printf("\t\t" "case 4:\n");
|
||||
printf("\t\t\t" "return 2 * spl;\n");
|
||||
printf("\t\t" "default:\n");
|
||||
printf("\t\t\t" "return factor * spl;\n");
|
||||
printf("\t\t" "}\n");
|
||||
printf("\t" "}\n");
|
||||
|
||||
printf("\t" "static bool canProcess(int factor)\n");
|
||||
printf("\t" "{\n");
|
||||
printf("\t\t" "switch (factor) {\n");
|
||||
printf("\t\t" "case 1:\n");
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[i];
|
||||
printf("\t\t" "case %d:\n", stage.factor);
|
||||
}
|
||||
printf("\t\t\t" "return true;\n");
|
||||
printf("\t\t" "default:\n");
|
||||
printf("\t\t\t" "return false;\n");
|
||||
printf("\t\t" "}\n");
|
||||
printf("\t" "}\n");
|
||||
|
||||
printf("\t" "void process(int factor, const float *in, float *out, int spl, float *temp, int ntemp)\n");
|
||||
printf("\t" "{\n");
|
||||
printf("\t\t" "switch (factor) {\n");
|
||||
printf("\t\t" "case 1:\n");
|
||||
printf("\t\t\t" "if (in != out) std::memcpy(out, in, spl * sizeof(float));\n");
|
||||
printf("\t\t\t" "break;\n");
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[i];
|
||||
printf("\t\t" "case %d:\n", stage.factor);
|
||||
printf("\t\t\t" "process%dx(in, out, spl, temp, ntemp);\n", stage.factor);
|
||||
printf("\t\t\t" "break;\n");
|
||||
}
|
||||
printf("\t\t" "default:\n");
|
||||
printf("\t\t\t" "ASSERTFALSE;\n");
|
||||
printf("\t\t\t" "break;\n");
|
||||
printf("\t\t" "}\n");
|
||||
printf("\t" "}\n");
|
||||
|
||||
for (int n = 1; n <= num_stages; ++n) {
|
||||
// special case factor=2, buffer not required
|
||||
if (stages[n - 1].factor == 2) {
|
||||
printf("\t" "void process2x(const float *in, float *out, int spl, float * = nullptr, int = 0)\n");
|
||||
printf("\t" "{\n");
|
||||
printf("\t\t" "up2_.process_block(out, in, spl);\n");
|
||||
printf("\t" "}\n");
|
||||
continue;
|
||||
}
|
||||
printf("\t" "void process%dx(const float *in, float *out, int spl, float *temp, int ntemp)\n", stages[n - 1].factor);
|
||||
printf("\t" "{\n");
|
||||
// special case factor=4, only 1 buffer required
|
||||
if (stages[n - 1].factor > 4)
|
||||
printf("\t\t" "int maxspl = ntemp / %d;\n", stages[n - 1].factor);
|
||||
else
|
||||
printf("\t\t" "int maxspl = ntemp / %d;\n", stages[n - 1].factor / 2);
|
||||
printf("\t\t" "ASSERT(maxspl > 0);\n");
|
||||
printf("\t\t" "float *t1 = temp;\n");
|
||||
if (stages[n - 1].factor > 4)
|
||||
printf("\t\t" "float *t2 = temp + %d * maxspl;\n", stages[n - 1].factor / 2);
|
||||
printf("\t\t" "while (spl > 0) {\n");
|
||||
printf("\t\t\t" "int curspl = (spl < maxspl) ? spl : maxspl;\n");
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const Stage &stage = stages[i];
|
||||
const char *tempnames[] = {"t1", "t2"};
|
||||
const char *outname = tempnames[i & 1];
|
||||
const char *inname = tempnames[1 - (i & 1)];
|
||||
if (i == 0)
|
||||
inname = "in";
|
||||
if (i + 1 == n)
|
||||
outname = "out";
|
||||
printf("\t\t\t" "up%d_.process_block(%s, %s, %d * curspl);\n", stage.factor, outname, inname, stage.factor / 2);
|
||||
}
|
||||
printf("\t\t\t" "in += curspl;\n");
|
||||
printf("\t\t\t" "out += curspl;\n");
|
||||
printf("\t\t\t" "spl -= curspl;\n");
|
||||
printf("\t\t" "}\n");
|
||||
printf("\t" "}\n");
|
||||
}
|
||||
|
||||
printf("private:\n");
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[i];
|
||||
printf("\t" "hiir::Upsampler2x<%d> up%d_;\n", stage.nbr_coefs, stage.factor);
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
static void generate_cpp_downsampler(const Stage *stages, int num_stages)
|
||||
{
|
||||
printf("class Downsampler {\n");
|
||||
printf("public:\n");
|
||||
|
||||
printf("\t" "Downsampler()\n");
|
||||
printf("\t" "{\n");
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[num_stages - 1 - i];
|
||||
printf("\t\t" "down%d_.set_coefs(OSCoeffs%dx);\n", stage.factor, stage.factor);
|
||||
}
|
||||
printf("\t" "}\n");
|
||||
|
||||
printf("\t" "void clear()\n");
|
||||
printf("\t" "{\n");
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[num_stages - 1 - i];
|
||||
printf("\t\t" "down%d_.clear_buffers();\n", stage.factor);
|
||||
}
|
||||
printf("\t" "}\n");
|
||||
|
||||
printf("\t" "static int recommendedBuffer(int factor, int spl)\n");
|
||||
printf("\t" "{\n");
|
||||
printf("\t\t" "switch (factor) {\n");
|
||||
printf("\t\t" "case 2:\n");
|
||||
printf("\t\t\t" "return 0;\n");
|
||||
printf("\t\t" "case 4:\n");
|
||||
printf("\t\t\t" "return 2 * spl;\n");
|
||||
printf("\t\t" "default:\n");
|
||||
printf("\t\t\t" "return factor * spl;\n");
|
||||
printf("\t\t" "}\n");
|
||||
printf("\t" "}\n");
|
||||
|
||||
printf("\t" "static bool canProcess(int factor)\n");
|
||||
printf("\t" "{\n");
|
||||
printf("\t\t" "switch (factor) {\n");
|
||||
printf("\t\t" "case 1:\n");
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[i];
|
||||
printf("\t\t" "case %d:\n", stage.factor);
|
||||
}
|
||||
printf("\t\t\t" "return true;\n");
|
||||
printf("\t\t" "default:\n");
|
||||
printf("\t\t\t" "return false;\n");
|
||||
printf("\t\t" "}\n");
|
||||
printf("\t" "}\n");
|
||||
|
||||
printf("\t" "void process(int factor, const float *in, float *out, int spl, float *temp, int ntemp)\n");
|
||||
printf("\t" "{\n");
|
||||
printf("\t\t" "switch (factor) {\n");
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[num_stages - 1 - i];
|
||||
printf("\t\t" "case %d:\n", stage.factor);
|
||||
printf("\t\t\t" "process%dx(in, out, spl, temp, ntemp);\n", stage.factor);
|
||||
printf("\t\t\t" "break;\n");
|
||||
}
|
||||
printf("\t\t" "case 1:\n");
|
||||
printf("\t\t\t" "if (in != out) std::memcpy(out, in, spl * sizeof(float));\n");
|
||||
printf("\t\t\t" "break;\n");
|
||||
printf("\t\t" "default:\n");
|
||||
printf("\t\t\t" "ASSERTFALSE;\n");
|
||||
printf("\t\t\t" "break;\n");
|
||||
printf("\t\t" "}\n");
|
||||
printf("\t" "}\n");
|
||||
|
||||
for (int n = 1; n <= num_stages; ++n) {
|
||||
// special case factor=2, buffer not required
|
||||
if (stages[n - 1].factor == 2) {
|
||||
printf("\t" "void process2x(const float *in, float *out, int spl, float * = nullptr, int = 0)\n");
|
||||
printf("\t" "{\n");
|
||||
printf("\t\t" "down2_.process_block(out, in, spl);\n");
|
||||
printf("\t" "}\n");
|
||||
continue;
|
||||
}
|
||||
printf("\t" "void process%dx(const float *in, float *out, int spl, float *temp, int ntemp)\n", stages[n - 1].factor);
|
||||
printf("\t" "{\n");
|
||||
// special case factor=4, only 1 buffer required
|
||||
if (stages[n - 1].factor > 4)
|
||||
printf("\t\t" "int maxspl = ntemp / %d;\n", stages[n - 1].factor);
|
||||
else
|
||||
printf("\t\t" "int maxspl = ntemp / %d;\n", stages[n - 1].factor / 2);
|
||||
printf("\t\t" "ASSERT(maxspl > 0);\n");
|
||||
printf("\t\t" "float *t1 = temp;\n");
|
||||
if (stages[n - 1].factor > 4)
|
||||
printf("\t\t" "float *t2 = temp + %d * maxspl;\n", stages[n - 1].factor / 2);
|
||||
printf("\t\t" "while (spl > 0) {\n");
|
||||
printf("\t\t\t" "int curspl = (spl < maxspl) ? spl : maxspl;\n");
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const Stage &stage = stages[n - 1 - i];
|
||||
const char *tempnames[] = {"t1", "t2"};
|
||||
const char *outname = tempnames[i & 1];
|
||||
const char *inname = tempnames[1 - (i & 1)];
|
||||
if (i == 0)
|
||||
inname = "in";
|
||||
if (i + 1 == n)
|
||||
outname = "out";
|
||||
printf("\t\t\t" "down%d_.process_block(%s, %s, %d * curspl);\n", stage.factor, outname, inname, stage.factor / 2);
|
||||
}
|
||||
printf("\t\t\t" "in += curspl;\n");
|
||||
printf("\t\t\t" "out += curspl;\n");
|
||||
printf("\t\t\t" "spl -= curspl;\n");
|
||||
printf("\t\t" "}\n");
|
||||
printf("\t" "}\n");
|
||||
}
|
||||
|
||||
printf("private:\n");
|
||||
for (int i = 0; i < num_stages; ++i) {
|
||||
const Stage &stage = stages[num_stages - 1 - i];
|
||||
printf("\t" "hiir::Downsampler2x<%d> down%d_;\n", stage.nbr_coefs, stage.factor);
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
444
src/external/hiir/hiir/PolyphaseIir2Designer.cpp
vendored
Normal file
444
src/external/hiir/hiir/PolyphaseIir2Designer.cpp
vendored
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
/*****************************************************************************
|
||||
|
||||
PolyphaseIir2Designer.cpp
|
||||
Author: Laurent de Soras, 2005
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma warning (1 : 4130) // "'operator' : logical operation on address of string constant"
|
||||
#pragma warning (1 : 4223) // "nonstandard extension used : non-lvalue array converted to pointer"
|
||||
#pragma warning (1 : 4705) // "statement has no effect"
|
||||
#pragma warning (1 : 4706) // "assignment within conditional expression"
|
||||
#pragma warning (4 : 4786) // "identifier was truncated to '255' characters in the debug information"
|
||||
#pragma warning (4 : 4800) // "forcing value to bool 'true' or 'false' (performance warning)"
|
||||
#pragma warning (4 : 4355) // "'this' : used in base member initializer list"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/def.h"
|
||||
#include "hiir/fnc.h"
|
||||
#include "hiir/PolyphaseIir2Designer.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: compute_nbr_coefs_from_proto
|
||||
Description:
|
||||
Finds the minimum number of coefficients for a given filter specification
|
||||
Input parameters:
|
||||
- attenuation: stopband attenuation, dB. > 0.
|
||||
- transition: normalized transition bandwith. Range ]0 ; 1/2[
|
||||
Returns: Number of coefficients, > 0
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
int PolyphaseIir2Designer::compute_nbr_coefs_from_proto (double attenuation, double transition)
|
||||
{
|
||||
assert (attenuation > 0);
|
||||
assert (transition > 0);
|
||||
assert (transition < 0.5);
|
||||
|
||||
double k;
|
||||
double q;
|
||||
compute_transition_param (k, q, transition);
|
||||
const int order = compute_order (attenuation, q);
|
||||
const int nbr_coefs = (order - 1) / 2;
|
||||
|
||||
return nbr_coefs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: compute_atten_from_order_tbw
|
||||
Description:
|
||||
Compute the attenuation correspounding to a given number of coefficients
|
||||
and the transition bandwith.
|
||||
Input parameters:
|
||||
- nbr_coefs: Number of desired coefficients. > 0.
|
||||
- transition: normalized transition bandwith. Range ]0 ; 1/2[
|
||||
Returns: stopband attenuation, dB. > 0.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
double PolyphaseIir2Designer::compute_atten_from_order_tbw (int nbr_coefs, double transition)
|
||||
{
|
||||
assert (nbr_coefs > 0);
|
||||
assert (transition > 0);
|
||||
assert (transition < 0.5);
|
||||
|
||||
double k;
|
||||
double q;
|
||||
compute_transition_param (k, q, transition);
|
||||
const int order = nbr_coefs * 2 + 1;
|
||||
const double attenuation = compute_atten (q, order);
|
||||
|
||||
return attenuation;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: compute_coefs
|
||||
Description:
|
||||
Computes coefficients for a half-band polyphase IIR filter, function of a
|
||||
given stopband gain / transition bandwidth specification.
|
||||
Order is automatically calculated.
|
||||
Input parameters:
|
||||
- attenuation: stopband attenuation, dB. > 0.
|
||||
- transition: normalized transition bandwith. Range ]0 ; 1/2[
|
||||
Output parameters:
|
||||
- coef_arr: Coefficient list, must be large enough to store all the
|
||||
coefficients. Filter order = nbr_coefs * 2 + 1
|
||||
Returns: number of coefficients
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
int PolyphaseIir2Designer::compute_coefs (double coef_arr [], double attenuation, double transition)
|
||||
{
|
||||
assert (attenuation > 0);
|
||||
assert (transition > 0);
|
||||
assert (transition < 0.5);
|
||||
|
||||
double k;
|
||||
double q;
|
||||
compute_transition_param (k, q, transition);
|
||||
|
||||
// Computes number of required coefficients
|
||||
const int order = compute_order (attenuation, q);
|
||||
const int nbr_coefs = (order - 1) / 2;
|
||||
|
||||
// Coefficient calculation
|
||||
for (int index = 0; index < nbr_coefs; ++index)
|
||||
{
|
||||
coef_arr [index] = compute_coef (index, k, q, order);
|
||||
}
|
||||
|
||||
return nbr_coefs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: compute_coefs_spec_order_tbw
|
||||
Description:
|
||||
Computes coefficients for a half-band polyphase IIR filter, function of a
|
||||
given transition bandwidth and desired filter order. Bandstop attenuation
|
||||
is set to the maximum value for these constraints.
|
||||
Input parameters:
|
||||
- nbr_coefs: Number of desired coefficients. > 0.
|
||||
- transition: normalized transition bandwith. Range ]0 ; 1/2[
|
||||
Output parameters:
|
||||
- coef_arr: Coefficient list, must be large enough to store all the
|
||||
coefficients.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
void PolyphaseIir2Designer::compute_coefs_spec_order_tbw (double coef_arr [], int nbr_coefs, double transition)
|
||||
{
|
||||
assert (nbr_coefs > 0);
|
||||
assert (transition > 0);
|
||||
assert (transition < 0.5);
|
||||
|
||||
double k;
|
||||
double q;
|
||||
compute_transition_param (k, q, transition);
|
||||
const int order = nbr_coefs * 2 + 1;
|
||||
|
||||
// Coefficient calculation
|
||||
for (int index = 0; index < nbr_coefs; ++index)
|
||||
{
|
||||
coef_arr [index] = compute_coef (index, k, q, order);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: compute_phase_delay
|
||||
Description:
|
||||
Computes the phase delay introduced by a single filtering unit at a
|
||||
specified frequency.
|
||||
The delay is given for a constant sampling rate between input and output.
|
||||
Input parameters:
|
||||
- a: coefficient for the cell, [0 ; 1]
|
||||
- f_fs: frequency relative to the sampling rate, [0 ; 0.5].
|
||||
Returns:
|
||||
The phase delay in samples, >= 0.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
double PolyphaseIir2Designer::compute_phase_delay (double a, double f_fs)
|
||||
{
|
||||
assert (a >= 0);
|
||||
assert (a <= 1);
|
||||
assert (f_fs >= 0);
|
||||
assert (f_fs < 0.5);
|
||||
|
||||
const double w = 2 * hiir::PI * f_fs;
|
||||
const double c = cos (w);
|
||||
const double s = sin (w);
|
||||
const double x = a + c + a * (c * (a + c) + s * s);
|
||||
const double y = a * a * s - s;
|
||||
double ph = atan2 (y, x);
|
||||
if (ph < 0)
|
||||
{
|
||||
ph += 2 * hiir::PI;
|
||||
}
|
||||
const double dly = ph / w;
|
||||
|
||||
return dly;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: compute_group_delay
|
||||
Description:
|
||||
Computes the group delay introduced by a single filtering unit at a
|
||||
specified frequency.
|
||||
The delay is given for a constant sampling rate between input and output.
|
||||
To compute the group delay of a complete filter, add the group delays
|
||||
of all the units in A0 (z).
|
||||
Input parameters:
|
||||
- a: coefficient for the cell, [0 ; 1]
|
||||
- f_fs: frequency relative to the sampling rate, [0 ; 0.5].
|
||||
- ph_flag: set if filtering unit is used in pi/2-phaser mode, in the form
|
||||
(a - z^-2) / (1 - az^-2)
|
||||
Returns:
|
||||
The group delay in samples, >= 0.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
double PolyphaseIir2Designer::compute_group_delay (double a, double f_fs, bool ph_flag)
|
||||
{
|
||||
assert (a >= 0);
|
||||
assert (a <= 1);
|
||||
assert (f_fs >= 0);
|
||||
assert (f_fs < 0.5);
|
||||
|
||||
const double w = 2 * hiir::PI * f_fs;
|
||||
const double a2 = a * a;
|
||||
const double sig = (ph_flag) ? -2 : 2;
|
||||
const double dly = 2 * (1 - a2) / (a2 + sig * a * cos (2 * w) + 1);
|
||||
|
||||
return dly;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: compute_group_delay
|
||||
Description:
|
||||
Computes the group delay introduced by a complete filter at a specified
|
||||
frequency.
|
||||
The delay is given for a constant sampling rate between input and output.
|
||||
Input parameters:
|
||||
- coef_arr: filter coefficient, as given by the designing functions
|
||||
- nbr_coefs: Number of filter coefficients. > 0.
|
||||
- f_fs: frequency relative to the sampling rate, [0 ; 0.5].
|
||||
- ph_flag: set if filter is used in pi/2-phaser mode, in the form
|
||||
(a - z^-2) / (1 - az^-2)
|
||||
Returns:
|
||||
The group delay in samples, >= 0.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
double PolyphaseIir2Designer::compute_group_delay (const double coef_arr [], int nbr_coefs, double f_fs, bool ph_flag)
|
||||
{
|
||||
assert (nbr_coefs > 0);
|
||||
assert (f_fs >= 0);
|
||||
assert (f_fs < 0.5);
|
||||
|
||||
double dly_total = 0;
|
||||
for (int k = 0; k < nbr_coefs; ++k)
|
||||
{
|
||||
const double dly = compute_group_delay (coef_arr [k], f_fs, ph_flag);
|
||||
dly_total += dly;
|
||||
}
|
||||
|
||||
return dly_total;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
void PolyphaseIir2Designer::compute_transition_param (double &k, double &q, double transition)
|
||||
{
|
||||
assert (transition > 0);
|
||||
assert (transition < 0.5);
|
||||
|
||||
k = tan ((1 - transition * 2) * hiir::PI / 4);
|
||||
k *= k;
|
||||
assert (k < 1);
|
||||
assert (k > 0);
|
||||
double kksqrt = pow (1 - k * k, 0.25);
|
||||
const double e = 0.5 * (1 - kksqrt) / (1 + kksqrt);
|
||||
const double e2 = e * e;
|
||||
const double e4 = e2 * e2;
|
||||
q = e * (1 + e4 * (2 + e4 * (15 + 150 * e4)));
|
||||
assert (q > 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int PolyphaseIir2Designer::compute_order (double attenuation, double q)
|
||||
{
|
||||
assert (attenuation > 0);
|
||||
assert (q > 0);
|
||||
|
||||
const double attn_p2 = pow (10.0, -attenuation / 10);
|
||||
const double a = attn_p2 / (1 - attn_p2);
|
||||
int order = hiir::ceil_int (log (a * a / 16) / log (q));
|
||||
if ((order & 1) == 0)
|
||||
{
|
||||
++ order;
|
||||
}
|
||||
if (order == 1)
|
||||
{
|
||||
order = 3;
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double PolyphaseIir2Designer::compute_atten (double q, int order)
|
||||
{
|
||||
assert (q > 0);
|
||||
assert (order > 0);
|
||||
assert ((order & 1) == 1);
|
||||
|
||||
const double a = 4 * exp (order * 0.5 * log (q));
|
||||
assert (a != -1.0);
|
||||
const double attn_p2 = a / (1 + a);
|
||||
const double attenuation = -10 * log10 (attn_p2);
|
||||
assert (attenuation > 0);
|
||||
|
||||
return attenuation;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double PolyphaseIir2Designer::compute_coef (int index, double k, double q, int order)
|
||||
{
|
||||
assert (index >= 0);
|
||||
assert (index * 2 < order);
|
||||
|
||||
const int c = index + 1;
|
||||
const double num = compute_acc_num (q, order, c) * pow (q, 0.25);
|
||||
const double den = compute_acc_den (q, order, c) + 0.5;
|
||||
const double ww = num / den;
|
||||
const double wwsq = ww * ww;
|
||||
|
||||
const double x = sqrt ((1 - wwsq * k) * (1 - wwsq / k)) / (1 + wwsq);
|
||||
const double coef = (1 - x) / (1 + x);
|
||||
|
||||
return coef;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double PolyphaseIir2Designer::compute_acc_num (double q, int order, int c)
|
||||
{
|
||||
assert (c >= 1);
|
||||
assert (c < order * 2);
|
||||
|
||||
int i = 0;
|
||||
int j = 1;
|
||||
double acc = 0;
|
||||
double q_ii1;
|
||||
do
|
||||
{
|
||||
q_ii1 = hiir::ipowp (q, i * (i + 1));
|
||||
q_ii1 *= sin ((i * 2 + 1) * c * hiir::PI / order) * j;
|
||||
acc += q_ii1;
|
||||
|
||||
j = -j;
|
||||
++i;
|
||||
}
|
||||
while (fabs (q_ii1) > 1e-100);
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double PolyphaseIir2Designer::compute_acc_den (double q, int order, int c)
|
||||
{
|
||||
assert (c >= 1);
|
||||
assert (c < order * 2);
|
||||
|
||||
int i = 1;
|
||||
int j = -1;
|
||||
double acc = 0;
|
||||
double q_i2;
|
||||
do
|
||||
{
|
||||
q_i2 = hiir::ipowp (q, i * i);
|
||||
q_i2 *= cos (i * 2 * c * hiir::PI / order) * j;
|
||||
acc += q_i2;
|
||||
|
||||
j = -j;
|
||||
++i;
|
||||
}
|
||||
while (fabs (q_i2) > 1e-100);
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
143
src/external/hiir/hiir/PolyphaseIir2Designer.h
vendored
Normal file
143
src/external/hiir/hiir/PolyphaseIir2Designer.h
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*****************************************************************************
|
||||
|
||||
PolyphaseIir2Designer.h
|
||||
Author: Laurent de Soras, 2005
|
||||
|
||||
Compute coefficients for 2-path polyphase IIR filter, half-band filter or
|
||||
Pi/2 phaser.
|
||||
|
||||
-2
|
||||
a + z
|
||||
N/2-1 2k
|
||||
A0 (z) = Prod ----------
|
||||
k = 0 -2
|
||||
1 + a z
|
||||
2k
|
||||
|
||||
-2
|
||||
a + z
|
||||
-1 (N-1)/2 2k+1
|
||||
A1 (z) = z . Prod ------------
|
||||
k = 0 -2
|
||||
1 + a z
|
||||
2k+1
|
||||
|
||||
1
|
||||
H (z) = - (A0 (z) + A1 (z))
|
||||
2
|
||||
|
||||
Sum of A0 and A1 gives a low-pass filter.
|
||||
Difference of A0 and A1 gives the complementary high-pass filter.
|
||||
|
||||
For the Pi/2 phaser, product form is (a - z^-2) / (1 - az^-2)
|
||||
Sum and difference of A0 and A1 have a Pi/2 phase difference.
|
||||
|
||||
References:
|
||||
|
||||
* Artur Krukowski
|
||||
Polyphase Two-Path Filter Designer in Java
|
||||
http://www.cmsa.wmin.ac.uk/~artur/Poly.html
|
||||
|
||||
* R.A. Valenzuela, A.G. Constantinides
|
||||
Digital Signal Processing Schemes for Efficient Interpolation and Decimation
|
||||
IEE Proceedings, Dec 1983
|
||||
|
||||
* Scott Wardle
|
||||
A Hilbert-Transformer Frequency Shifter for Audio
|
||||
International Conference on Digital Audio Effects (DAFx) 1998
|
||||
http://www.iua.upf.es/dafx98/papers/WAR19.PS
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if ! defined (hiir_PolyphaseIir2Designer_HEADER_INCLUDED)
|
||||
#define hiir_PolyphaseIir2Designer_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma once
|
||||
#pragma warning (4 : 4250) // "Inherits via dominance."
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
class PolyphaseIir2Designer
|
||||
{
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
public:
|
||||
|
||||
static int compute_nbr_coefs_from_proto (double attenuation, double transition);
|
||||
static double compute_atten_from_order_tbw (int nbr_coefs, double transition);
|
||||
|
||||
static int compute_coefs (double coef_arr [], double attenuation, double transition);
|
||||
static void compute_coefs_spec_order_tbw (double coef_arr [], int nbr_coefs, double transition);
|
||||
|
||||
static double compute_phase_delay (double a, double f_fs);
|
||||
static double compute_group_delay (double a, double f_fs, bool ph_flag);
|
||||
static double compute_group_delay (const double coef_arr [], int nbr_coefs, double f_fs, bool ph_flag);
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
static void compute_transition_param (double &k, double &q, double transition);
|
||||
static int compute_order (double attenuation, double q);
|
||||
static double compute_atten (double q, int order);
|
||||
static double compute_coef (int index, double k, double q, int order);
|
||||
static double compute_acc_num (double q, int order, int c);
|
||||
static double compute_acc_den (double q, int order, int c);
|
||||
|
||||
|
||||
|
||||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
PolyphaseIir2Designer ();
|
||||
~PolyphaseIir2Designer ();
|
||||
PolyphaseIir2Designer (const PolyphaseIir2Designer &other);
|
||||
PolyphaseIir2Designer &
|
||||
operator = (const PolyphaseIir2Designer &other);
|
||||
bool operator == (const PolyphaseIir2Designer &other);
|
||||
bool operator != (const PolyphaseIir2Designer &other);
|
||||
|
||||
}; // class PolyphaseIir2Designer
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#endif // hiir_PolyphaseIir2Designer_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "Oversampler.h"
|
||||
#include "OversamplerHelpers.h"
|
||||
#include "Buffer.h"
|
||||
#include "AudioSpan.h"
|
||||
#include "AudioReader.h"
|
||||
|
|
@ -14,52 +15,6 @@
|
|||
template <class T, std::size_t A = sfz::config::defaultAlignment>
|
||||
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)
|
||||
: 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 numChannels = input.getNumChannels();
|
||||
|
||||
aligned_vector<Upsampler2x> upsampler2x;
|
||||
aligned_vector<Upsampler4x> upsampler4x;
|
||||
aligned_vector<Upsampler8x> upsampler8x;
|
||||
aligned_vector<Upsampler> upsampler(numChannels);
|
||||
|
||||
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
|
||||
sfz::Buffer<float> buffer1 { chunkSize * 2 };
|
||||
sfz::Buffer<float> buffer2 { chunkSize * 4 };
|
||||
auto span1 = absl::MakeSpan(buffer1);
|
||||
auto span2 = absl::MakeSpan(buffer2);
|
||||
// Intermediate buffer
|
||||
sfz::Buffer<float> temp { std::max<size_t>(128, Upsampler::recommendedBuffer(16, chunkSize)) };
|
||||
|
||||
size_t inputFrameCounter { 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++) {
|
||||
const auto inputChunk = input.getSpan(chanIdx).subspan(inputFrameCounter, thisChunkSize);
|
||||
const auto outputChunk = output.getSpan(chanIdx).subspan(outputFrameCounter, outputChunkSize);
|
||||
switch (factor) {
|
||||
case Oversampling::x1:
|
||||
copy<float>(inputChunk, outputChunk);
|
||||
break;
|
||||
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;
|
||||
}
|
||||
upsampler[chanIdx].process(
|
||||
static_cast<int>(factor),
|
||||
inputChunk.data(), outputChunk.data(), static_cast<int>(inputChunk.size()),
|
||||
temp.data(), static_cast<int>(temp.size()));
|
||||
}
|
||||
inputFrameCounter += thisChunkSize;
|
||||
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 numChannels = input.channels();
|
||||
|
||||
aligned_vector<Upsampler2x> upsampler2x;
|
||||
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;
|
||||
}
|
||||
aligned_vector<Upsampler> upsampler(numChannels);
|
||||
|
||||
// Intermediate buffers
|
||||
sfz::Buffer<float> fileBlock { chunkSize * numChannels };
|
||||
sfz::Buffer<float> buffer1 { chunkSize * 2 };
|
||||
sfz::Buffer<float> buffer2 { chunkSize * 4 };
|
||||
auto span1 = absl::MakeSpan(buffer1);
|
||||
auto span2 = absl::MakeSpan(buffer2);
|
||||
sfz::Buffer<float> channelBlock { chunkSize };
|
||||
sfz::Buffer<float> temp { std::max<size_t>(128, Upsampler::recommendedBuffer(16, chunkSize)) };
|
||||
|
||||
auto upsample2xFromInterleaved = [numChannels](
|
||||
Upsampler2x& upsampler, float* output, const float* input,
|
||||
size_t numInputFrames, unsigned chanIdx)
|
||||
auto deinterleave = [numChannels](
|
||||
float* output, const float* input, size_t numFrames, unsigned chanIdx)
|
||||
{
|
||||
for (size_t i = 0; i < numInputFrames; ++i) {
|
||||
float* outp = &output[2 * i];
|
||||
const float* inp = &input[i * numChannels + chanIdx];
|
||||
upsampler.process_sample(outp[0], outp[1], inp[0]);
|
||||
}
|
||||
for (size_t i = 0; i < numFrames; ++i)
|
||||
output[i] = input[i * numChannels + chanIdx];
|
||||
};
|
||||
|
||||
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++) {
|
||||
const auto outputChunk = output.getSpan(chanIdx).subspan(outputFrameCounter, outputChunkSize);
|
||||
switch (factor) {
|
||||
case Oversampling::x1:
|
||||
for (size_t i = 0; i < thisChunkSize; ++i)
|
||||
outputChunk[i] = fileBlock[i * numChannels + chanIdx];
|
||||
break;
|
||||
case Oversampling::x2:
|
||||
upsample2xFromInterleaved(upsampler2x[chanIdx], outputChunk.data(), fileBlock.data(), thisChunkSize, chanIdx);
|
||||
break;
|
||||
case Oversampling::x4:
|
||||
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;
|
||||
|
||||
if (factor == Oversampling::x1)
|
||||
deinterleave(outputChunk.data(), fileBlock.data(), thisChunkSize, chanIdx);
|
||||
else {
|
||||
deinterleave(channelBlock.data(), fileBlock.data(), thisChunkSize, chanIdx);
|
||||
upsampler[chanIdx].process(
|
||||
static_cast<int>(factor),
|
||||
channelBlock.data(), outputChunk.data(), static_cast<int>(thisChunkSize),
|
||||
temp.data(), static_cast<int>(temp.size()));
|
||||
}
|
||||
}
|
||||
inputFrameCounter += thisChunkSize;
|
||||
|
|
|
|||
50
src/sfizz/OversamplerHelpers.h
Normal file
50
src/sfizz/OversamplerHelpers.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
#include "SIMDConfig.h"
|
||||
#include "Config.h"
|
||||
#include "Debug.h"
|
||||
#include <type_traits>
|
||||
#include <cstring>
|
||||
|
||||
namespace sfz {
|
||||
class Upsampler;
|
||||
class Downsampler;
|
||||
} // namespace sfz
|
||||
|
||||
#include <hiir/Upsampler2xFpu.h>
|
||||
#include <hiir/Downsampler2xFpu.h>
|
||||
|
||||
// Note: according to HIIR documentation, FPU versions are
|
||||
// more efficient than SIMD below 12 coefficients.
|
||||
|
||||
#if SFIZZ_HAVE_SSE
|
||||
#include <hiir/Upsampler2xSse.h>
|
||||
#include <hiir/Downsampler2xSse.h>
|
||||
|
||||
namespace hiir {
|
||||
template <int NC> using Upsampler2x = typename std::conditional<NC >= 12,
|
||||
hiir::Upsampler2xSse<NC>, hiir::Upsampler2xFpu<NC>>::type;
|
||||
template <int NC> using Downsampler2x = typename std::conditional<NC >= 12,
|
||||
hiir::Downsampler2xSse<NC>, hiir::Downsampler2xFpu<NC>>::type;
|
||||
} // namespace hiir
|
||||
|
||||
#elif SFIZZ_HAVE_NEON
|
||||
#include <hiir/Upsampler2xNeon.h>
|
||||
#include <hiir/Downsampler2xNeon.h>
|
||||
|
||||
namespace hiir {
|
||||
template <int NC> using Upsampler2x = typename std::conditional<NC >= 12,
|
||||
hiir::Upsampler2xNeon<NC>, hiir::Upsampler2xFpu<NC>>::type;
|
||||
template <int NC> using Downsampler2x = typename std::conditional<NC >= 12,
|
||||
hiir::Downsampler2xNeon<NC>, hiir::Downsampler2xFpu<NC>>::type;
|
||||
} // namespace hiir
|
||||
|
||||
#else
|
||||
|
||||
namespace hiir {
|
||||
template <int NC> using Upsampler2x = hiir::Upsampler2xFpu<NC>;
|
||||
template <int NC> using Downsampler2x = hiir::Downsampler2xFpu<NC>;
|
||||
} // namespace hiir
|
||||
|
||||
#endif
|
||||
|
||||
#include "OversamplerHelpers.hxx"
|
||||
462
src/sfizz/OversamplerHelpers.hxx
Normal file
462
src/sfizz/OversamplerHelpers.hxx
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// This is generated by the Sfizz HIIR designer
|
||||
// Using options: -a 96 -t 0.01 -o 128
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include "OversamplerHelpers.h"
|
||||
|
||||
namespace sfz {
|
||||
|
||||
// 2x <-> 1x: TBW = 0.01
|
||||
static constexpr double OSCoeffs2x[12] = {
|
||||
0.036681502163648017,
|
||||
0.136547624631957715,
|
||||
0.274631759379454110,
|
||||
0.423138617436566666,
|
||||
0.561098697879194752,
|
||||
0.677540049974161618,
|
||||
0.769741833863226588,
|
||||
0.839889624849638028,
|
||||
0.892260818003878908,
|
||||
0.931541959963183896,
|
||||
0.962094548378083947,
|
||||
0.987816370732897076,
|
||||
};
|
||||
// 4x <-> 2x: TBW = 0.255
|
||||
static constexpr double OSCoeffs4x[4] = {
|
||||
0.041893991997656171,
|
||||
0.168903482439952013,
|
||||
0.390560772921165922,
|
||||
0.743895748268478152,
|
||||
};
|
||||
// 8x <-> 4x: TBW = 0.3775
|
||||
static constexpr double OSCoeffs8x[3] = {
|
||||
0.055748680811302048,
|
||||
0.243051195741530918,
|
||||
0.646699131192682297,
|
||||
};
|
||||
// 16x <-> 8x: TBW = 0.43875
|
||||
static constexpr double OSCoeffs16x[2] = {
|
||||
0.107172166664564611,
|
||||
0.530904350331903085,
|
||||
};
|
||||
// 32x <-> 16x: TBW = 0.469375
|
||||
static constexpr double OSCoeffs32x[2] = {
|
||||
0.105969237763476387,
|
||||
0.528620279623742473,
|
||||
};
|
||||
// 64x <-> 32x: TBW = 0.484687
|
||||
static constexpr double OSCoeffs64x[1] = {
|
||||
0.333526281707771211,
|
||||
};
|
||||
// 128x <-> 64x: TBW = 0.492344
|
||||
static constexpr double OSCoeffs128x[1] = {
|
||||
0.333381553051105561,
|
||||
};
|
||||
|
||||
class Upsampler {
|
||||
public:
|
||||
Upsampler()
|
||||
{
|
||||
up2_.set_coefs(OSCoeffs2x);
|
||||
up4_.set_coefs(OSCoeffs4x);
|
||||
up8_.set_coefs(OSCoeffs8x);
|
||||
up16_.set_coefs(OSCoeffs16x);
|
||||
up32_.set_coefs(OSCoeffs32x);
|
||||
up64_.set_coefs(OSCoeffs64x);
|
||||
up128_.set_coefs(OSCoeffs128x);
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
up2_.clear_buffers();
|
||||
up4_.clear_buffers();
|
||||
up8_.clear_buffers();
|
||||
up16_.clear_buffers();
|
||||
up32_.clear_buffers();
|
||||
up64_.clear_buffers();
|
||||
up128_.clear_buffers();
|
||||
}
|
||||
static int recommendedBuffer(int factor, int spl)
|
||||
{
|
||||
switch (factor) {
|
||||
case 2:
|
||||
return 0;
|
||||
case 4:
|
||||
return 2 * spl;
|
||||
default:
|
||||
return factor * spl;
|
||||
}
|
||||
}
|
||||
static bool canProcess(int factor)
|
||||
{
|
||||
switch (factor) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
case 16:
|
||||
case 32:
|
||||
case 64:
|
||||
case 128:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void process(int factor, const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
switch (factor) {
|
||||
case 1:
|
||||
if (in != out) std::memcpy(out, in, spl * sizeof(float));
|
||||
break;
|
||||
case 2:
|
||||
process2x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 4:
|
||||
process4x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 8:
|
||||
process8x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 16:
|
||||
process16x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 32:
|
||||
process32x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 64:
|
||||
process64x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 128:
|
||||
process128x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
default:
|
||||
ASSERTFALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void process2x(const float *in, float *out, int spl, float * = nullptr, int = 0)
|
||||
{
|
||||
up2_.process_block(out, in, spl);
|
||||
}
|
||||
void process4x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 2;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
up2_.process_block(t1, in, 1 * curspl);
|
||||
up4_.process_block(out, t1, 2 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
void process8x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 8;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
float *t2 = temp + 4 * maxspl;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
up2_.process_block(t1, in, 1 * curspl);
|
||||
up4_.process_block(t2, t1, 2 * curspl);
|
||||
up8_.process_block(out, t2, 4 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
void process16x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 16;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
float *t2 = temp + 8 * maxspl;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
up2_.process_block(t1, in, 1 * curspl);
|
||||
up4_.process_block(t2, t1, 2 * curspl);
|
||||
up8_.process_block(t1, t2, 4 * curspl);
|
||||
up16_.process_block(out, t1, 8 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
void process32x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 32;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
float *t2 = temp + 16 * maxspl;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
up2_.process_block(t1, in, 1 * curspl);
|
||||
up4_.process_block(t2, t1, 2 * curspl);
|
||||
up8_.process_block(t1, t2, 4 * curspl);
|
||||
up16_.process_block(t2, t1, 8 * curspl);
|
||||
up32_.process_block(out, t2, 16 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
void process64x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 64;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
float *t2 = temp + 32 * maxspl;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
up2_.process_block(t1, in, 1 * curspl);
|
||||
up4_.process_block(t2, t1, 2 * curspl);
|
||||
up8_.process_block(t1, t2, 4 * curspl);
|
||||
up16_.process_block(t2, t1, 8 * curspl);
|
||||
up32_.process_block(t1, t2, 16 * curspl);
|
||||
up64_.process_block(out, t1, 32 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
void process128x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 128;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
float *t2 = temp + 64 * maxspl;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
up2_.process_block(t1, in, 1 * curspl);
|
||||
up4_.process_block(t2, t1, 2 * curspl);
|
||||
up8_.process_block(t1, t2, 4 * curspl);
|
||||
up16_.process_block(t2, t1, 8 * curspl);
|
||||
up32_.process_block(t1, t2, 16 * curspl);
|
||||
up64_.process_block(t2, t1, 32 * curspl);
|
||||
up128_.process_block(out, t2, 64 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
private:
|
||||
hiir::Upsampler2x<12> up2_;
|
||||
hiir::Upsampler2x<4> up4_;
|
||||
hiir::Upsampler2x<3> up8_;
|
||||
hiir::Upsampler2x<2> up16_;
|
||||
hiir::Upsampler2x<2> up32_;
|
||||
hiir::Upsampler2x<1> up64_;
|
||||
hiir::Upsampler2x<1> up128_;
|
||||
};
|
||||
|
||||
class Downsampler {
|
||||
public:
|
||||
Downsampler()
|
||||
{
|
||||
down128_.set_coefs(OSCoeffs128x);
|
||||
down64_.set_coefs(OSCoeffs64x);
|
||||
down32_.set_coefs(OSCoeffs32x);
|
||||
down16_.set_coefs(OSCoeffs16x);
|
||||
down8_.set_coefs(OSCoeffs8x);
|
||||
down4_.set_coefs(OSCoeffs4x);
|
||||
down2_.set_coefs(OSCoeffs2x);
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
down128_.clear_buffers();
|
||||
down64_.clear_buffers();
|
||||
down32_.clear_buffers();
|
||||
down16_.clear_buffers();
|
||||
down8_.clear_buffers();
|
||||
down4_.clear_buffers();
|
||||
down2_.clear_buffers();
|
||||
}
|
||||
static int recommendedBuffer(int factor, int spl)
|
||||
{
|
||||
switch (factor) {
|
||||
case 2:
|
||||
return 0;
|
||||
case 4:
|
||||
return 2 * spl;
|
||||
default:
|
||||
return factor * spl;
|
||||
}
|
||||
}
|
||||
static bool canProcess(int factor)
|
||||
{
|
||||
switch (factor) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
case 16:
|
||||
case 32:
|
||||
case 64:
|
||||
case 128:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void process(int factor, const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
switch (factor) {
|
||||
case 128:
|
||||
process128x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 64:
|
||||
process64x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 32:
|
||||
process32x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 16:
|
||||
process16x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 8:
|
||||
process8x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 4:
|
||||
process4x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 2:
|
||||
process2x(in, out, spl, temp, ntemp);
|
||||
break;
|
||||
case 1:
|
||||
if (in != out) std::memcpy(out, in, spl * sizeof(float));
|
||||
break;
|
||||
default:
|
||||
ASSERTFALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void process2x(const float *in, float *out, int spl, float * = nullptr, int = 0)
|
||||
{
|
||||
down2_.process_block(out, in, spl);
|
||||
}
|
||||
void process4x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 2;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
down4_.process_block(t1, in, 2 * curspl);
|
||||
down2_.process_block(out, t1, 1 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
void process8x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 8;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
float *t2 = temp + 4 * maxspl;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
down8_.process_block(t1, in, 4 * curspl);
|
||||
down4_.process_block(t2, t1, 2 * curspl);
|
||||
down2_.process_block(out, t2, 1 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
void process16x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 16;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
float *t2 = temp + 8 * maxspl;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
down16_.process_block(t1, in, 8 * curspl);
|
||||
down8_.process_block(t2, t1, 4 * curspl);
|
||||
down4_.process_block(t1, t2, 2 * curspl);
|
||||
down2_.process_block(out, t1, 1 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
void process32x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 32;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
float *t2 = temp + 16 * maxspl;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
down32_.process_block(t1, in, 16 * curspl);
|
||||
down16_.process_block(t2, t1, 8 * curspl);
|
||||
down8_.process_block(t1, t2, 4 * curspl);
|
||||
down4_.process_block(t2, t1, 2 * curspl);
|
||||
down2_.process_block(out, t2, 1 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
void process64x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 64;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
float *t2 = temp + 32 * maxspl;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
down64_.process_block(t1, in, 32 * curspl);
|
||||
down32_.process_block(t2, t1, 16 * curspl);
|
||||
down16_.process_block(t1, t2, 8 * curspl);
|
||||
down8_.process_block(t2, t1, 4 * curspl);
|
||||
down4_.process_block(t1, t2, 2 * curspl);
|
||||
down2_.process_block(out, t1, 1 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
void process128x(const float *in, float *out, int spl, float *temp, int ntemp)
|
||||
{
|
||||
int maxspl = ntemp / 128;
|
||||
ASSERT(maxspl > 0);
|
||||
float *t1 = temp;
|
||||
float *t2 = temp + 64 * maxspl;
|
||||
while (spl > 0) {
|
||||
int curspl = (spl < maxspl) ? spl : maxspl;
|
||||
down128_.process_block(t1, in, 64 * curspl);
|
||||
down64_.process_block(t2, t1, 32 * curspl);
|
||||
down32_.process_block(t1, t2, 16 * curspl);
|
||||
down16_.process_block(t2, t1, 8 * curspl);
|
||||
down8_.process_block(t1, t2, 4 * curspl);
|
||||
down4_.process_block(t2, t1, 2 * curspl);
|
||||
down2_.process_block(out, t2, 1 * curspl);
|
||||
in += curspl;
|
||||
out += curspl;
|
||||
spl -= curspl;
|
||||
}
|
||||
}
|
||||
private:
|
||||
hiir::Downsampler2x<1> down128_;
|
||||
hiir::Downsampler2x<1> down64_;
|
||||
hiir::Downsampler2x<2> down32_;
|
||||
hiir::Downsampler2x<2> down16_;
|
||||
hiir::Downsampler2x<3> down8_;
|
||||
hiir::Downsampler2x<4> down4_;
|
||||
hiir::Downsampler2x<12> down2_;
|
||||
};
|
||||
|
||||
} // namespace sfz
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include "Opcode.h"
|
||||
#include "AudioSpan.h"
|
||||
#include "MathHelpers.h"
|
||||
#include "OversamplerHelpers.h"
|
||||
#include "absl/memory/memory.h"
|
||||
|
||||
static constexpr int _oversampling = 2;
|
||||
|
|
@ -35,8 +36,8 @@ namespace fx {
|
|||
float _inputGain { Default::compGain };
|
||||
AudioBuffer<float, 2> _tempBuffer2x { 2, _oversampling * config::defaultSamplesPerBlock };
|
||||
AudioBuffer<float, 2> _gain2x { 2, _oversampling * config::defaultSamplesPerBlock };
|
||||
hiir::Downsampler2xFpu<12> _downsampler2x[EffectChannels];
|
||||
hiir::Upsampler2xFpu<12> _upsampler2x[EffectChannels];
|
||||
hiir::Downsampler2x<12> _downsampler2x[EffectChannels];
|
||||
hiir::Upsampler2x<12> _upsampler2x[EffectChannels];
|
||||
|
||||
#define DEFINE_SET_GET(type, ident, name, var, def, min, max, step) \
|
||||
float get_##ident(size_t i) const noexcept { return _compressor[i].var; } \
|
||||
|
|
@ -65,11 +66,9 @@ namespace fx {
|
|||
comp.instanceConstants(sampleRate);
|
||||
}
|
||||
|
||||
static constexpr double coefs2x[12] = { 0.036681502163648017, 0.13654762463195794, 0.27463175937945444, 0.42313861743656711, 0.56109869787919531, 0.67754004997416184, 0.76974183386322703, 0.83988962484963892, 0.89226081800387902, 0.9315419599631839, 0.96209454837808417, 0.98781637073289585 };
|
||||
|
||||
for (unsigned c = 0; c < EffectChannels; ++c) {
|
||||
impl._downsampler2x[c].set_coefs(coefs2x);
|
||||
impl._upsampler2x[c].set_coefs(coefs2x);
|
||||
impl._downsampler2x[c].set_coefs(OSCoeffs2x);
|
||||
impl._upsampler2x[c].set_coefs(OSCoeffs2x);
|
||||
}
|
||||
|
||||
clear();
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
#include "Effects.h"
|
||||
#include "hiir/Downsampler2xFpu.h"
|
||||
#include "hiir/Upsampler2xFpu.h"
|
||||
#include <memory>
|
||||
|
||||
namespace sfz {
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@
|
|||
#include "Opcode.h"
|
||||
#include "Config.h"
|
||||
#include "MathHelpers.h"
|
||||
#include <hiir/Upsampler2xFpu.h>
|
||||
#include <hiir/Downsampler2xFpu.h>
|
||||
#include "OversamplerHelpers.h"
|
||||
#include <absl/types/span.h>
|
||||
#include <cmath>
|
||||
|
||||
|
|
@ -47,15 +46,9 @@ struct Disto::Impl {
|
|||
float _toneLpfMem[EffectChannels] = {};
|
||||
faustDisto _stages[EffectChannels][Default::maxDistoStages];
|
||||
|
||||
hiir::Upsampler2xFpu<12> _up2x[EffectChannels];
|
||||
hiir::Upsampler2xFpu<4> _up4x[EffectChannels];
|
||||
hiir::Upsampler2xFpu<3> _up8x[EffectChannels];
|
||||
|
||||
hiir::Downsampler2xFpu<12> _down2x[EffectChannels];
|
||||
hiir::Downsampler2xFpu<4> _down4x[EffectChannels];
|
||||
hiir::Downsampler2xFpu<3> _down8x[EffectChannels];
|
||||
|
||||
std::unique_ptr<float[]> _temp8x[2];
|
||||
sfz::Upsampler _upsampler[EffectChannels];
|
||||
sfz::Downsampler _downsampler[EffectChannels];
|
||||
std::unique_ptr<float[]> _temp[2];
|
||||
|
||||
// use the same formula as reverb
|
||||
float toneCutoff() const noexcept
|
||||
|
|
@ -97,27 +90,14 @@ void Disto::setSampleRate(double sampleRate)
|
|||
stage.instanceConstants(sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr double coefs2x[12] = { 0.036681502163648017, 0.13654762463195794, 0.27463175937945444, 0.42313861743656711, 0.56109869787919531, 0.67754004997416184, 0.76974183386322703, 0.83988962484963892, 0.89226081800387902, 0.9315419599631839, 0.96209454837808417, 0.98781637073289585 };
|
||||
static constexpr double coefs4x[4] = { 0.042448989488488006, 0.17072114107630679, 0.39329183835224008, 0.74569514831986694 };
|
||||
static constexpr double coefs8x[3] = { 0.055748680811302048, 0.24305119574153092, 0.6466991311926823 };
|
||||
|
||||
for (unsigned c = 0; c < EffectChannels; ++c) {
|
||||
impl._down2x[c].set_coefs(coefs2x);
|
||||
impl._down4x[c].set_coefs(coefs4x);
|
||||
impl._down8x[c].set_coefs(coefs8x);
|
||||
impl._up2x[c].set_coefs(coefs2x);
|
||||
impl._up4x[c].set_coefs(coefs4x);
|
||||
impl._up8x[c].set_coefs(coefs8x);
|
||||
}
|
||||
}
|
||||
|
||||
void Disto::setSamplesPerBlock(int samplesPerBlock)
|
||||
{
|
||||
Impl& impl = *_impl;
|
||||
|
||||
for (std::unique_ptr<float[]>& temp : impl._temp8x)
|
||||
temp.reset(new float[8 * samplesPerBlock]);
|
||||
for (std::unique_ptr<float[]>& temp : impl._temp)
|
||||
temp.reset(new float[_oversampling * samplesPerBlock]);
|
||||
}
|
||||
|
||||
void Disto::clear()
|
||||
|
|
@ -130,12 +110,8 @@ void Disto::clear()
|
|||
|
||||
for (unsigned c = 0; c < EffectChannels; ++c) {
|
||||
impl._toneLpfMem[c] = 0.0f;
|
||||
impl._up2x[c].clear_buffers();
|
||||
impl._up4x[c].clear_buffers();
|
||||
impl._up8x[c].clear_buffers();
|
||||
impl._down2x[c].clear_buffers();
|
||||
impl._down4x[c].clear_buffers();
|
||||
impl._down8x[c].clear_buffers();
|
||||
impl._downsampler[c].clear();
|
||||
impl._upsampler[c].clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -162,14 +138,12 @@ void Disto::process(const float* const inputs[], float* const outputs[], unsigne
|
|||
}
|
||||
impl._toneLpfMem[c] = lpfMem;
|
||||
|
||||
// upsample to 8x
|
||||
// upsample
|
||||
absl::Span<float> temp[2] = {
|
||||
absl::Span<float>(impl._temp8x[0].get(), 8 * nframes),
|
||||
absl::Span<float>(impl._temp8x[1].get(), 8 * nframes),
|
||||
absl::Span<float>(impl._temp[0].get(), _oversampling * nframes),
|
||||
absl::Span<float>(impl._temp[1].get(), _oversampling * nframes),
|
||||
};
|
||||
impl._up2x[c].process_block(temp[0].data(), lpfOut.data(), nframes);
|
||||
impl._up4x[c].process_block(temp[1].data(), temp[0].data(), 2 * nframes);
|
||||
impl._up8x[c].process_block(temp[0].data(), temp[1].data(), 4 * nframes);
|
||||
impl._upsampler[c].process(_oversampling, lpfOut.data(), temp[0].data(), nframes, temp[1].data(), static_cast<int>(temp[1].size()));
|
||||
absl::Span<float> upsamplerOut = temp[0];
|
||||
|
||||
// run disto stages
|
||||
|
|
@ -180,13 +154,11 @@ void Disto::process(const float* const inputs[], float* const outputs[], unsigne
|
|||
//
|
||||
float *faustIn[] = { stageInOut.data() };
|
||||
float *faustOut[] = { stageInOut.data() };
|
||||
impl._stages[c][s].compute(8 * nframes, faustIn, faustOut);
|
||||
impl._stages[c][s].compute(_oversampling * nframes, faustIn, faustOut);
|
||||
}
|
||||
|
||||
// downsample to 1x
|
||||
impl._down8x[c].process_block(temp[1].data(), stageInOut.data(), 4 * nframes);
|
||||
impl._down4x[c].process_block(temp[0].data(), temp[1].data(), 2 * nframes);
|
||||
impl._down2x[c].process_block(outputs[c], temp[0].data(), nframes);
|
||||
// downsample
|
||||
impl._downsampler[c].process(_oversampling, stageInOut.data(), outputs[c], nframes, temp[1].data(), static_cast<int>(temp[1].size()));
|
||||
|
||||
// dry/wet mix
|
||||
absl::Span<float> mixOut(outputs[c], nframes);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "Opcode.h"
|
||||
#include "AudioSpan.h"
|
||||
#include "MathHelpers.h"
|
||||
#include "OversamplerHelpers.h"
|
||||
#include "absl/memory/memory.h"
|
||||
|
||||
static constexpr int _oversampling = 2;
|
||||
|
|
@ -38,8 +39,8 @@ namespace fx {
|
|||
float _inputGain = 1.0;
|
||||
AudioBuffer<float, 2> _tempBuffer2x { 2, _oversampling * config::defaultSamplesPerBlock };
|
||||
AudioBuffer<float, 2> _gain2x { 2, _oversampling * config::defaultSamplesPerBlock };
|
||||
hiir::Downsampler2xFpu<12> _downsampler2x[EffectChannels];
|
||||
hiir::Upsampler2xFpu<12> _upsampler2x[EffectChannels];
|
||||
hiir::Downsampler2x<12> _downsampler2x[EffectChannels];
|
||||
hiir::Upsampler2x<12> _upsampler2x[EffectChannels];
|
||||
|
||||
#define DEFINE_SET_GET(type, ident, name, var, def, min, max, step) \
|
||||
float get_##ident(size_t i) const noexcept { return _gate[i].var; } \
|
||||
|
|
@ -68,11 +69,9 @@ namespace fx {
|
|||
gate.instanceConstants(sampleRate);
|
||||
}
|
||||
|
||||
static constexpr double coefs2x[12] = { 0.036681502163648017, 0.13654762463195794, 0.27463175937945444, 0.42313861743656711, 0.56109869787919531, 0.67754004997416184, 0.76974183386322703, 0.83988962484963892, 0.89226081800387902, 0.9315419599631839, 0.96209454837808417, 0.98781637073289585 };
|
||||
|
||||
for (unsigned c = 0; c < EffectChannels; ++c) {
|
||||
impl._downsampler2x[c].set_coefs(coefs2x);
|
||||
impl._upsampler2x[c].set_coefs(coefs2x);
|
||||
impl._downsampler2x[c].set_coefs(OSCoeffs2x);
|
||||
impl._upsampler2x[c].set_coefs(OSCoeffs2x);
|
||||
}
|
||||
|
||||
clear();
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
#include "Effects.h"
|
||||
#include "hiir/Downsampler2xFpu.h"
|
||||
#include "hiir/Upsampler2xFpu.h"
|
||||
#include <memory>
|
||||
|
||||
namespace sfz {
|
||||
|
|
|
|||
|
|
@ -36,11 +36,9 @@ namespace fx {
|
|||
_limiter->classInit(sampleRate);
|
||||
_limiter->instanceConstants(sampleRate);
|
||||
|
||||
static constexpr double coefs2x[12] = { 0.036681502163648017, 0.13654762463195794, 0.27463175937945444, 0.42313861743656711, 0.56109869787919531, 0.67754004997416184, 0.76974183386322703, 0.83988962484963892, 0.89226081800387902, 0.9315419599631839, 0.96209454837808417, 0.98781637073289585 };
|
||||
|
||||
for (unsigned c = 0; c < EffectChannels; ++c) {
|
||||
_downsampler2x[c].set_coefs(coefs2x);
|
||||
_upsampler2x[c].set_coefs(coefs2x);
|
||||
_downsampler2x[c].set_coefs(OSCoeffs2x);
|
||||
_upsampler2x[c].set_coefs(OSCoeffs2x);
|
||||
}
|
||||
|
||||
clear();
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
#include "Effects.h"
|
||||
#include "hiir/Downsampler2xFpu.h"
|
||||
#include "hiir/Upsampler2xFpu.h"
|
||||
#include "OversamplerHelpers.h"
|
||||
class faustLimiter;
|
||||
|
||||
namespace sfz {
|
||||
|
|
@ -50,8 +49,8 @@ namespace fx {
|
|||
private:
|
||||
std::unique_ptr<faustLimiter> _limiter;
|
||||
AudioBuffer<float, 2> _tempBuffer2x { 2, 2 * config::defaultSamplesPerBlock };
|
||||
hiir::Downsampler2xFpu<12> _downsampler2x[EffectChannels];
|
||||
hiir::Upsampler2xFpu<12> _upsampler2x[EffectChannels];
|
||||
hiir::Downsampler2x<12> _downsampler2x[EffectChannels];
|
||||
hiir::Upsampler2x<12> _upsampler2x[EffectChannels];
|
||||
};
|
||||
|
||||
} // namespace fx
|
||||
|
|
|
|||
|
|
@ -101,8 +101,7 @@ namespace fx {
|
|||
{
|
||||
(void)sampleRate;
|
||||
|
||||
static constexpr double coefs2x[12] = { 0.036681502163648017, 0.13654762463195794, 0.27463175937945444, 0.42313861743656711, 0.56109869787919531, 0.67754004997416184, 0.76974183386322703, 0.83988962484963892, 0.89226081800387902, 0.9315419599631839, 0.96209454837808417, 0.98781637073289585 };
|
||||
fDownsampler2x.set_coefs(coefs2x);
|
||||
fDownsampler2x.set_coefs(OSCoeffs2x);
|
||||
}
|
||||
|
||||
void Lofi::Bitred::clear()
|
||||
|
|
@ -153,8 +152,7 @@ namespace fx {
|
|||
{
|
||||
fSampleTime = 1.0f / static_cast<float>(sampleRate);
|
||||
|
||||
static constexpr double coefs2x[12] = { 0.036681502163648017, 0.13654762463195794, 0.27463175937945444, 0.42313861743656711, 0.56109869787919531, 0.67754004997416184, 0.76974183386322703, 0.83988962484963892, 0.89226081800387902, 0.9315419599631839, 0.96209454837808417, 0.98781637073289585 };
|
||||
fDownsampler2x.set_coefs(coefs2x);
|
||||
fDownsampler2x.set_coefs(OSCoeffs2x);
|
||||
}
|
||||
|
||||
void Lofi::Decim::clear()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
#include "Effects.h"
|
||||
#include "hiir/Downsampler2xFpu.h"
|
||||
#include "OversamplerHelpers.h"
|
||||
|
||||
namespace sfz {
|
||||
namespace fx {
|
||||
|
|
@ -57,7 +57,7 @@ namespace fx {
|
|||
private:
|
||||
float fDepth = 0.0;
|
||||
float fLastValue = 0.0;
|
||||
hiir::Downsampler2xFpu<12> fDownsampler2x;
|
||||
hiir::Downsampler2x<12> fDownsampler2x;
|
||||
};
|
||||
|
||||
///
|
||||
|
|
@ -73,7 +73,7 @@ namespace fx {
|
|||
float fDepth = 0.0;
|
||||
float fPhase = 0.0;
|
||||
float fLastValue = 0.0;
|
||||
hiir::Downsampler2xFpu<12> fDownsampler2x;
|
||||
hiir::Downsampler2x<12> fDownsampler2x;
|
||||
};
|
||||
|
||||
///
|
||||
|
|
|
|||
|
|
@ -28,11 +28,9 @@ namespace fx {
|
|||
{
|
||||
(void)sampleRate;
|
||||
|
||||
static constexpr double coefs2x[12] = { 0.036681502163648017, 0.13654762463195794, 0.27463175937945444, 0.42313861743656711, 0.56109869787919531, 0.67754004997416184, 0.76974183386322703, 0.83988962484963892, 0.89226081800387902, 0.9315419599631839, 0.96209454837808417, 0.98781637073289585 };
|
||||
|
||||
for (unsigned c = 0; c < EffectChannels; ++c) {
|
||||
_downsampler2x[c].set_coefs(coefs2x);
|
||||
_upsampler2x[c].set_coefs(coefs2x);
|
||||
_downsampler2x[c].set_coefs(OSCoeffs2x);
|
||||
_upsampler2x[c].set_coefs(OSCoeffs2x);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
#include "Effects.h"
|
||||
#include "hiir/Downsampler2xFpu.h"
|
||||
#include "hiir/Upsampler2xFpu.h"
|
||||
#include "OversamplerHelpers.h"
|
||||
|
||||
namespace sfz {
|
||||
namespace fx {
|
||||
|
|
@ -47,8 +46,8 @@ namespace fx {
|
|||
|
||||
private:
|
||||
AudioBuffer<float, 1> _tempBuffer { 1, config::defaultSamplesPerBlock };
|
||||
hiir::Downsampler2xFpu<12> _downsampler2x[2];
|
||||
hiir::Upsampler2xFpu<12> _upsampler2x[2];
|
||||
hiir::Downsampler2x<12> _downsampler2x[2];
|
||||
hiir::Upsampler2x<12> _upsampler2x[2];
|
||||
|
||||
float _amount = 0;
|
||||
bool _full = false;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue