How I write code

Next article I’ll post an update of our wave table oscillator, but first I’ll take the opportunity to discuss how I write code these days. Maybe it will help make sense of some of the choices in the code I post going forward.

I tend to build all DSP units as inlines in header files

True story: Recently, I moved an audio plug-in project I was developing on the Mac in Xcode, to Windows and Visual Studio. I was shocked to see that my source files had disappeared! There was only the main implementation cpp file (not counting the plugin framework), and my headers files. All files were backed up, of course, but it was still unsettling—what could have happened? Then it sank in—I’d written most of the good stuff in header files, so that outside of the plug-in framework, there was indeed only one cpp file—leveraging 28 header files.

The main reason my DSP functions reside in header files is that I make my basic functions inline-able for speed. In a perfectly orderly world, that still might be a header file for the smallest and most critical functions, and a companion C++ source file (.cpp) for the rest. But it’s faster to code and make changes to a single file instead of bouncing between two. And I need only include the header where I use it, instead of also pulling in and marking companion cpp files for compilation.

Further, I write “atomic” DSP components that handle basic functions, and build more complex components from these atomic functions. For instance, I have a delay line function, from which I make an allpass delay. Writing a reverb function can be very short and clear, combining these basic functions with other filter functions and feedback. And feedback is easy because all components process a sample at a time instead of blocks. Examples of my library files: OnePole.h, Biquad.h, StateVar.h, DelayLine.h, FIR.h, Noise.h, Gain.h, ADSR.h, WaveTableOsc.h.

Note that “inline” is a request to the compiler. But compilers are pretty good about honoring it. Remember, inline matters most for small functions, where function call overhead is a bigger consideration. And small functions are easiest to inline, so there’s little reason for a compiler to not comply. If you’re concerned, just list and examine the preprocessor output of a source file to see it.

By the way, the usual argument against inlines—“they lead to bloated code”—doesn’t apply much in the DSP context. These are not large functions used many places in your code. They are built for efficiency. The process routines are localized to your audio processing function, and the setting routines mostly in your plug-in’s parameter handling code.

My DSP units are designed for individual samples, not blocks of samples

Dedicated DSP chips usually process audio a sample at a time. But DSP run on a host computer’s process must handle audio a buffer at a time, to minimize the overhead of context switching. So, if you look at open source DSP libraries, you’ll see that many are written to operate on a buffer of samples.

I don’t do that—my inline functions process a single sample at a time. Of course, you can easily wrap that in a for loop, perhaps partially unrolled to minimize loop overhead. The the next process that acts on the entire buffer, then the next. Or you can string them together, one after the other, to complete your entire algorithm one sample at a time, with an outer loop to iterate through the buffer. The former might work better do the caching advantages, at the expense of more loop overhead. But it’s easier to make this choice with single-sample processes than for a library that’s entirely optimized for buffer processing.

I usually write my DSP units as templates

Mainly, I template them to handle float or double. I use double when developing, but have the option of float available. Filters are a case in which I’ll always use double. For a wavetable oscillator, I want double parameters but float wavetables. A delay line element might be float or double depending on the need. I’d rather build the choice into the DSP unit than to run into a different need and have to take time to rewrite the DSP unit or make a new version of it.

I tend to avoid posting templated stuff on my website, because it can be a distraction from what I’m trying to show.

No virtual functions

I don’t want a vtable. I’m not going to inherit from these basic DSP functions anyway, they are built to do one thing efficiently.

Minor detail: I wrap each DSP header file in a namespace. (I use namespace ESP—which stand for “EarLevel Signal Processing”.) Then I can be lazy with my class names without concern of one day having a namespace collision issue (my “Biquad” versus another “Biquad” built into the plug-in library, for instance).

This entry was posted in Source Code. Bookmark the permalink.

9 Responses to How I write code

  1. Eric Nichols says:

    Hi Ni!
    Just happened to catch your site right after you made a new post….that doesn’t happen often. 🙂
    What? You aren’t writing everything in LISP? 🙂
    Have a great week!

    • Nigel Redmon says:

      Hi Eric! At least someone reads these things…Well, LISP is not an easy choice for real time DSP, as you probably know, and even tougher for wedging into constraints of things like audio processing plug-ins. I’m fully thankful to no longer be constrained to 56k assembly language, so C++ isn’t so bad 😉 Plus, I’m also pretty happy for C++11 and later improvements, and enjoying it more these days…

  2. Miles Egan says:

    I see the advantages of doing things this way. I’ve been working on my own DSP library and one of my goals was to make the components modular, so they could in theory be linked together at run time like a software modular. I haven’t figured out a way to do this without vtables though. Any ideas?

    And yes, someone is definitely reading your articles. Thanks for taking the time to write them and share your knowledge!

  3. Bart says:

    I understand you inline for 2 reasons: (1)no function call overhead (2) you don’t need to think about which cpp files to add to your projects, as the primitives consist of inline functions only. No duplicate symbols complaints from your linker when using inline.
    I fail to get it…
    (1)this benefit is thrown completely out of the window with sample based processing, a technique comparable with driving to the shop 10 times when you want to fill your fridge with 10 items.
    (2)just throw all your cpp files into a library that you link in. The linker will happily throw out any unused code out of the executable. Yes your function prototypes are redundant, but it is a handy place to fully comment the usage of the API. Function prototypes (or classes) are a nice TOC/summary on itself.
    But i guess… probably I misunderstand the full context or goal/target of your code…

    • Nigel Redmon says:

      Don’t like your analogy—if my atomic action is equivalent to placing a can of tuna into your cart, it does not imply driving to the store each time you want to do that…

      • Bart says:

        I agree the analogy is over-the-top, I should have used more nuance, I apologize.
        In my embedded DSP world adding loops at a higher level than where the action is, has the effect of frequent pipeline flushing and memory io stalling, doing away with the benefits of DSP (multiple MACS per cycle). Driving to the store would indeed be a lot worse even.

  4. Bart says:

    PS about building upon primitives: that I fully endorse. It makes your code easy to maintain (limited areas of change) and smarter (abstraction is powerful), and enhances re-use and portability.

  5. Bart says:

    On second thought… when you rely on optimizations done mostly by the compiler itself (as opposed to hand-optimizing), inlining can make sense… The compiler should be smart enough to replace calls to inlines by their code and then use optimisation techniques like loop unrolling, vector processing (SIMD operations), re-ordering, pipeline fill enhancing, branch prediction, io serializing, etc… on the higher level code. Thinking of it even more… this could be quite powerful when using multiple layers (inline functions calling inlines), the compiler has several degrees of freedom to optimize depending on the specific use of or order of primitives.

Leave a Reply

Your email address will not be published. Required fields are marked *