Protect Your Ears

How to prevent mistakes in audio code from causing hearing damage.

Be careful when developing audio software while wearing headphones!

It’s really easy to make a tiny mistake in your DSP code and get extremely loud, screaming feedback as output. That’s certainly unpleasant when using loudspeakers, but downright dangerous on headphones. I’ve had it happen several times and it’s not fun.

Of course, sometimes you do need to use headphones — if only to stop scaring the cat with those weird noises you’re making — and so it’s smart to protect your ears when you do.

Why does this happen?

Digital audio consists of floating-point numbers in the range from –1.0 to +1.0, but there is nothing stopping you from outputting larger numbers or even inf for infinity or NaN for “Not a Number”. Trust me, those don’t sound very nice…

It’s OK for audio to go outside the [–1, 1] range temporarily, but by the time the sound gets to the output device you will want to limit it. The operating system may automatically clip the audio so it stays in the allowed range, but on Macs at least, the system doesn’t appear to limit or clip the sound at all and it will be screaming loud.

Even if you didn’t make a programming mistake, there still can be garbage in the audio buffers. For performance reasons JUCE does not zero out the audio buffers it gives you, and if you’re writing a synth you’re supposed to overwrite all the samples in these buffers with proper values. If you don’t, random garbage gets played through your speakers — or worse, headphones. Ouch!

Tips for keeping your hearing intact

To avoid such unpleasant surprises, I suggest placing some kind of limiter at the end of your processing chain, either in the plug-in itself or in your DAW. Or for testing, use a DAW such as REAPER that automatically mutes the sound when it detects that the levels are too high.

If you make a code change and feel like it may cause problems, then:

Unfortunately, I’ve found that screaming feedback tends to happen when I don’t expect it. Recently I had a silly UI bug that made it possible to drag a filter cutoff slider too far, giving a cutoff frequency that was higher than Nyquist. Instant ear explosion!

Because of these kinds of surprises, I suggest building some automatic protection into your plug-in as well. Just in case.

Adding a limiter into your plug-in

What I do personally is add the following code to my project, in a header file named ProtectYourEars.h:

#pragma once

#include <JuceHeader.h>

inline void protectYourEars(float* buffer, int sampleCount)
{
    if (buffer == nullptr) { return; }
    bool firstWarning = true;
    for (int i = 0; i < sampleCount; ++i) {
        float x = buffer[i];
        bool silence = false;
        if (std::isnan(x)) {
            DBG("!!! WARNING: nan detected in audio buffer, silencing !!!");
            silence = true;
        } else if (std::isinf(x)) {
            DBG("!!! WARNING: inf detected in audio buffer, silencing !!!");
            silence = true;
        } else if (x < -2.0f || x > 2.0f) {  // screaming feedback
            DBG("!!! WARNING: sample out of range, silencing !!!");
            silence = true;
        } else if (x < -1.0f) {
            if (firstWarning) {
                DBG("!!! WARNING: sample out of range, clamping !!!");
                firstWarning = false;
            }
            buffer[i] = -1.0f;
        } else if (x > 1.0f) {
            if (firstWarning) {
                DBG("!!! WARNING: sample out of range, clamping !!!");
                firstWarning = false;
            }
            buffer[i] = 1.0f;
        }
        if (silence) {
            memset(buffer, 0, sampleCount * sizeof(float));
            return;
        }
    }
}

The DBG macro is from JUCE and prints a message to the debug console. You’re not really supposed to write to the console from your audio thread, but this is an exceptional situation that you definitely want to know about.

Then in the AudioProcessor, at the end of processBlock, call this on the audio buffers:


#ifdef JUCE_DEBUG
protectYourEars(buffer.getWritePointer(0), buffer.getNumSamples());
protectYourEars(buffer.getWritePointer(1), buffer.getNumSamples());
#endif

The protectYourEars function handles the following situations:

The warning messages are there to let you know why the output is suddenly silent.

Since protectYourEars is only intended for debugging, you’d remove this from release builds once you’re sure the DSP has no more eardrum-bursting bugs.

Tip: You may want to disable the lines that clip the audio to –1 or 1, if you don’t mind the sound going a little over the limits. This usually won’t hurt, whereas clipping creates an annoying distortion. In certain situations, you may even want to disable protectYourEars altogether. For example, I was testing some filtering code in PluginDoctor and got a weird frequency response. It took me a while to realize that the filter had gain higher than 0 dB and that protectYourEars was preventing the signal from becoming too loud. Normally that kind of protection is what you want, but when you’re analyzing the behavior of the DSP and not actually producing any sound, you may want to turn it off to avoid confusion.

Even with these protections it’s still possible to create loud, unpleasant sounds, but at least it protects you from the worst situations. I still do most of my testing and debugging with the volume set as low as possible, especially when wearing headphones. Your ears matter, especially as an audio programmer!