Skip to content

JDSherbert/Audio-DSP-HAAS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

image

Audio DSP: Haas Effect

Stars Badge

Forks Badge

Watchers Badge

Issues Badge


Audio Processing License




Overview

A C++ implementation of the Haas effect (also known as the precedence effect), intended as a learning resource for understanding how inter-channel delay creates perceived stereo width from a mono source.

The Haas effect is a psychoacoustic phenomenon: when the same sound reaches both ears with a delay of 1–40ms, the brain fuses them into a single perceived sound and localises it toward the ear that heard it first. This is exploited in audio production to widen a mono signal into a convincing stereo image, without creating a perceptible echo.

For a deeper dive into delay in general, see the Audio Delay repository.

image


Files

File Description
Haas.h / .cpp Haas effect implementation — stereo delay with configurable delayed channel
main.cpp Example: applies the effect to a 440Hz mono sine wave, demonstrates both channel configurations and two delay times

How It Works

Both channels receive the same mono input signal. One channel (the leading channel) passes through unmodified. The other (the delayed channel) is held in a circular buffer and output after the configured delay time:

monoInput ──►── Left  (leading) ──────────────────────────────────► leftOut
            │
            └──► Right (delayed) ──► [ circular buffer, N samples ] ──► rightOut

The brain perceives this timing difference as spatial information and localises the sound toward the leading channel. Swapping which channel is delayed flips the perceived position.

The delay is implemented as a fixed-size circular buffer — identical in structure to the reverb implementation — so no memory is allocated or freed during audio processing.


Delay Time Reference

Delay Perceptual Effect
< 1ms Phase cancellation / comb filtering — not a Haas effect
1 – 10ms Subtle width, tight image
10 – 30ms Clear stereo spread, natural sounding
30 – 40ms Wide image, approaching the limit of fusion
> 40ms Perceived as a distinct echo rather than spatial width

Parameters

Parameter Type Range Description
delayInMilliseconds int 1 – 40ms Delay applied to the delayed channel.
delayedChannel DelayedChannel Left or Right Which channel is delayed. The other channel leads.
sampleRate float > 0.0 Audio sample rate in Hz. Used to convert ms to samples.

DelayedChannel enum:

Value Leading Channel Perceived Image
DelayedChannel::Right Left Sound perceived from the left
DelayedChannel::Left Right Sound perceived from the right

Usage

Basic

// 20ms delay, right channel delayed, at 44100Hz
Sherbert::Haas haas(20, Sherbert::Haas::DelayedChannel::Right, 44100.0f);
 
float leftOut  = 0.0f;
float rightOut = 0.0f;
 
// Pass the same mono signal to both inputs
haas.ProcessSample(monoIn, monoIn, leftOut, rightOut);

Flipping the Perceived Position

// Flip which channel is delayed at runtime — no reset required
haas.setDelayedChannel(Sherbert::Haas::DelayedChannel::Left);

Changing Delay Time

// setDelayInMilliseconds calls reset() internally to avoid buffer artefacts
haas.setDelayInMilliseconds(5);

Resetting State

haas.reset();

API Reference

Method Description
Haas(delayInMs, delayedChannel, sampleRate) Construct with initial parameters.
ProcessSample(leftIn, rightIn, leftOut, rightOut) Process one stereo sample pair. Leading channel passes through; delayed channel is buffered.
reset() Clears the delay buffer and resets the write index.
setDelayInMilliseconds(value) Update delay time. Calls reset() internally.
setDelayedChannel(value) Swap which channel is delayed. Does not call reset().
getDelayInMilliseconds() Returns current delay time in ms.
getDelayedChannel() Returns current DelayedChannel value.
getSampleRate() Returns the sample rate set at construction.

Limitations & Next Steps

No wet/dry mix — this implementation outputs the delayed channel fully wet. In a production context you would typically blend the delayed channel with the dry signal to control the intensity of the effect.

Mono input assumed — the ProcessSample API accepts separate leftIn and rightIn values, but the Haas effect only makes sense when both inputs carry the same mono signal. Passing a true stereo source will produce unpredictable results.

No delay time modulation — some production Haas implementations slowly drift the delay time to avoid phase cancellation artefacts when the effect is mixed to mono. This would require an LFO similar to the phaser implementation.

If you want to explore further, the natural next steps from here are:

  • A wet/dry mix parameter
  • Slow LFO modulation of the delay time to reduce mono compatibility issues
  • Mid/Side processing — applying the Haas effect only to the Side signal to preserve mono compatibility

About

Simple C++ implementation of the haas technique, with brief explanation.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages