sfFDN is a C++ library inspired by the MATLAB Feedback Delay Network Toolbox (FDNTB) by S. J. Schlecht1. It provides efficient implementations of FDNs with various features such as:
- Configurable delay lines
- Different types of feedback matrices (e.g., Hadamard, Householder, Random, Circulant, etc.)
- Filter Feedback Matrices (FFM) as presented in 2
- Single channel and multi-channel IIR filters
- Graphic Equalizers filter as presented in 3
- Partitioned convolution for FIR filtering
- Sparse FIR filter
- Schroeder all-pass filters
- Time-varying delay lines
- Time-varying input and output gains
The FDN topology implemented in sfFDN is based on the canonical structure found in the literature and is shown here:
This topology can be separated into seven building blocks: the input gains (green), the delay lines (yellow), the loop filters (red), the feedback matrix (orange), the output gains (blue), the tone correction filter (purple), and the direct gain (gray). At the heart of the library is the \verb|AudioProcessor| interface. In the context of sfFDN, an audio processor is defined as a class that can take AudioProcessorChain class can be used to chain multiple audio processors in series and the FilterBank class can similarly be used to group multiple single-channel audio processors into a bank of parallel processor.
Input/Output gains
The input gains block supports any processor that takes a single channel of audio as input and outputs ParallelGains class which can either split a single input channel into ParallelGains processor. For longer FIR filters, the PartitionedConvolver processor can be used to greatly reduce the computational cost of the convolution. Fagerström et al. (2020)4 proposed a novel FDN structure where the input and output gains are replaced by velvet noise filters, resulting in an increase in echo density. This so-called velvet-noise FDN can be implemented easily in sfFDN by using the SparseFIR class which provides an efficient implementation of sparse FIR filters, especially suited for velvet-noise sequences. The FilterBank processor can also be used to create a bank of parallel filters, allowing for each channel to have its own unique FIR or velvet-noise filter.
Delay Lines
For efficiency reasons, sfFDN restrict the main delay lengths to integer values, but fractional and
time-varying delay lines can easily be integrated by including a DelayInterp or DelayTimeVarying
processor inside the loop filter block. The GetDelayLengths() function provide a conve-
nient way to generate delay lengths based on several heuristics:
- Random Randomly generate delay lengths pulled from a uniform distribution
- Gaussian: Randomly generate delay lengths pulled from a Gaussian distribution.
- Prime: Randomly generate delay lengths pulled from a uniform distribution. Delays are guaranteed to be prime numbers.
- Uniform: Delays are uniformly spaced between a minimum and maximum value.
- Prime Power: Delays are integer powers of prime numbers. Based on the implementation found in the Faust library
- Steam Audio: Re-implementation of the delay length generation method used in the reverberator of the Steam Audio Library. This heuristic is based on the Prime Power method but with some amount of randomization added.
- Mean Delay: Delays are generated based on Eq.(40) from (Schlecht & Habets, 2017a)5. The resulting delays are logarithmically spaced to obtain a desired mean delay length and standard deviation.
Feedback Matrix
The feedback matrix supports any processor that takes N channels of audio as input and outputs
Loop Filters
The loop filters block is an optional block that supports any processor that takes CreateAttenuationFilterBank() can be used to create a bank of filters to control the decay time of the FDN. The type of filter used depends on the length of the t60s span parameter. If 1 FilterBank class can also be used to create an arbitrary bank of parallel filters, allowing for each channel to have its own unique filter.
Here is an example of how to create a 'classic' FDN of 8 delay lines with a Hadamard feedback matrix:
#include <sffdn/sffdn.h>
constexpr uint32_t kSampleRate = 48000;
constexpr uint32_t kFDNOrder = 8;
sfFDN::FDN fdn(kFDNOrder);
// Set all input gains to 0.5
std::vector<float> input_gains(kFDNOrder, 0.5f);
fdn.SetInputGains(input_gains);
// Set all output gains to 0.5
std::vector<float> output_gains(kFDNOrder, 0.5f);
fdn.SetOutputGains(output_gains);
// Set Hadamard feedback matrix
sfFDN::ScalarFeedbackMatrixOptions feedback_matrix_options;
feedback_matrix_options.matrix_size = kFDNOrder;
feedback_matrix_options.type = sfFDN::ScalarMatrixType::Hadamard;
auto feedback_matrix = std::make_unique<sfFDN::ScalarFeedbackMatrix>(feedback_matrix_options);
fdn.SetFeedbackMatrix(std::move(feedback_matrix));
// Set random delay lengths
std::vector<uint32_t> delays = sfFDN::GetDelayLengths(kFDNOrder, 500, 3000, sfFDN::DelayLengthType::Random);
fdn.SetDelays(delays);
// Set homogeneous decay of 1 second
constexpr std::array t60s = {1.0f};
auto attenuation_filter = sfFDN::CreateAttenuationFilterBank(t60s, delays, kSampleRate);
fdn.SetLoopFilter(std::move(attenuation_filter));
Another way to create the same FDN is to use the CreateFDNFromConfig() function which takes a configuration struct as input.
The FDNConfig struct is serializable to JSON format, allowing for easy saving and loading of FDN configurations.
sfFDN::FDNConfig config;
config.fdn_size = 8;
config.direct_gain = 1.f;
config.block_size = 128;
config.sample_rate = 48000;
sfFDN::DelayBankOptions delay_bank_options{
.delays = sfFDN::GetDelayLengths(config.fdn_size, 500, 3000, sfFDN::DelayLengthType::Random),
.block_size = config.block_size,
.interpolation_type = sfFDN::DelayInterpolationType::None};
config.delay_bank_config = delay_bank_options;
sfFDN::ParallelGainsOptions input_gains_options{.mode = sfFDN::ParallelGainsMode::Split,
.gains = std::vector<float>(config.fdn_size, 0.5f)};
config.input_block_config.parallel_gains_config = input_gains_options;
sfFDN::ScalarFeedbackMatrixOptions feedback_matrix_options{
.matrix_size = config.fdn_size,
.type = sfFDN::ScalarMatrixType::Hadamard};
config.feedback_matrix_config = feedback_matrix_options;
sfFDN::AttenuationFilterBankOptions attenuation_filter_bank_options;
sfFDN::HomogenousFilterOptions homogenous_filter_options{
.t60 = 1.f,
.delay = 0.f,
.sample_rate = config.sample_rate};
attenuation_filter_bank_options.filter_configs.push_back(homogenous_filter_options);
config.loop_filter_configs.push_back(attenuation_filter_bank_options);
sfFDN::ParallelGainsOptions output_gains_options{
.mode = sfFDN::ParallelGainsMode::Merge,
.gains = std::vector<float>(config.fdn_size, 0.5f)};
config.output_block_config.parallel_gains_config = output_gains_options;
auto fdn = sfFDN::CreateFDNFromConfig(config);The library is built using CMake. sfFDN uses CPM to manage dependencies. CMake presets are provided for building with Ninja and LLVM.
# configure with Ninja and LLVM
cmake --preset llvm-ninja
# build
cmake --build --preset llvm --config ReleasesfFDN can be included in your project using CPM (or CMake's FetchContent directly)
CPMAddPackage(
NAME sfFDN
GIT_REPOSITORY https://github.com/Segfault1602/sfFDN.git
GIT_TAG main
)
target_link_libraries(your_target PRIVATE sfFDN::sfFDN)- Eigen - Linear algebra library
- PFFFT - FFT library for partitioned convolution
- KissFFT - FFT library used for FFT size less than what PFFFT supports
- nlohmann-json - Used to export/import FDN configurations to JSON files. Can be omitted if you don't need this feature by not building fdn_config.cpp
- nanobench - Microbenchmarking library used for performance testing. Not required if SFFDN_BUILD_TESTS is OFF.
- Catch2 - Unit testing framework used for testing. Not required if SFFDN_BUILD_TESTS is OFF.
- libsndfile - Used in unit tests for reading/writing WAV files. Not required if SFFDN_BUILD_TESTS is OFF.
Footnotes
-
S. J. Schlecht, “FDNTB: the feedback delay network toolbox,” 23rd International Conference on Digital Audio Effects (DAFx2020), 2020. ↩
-
S. J. Schlecht and E. A. P. Habets, “Scattering in Feedback Delay Networks,” IEEE/ACM Transactions on Audio, Speech, and Language Processing, vol. 28, June 2020. ↩
-
V. Välimäki, K. Prawda, and S. J. Schlecht, “Two-Stage Attenuation Filter for Artificial Reverberation,” IEEE Signal Processing Letters, vol. 31, pp. 391–395, Jan. 2024, doi: 10.1109/LSP.2024.3352510. ↩
-
J. Fagerström, B. Alary, S. J. Schlecht, and V. Välimäki, “Velvet-Noise Feedback Delay Network,” in Proc. Int. Conf. Digital Audio Effects (DAFx), 2020. ↩
-
S. J. Schlecht and E. A. P. Habets, “Feedback Delay Networks: Echo Density and Mixing Time,” IEEE/ACM Trans. Audio, Speech, Lang. Process., vol. 25, no. 2, pp. 374–383, Feb. 2017, doi: 10.1109/TASLP.2016.2635027. ↩
-
S. J. Schlecht, “Allpass Feedback Delay Networks,” IEEE Trans. Signal Process., vol. 69, pp. 1028–1038, 2021, doi: 10.1109/TSP.2021.3053507. ↩
-
S. J. Schlecht and E. A. P. Habets, “Scattering in Feedback Delay Networks,” IEEE/ACM Trans. Audio, Speech, Lang. Process., vol. 28, Jun. 2020. ↩