Skip to content

Chowdhury-DSP/chowdsp_wdf

Repository files navigation

CI License codecov arXiv Try it on godbolt online

Wave Digital Filters

chowdsp_wdf is a header only library for implementing real-time Wave Digital Filter (WDF) circuit models in C++.

More information:

Usage

Since chowdsp_wdf is a header-only library, it should be possible to use the library simply by including include/chowdsp_wdf/chowdsp_wdf.h. However, it is recommended to use the library via CMake.

add_subdirectory(chowdsp_wdf)
target_link_libraries(my_cmake_target PUBLIC chowdsp_wdf)

Dependencies

  • C++14 or higher
  • CMake (Version 3.1+, optional)
  • XSIMD (Version 8.0.0+, optional)

Basic Usage

A basic RC lowpass filter can be constructed as follows:

namespace wdft = chowdsp::wdft;
struct RCLowpass {
    wdft::ResistorT<double> r1 { 1.0e3 };     // 1 KOhm Resistor
    wdft::CapacitorT<double> c1 { 1.0e-6 };   // 1 uF capacitor
    
    wdft::WDFSeriesT<double, decltype (r1), decltype (c1)> s1 { r1, c1 };   // series connection of r1 and c1
    wdft::PolarityInverterT<float, decltype(s1)> i1 { s1 };                 // invert polarity
    wdft::IdealVoltageSourceT<double, decltype (s1)> vs { s1 };             // input voltage source
    
    // prepare the WDF model here...
    void prepare (double sampleRate) {
        c1.prepare (sampleRate);
    }
    
    // use the WDF model to process one sample of data
    inline double processSample (double x) {
        vs.setVoltage (x);

        vs.incident(i1.reflected());
        i1.incident(vs.reflected());

        return wdft::voltage<double> (c1);
    }
};

More complicated examples can be found in the examples repository.

Using XSIMD

There are two specific situations where you may want to use SIMD intrinsics as part of your WDF model:

  1. You want to run the same circuit model on several values in parallel. For example, maybe you have a WDF model of a synthesizer voice, and want to run several voices, like for a polyphonic synthesizer.
  2. You have a circuit model that requires an R-Type adaptor. The R-Type adaptor requires a matrix multiply operation which can be greatly accelerated with SIMD intrinsics.

In both cases, to use SIMD intrinsics in your WDF model, you must include XSIMD in your project before chowdsp_wdf.

#include <xsimd/xsmd.hpp> // this must be included _before_ the chowdsp_wdf header!
#include <chowdsp_wdf/chowdsp_wdf.h>

For case 2 above, simply construct your circuit with an R-Type adaptor as desired, and the SIMD optomizations will be taken care of behind the scenes!

For case 1 above, you will want to construct your WDF model so that the circuit elements may process XSIMD types. Going back to the RC lowpass example:

namespace wdft = chowdsp::wdft;

// Define the WDF model to process a template-defined FloatType
template <typename FloatType>
struct RCLowpass {
    wdft::ResistorT<FloatType> r1 { 1.0e3 };     // 1 KOhm Resistor
    wdft::CapacitorT<FloatType> c1 { 1.0e-6 };   // 1 uF capacitor
    
    wdft::WDFSeriesT<FloatType, decltype (r1), decltype (c1)> s1 { r1, c1 };   // series connection of r1 and c1
    wdft::PolarityInverterT<FloatType, decltype(s1)> i1 { s1 };                // invert polarity
    wdft::IdealVoltageSourceT<FloatType, decltype (s1)> vs { s1 };             // input voltage source
    
    // prepare the WDF model here...
    void prepare (double sampleRate) {
        c1.prepare ((FloatType) sampleRate);
    }
    
    // use the WDF model to process one sample of data
    inline FloatType processSample (FloatType x) {
        vs.setVoltage (x);

        vs.incident (i1.reflected());
        i1.incident (vs.reflected());

        return wdft::voltage<FloatType> (c1);
    }
};

RCLowpass<xsimd::batch<double>> myFilter; // instantiate the WDF to process an XSIMD type!

If you are using chowdsp_wdf with XSIMD, please remember to abide by the XSIMD license.

Citation

If you are using chowdsp_wdf as part of an academic work, please cite the library as follows:

@article{chowdhury2022chowdspwdf,
        title = {chowdsp_wdf: An Advanced C++ Library for Wave Digital Circuit Modelling},
        author = {Chowdhury, Jatin},
        year = {2022},
        journal={arXiv preprint arXiv:2210.12554}
        doi = {10.48550/ARXIV.2210.12554},
        url = {https://arxiv.org/abs/2210.12554},
}

Resources

The design and implementation of the library were discussed on The Audio Programmer meetup in December 2021. The presentation can be watched on YouTube.

The following academic papers may also be useful:

[1] Alfred Fettweis, "Wave Digital Filters: Theory and Practice", 1986, IEEE Invited Paper, link.

[2] Julius Smith, "Wave Digital Filters", (Chapter from Physical Audio Signal Processing) link.

[3] David Yeh and Julius Smith, "Simulating Guitar Distortion Circuits Using Wave Digital And Nonlinear State Space Formulations", Proc. of the 11th Int. Conference on Digital Audio Effects, 2008, link.

[4] Kurt Werner, et al., "Wave Digital Filter Modeling of Circuits with Operational Amplifiers", 24th European Signal Processing Conference, 2016, link.

[5] Kurt Werner, et al., "Resolving Wave Digital Filters with Multiple/Multiport Nonlinearities", Proc. of the 18th Int. Conference on Digital Audio Effects, 2015, link.

[6] Kurt Werner, "Virtual Analog Modeling of Audio Circuitry Using Wave Digital Filters", PhD. Dissertation, Stanford University, 2016, link.

[7] Jingjie Zhang and Julius Smith, "Real-time Wave Digital Simulation of Cascaded Vacuum Tube Amplifiers Using Modified Blockwise Method", Proc. of the 21st International Conference on Digital Audio Effects, 2018, link.

Credits

The diode models in the library utilise an approximation of the Wright Omega function based on Stefano D'Angelo's implementation, which is licensed under the MIT license.

Many thanks to the following individuals who have contributed to the theory, design, and implementation of the library:

Licensing

The code in this repository is open source, and licensed under the BSD 3-Clause License. Enjoy!