Skip to content

About

Real-time audio recorder with delay and reverb — DSP core in C++, compiled to WebAssembly via Emscripten, UI in Web Audio API.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

Echo Chamber

Real-time audio recorder with delay and reverb — DSP core in C++, compiled to WebAssembly via Emscripten, UI in Web Audio API.

Built as a browser-deployable demonstration of the same DSP topology used in EchoPsychFX (JUCE/C++ plugin).

Live demo →


Architecture

Mic (MediaStream)
  └─► ScriptProcessorNode (JS glue)
        ├─► copy Float32 samples into WASM HEAPF32 input buffer
        ├─► EchoChamber_process() [C++ / WASM]
        │     ├─ DelayLine      — ring buffer + linear interpolation + feedback loop
        │     └─ ReverbEngine   — Schroeder network (4× comb + 2× all-pass + pre-delay)
        └─► copy WASM output buffer back to Web Audio output

All DSP runs in echo_chamber.cpp. JavaScript handles only:

  • Audio I/O (Web Audio API, MediaRecorder)
  • WASM memory bridge (HEAPF32 reads/writes)
  • UI and parameter forwarding

DSP Details

Delay Line (DelayLine)

A power-of-2 RingBuffer with linear interpolation for sub-sample accuracy. Feedback is clamped to 0.95 to prevent energy divergence. Parameters are smoothed with a one-pole filter (coefficient ≈ 0.995) to eliminate zipper noise on real-time parameter changes.

Same topology as the delay line in EchoPsychFX. The main difference in the JUCE version: the buffer is allocated in prepareToPlay() and the feedback path runs through a dsp::DelayLine<float> with identical ring arithmetic.

Reverb Engine (ReverbEngine)

Schroeder / Moorer / Gardner architecture:

  1. Pre-delay — RingBuffer, up to 500 ms. Encodes perceived room size (Gardner 1992: pre-delay < 20 ms → small room, > 50 ms → large hall).
  2. 4× parallel comb filters — delay lengths are mutually prime (1557, 1617, 1491, 1422 samples at 44100 Hz) to avoid beating artefacts. Feedback gain is derived from target RT60:
    g = 10 ^ (-3 × delayTime / RT60)
    
  3. 2× series all-pass diffusors — Schroeder all-pass (|H(f)| = 1) breaks up spectral coloration from the comb bank. Gain = 0.5 (Schroeder's recommendation).

This is the same topology as Freeverb (Jezar at Dreampoint, 2000), which is itself the canonical open-source implementation of the Schroeder model.

Parameter Smoothing (Smoother)

One-pole IIR smoother applied per-sample to every parameter. Without this, fast slider movement causes audible zipper noise (step discontinuities at block boundaries). The same pattern is used in every JUCE AudioProcessor that handles real-time parameter automation.


Building

Prerequisites

# Install Emscripten SDK
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh   # Windows: emsdk_env.bat

Verify: emcc --version should print a version string.

Compile

cd echo_chamber
chmod +x build.sh
./build.sh

Output: dist/echo_chamber.js (WASM binary inlined as base64 via SINGLE_FILE=1).

Run

# Serve locally — required because WASM needs HTTP (not file://)
python3 -m http.server 8080
# open http://localhost:8080/echo-chamber.html

Or drop echo-chamber.html and dist/echo_chamber.js (renamed to echo_chamber.js) into any web server directory.


File Structure

echo_chamber/
├── echo_chamber.cpp     # C++ DSP core (delay, reverb, WASM exports)
├── echo-chamber.html    # Browser UI + Web Audio / WASM glue
├── build.sh             # Emscripten build script
├── dist/
│   └── echo_chamber.js  # Generated — Emscripten output (WASM + glue, single file)
└── README.md

Relation to EchoPsychFX

The DelayLine and ReverbEngine classes here are a simplified version of the processing chain in EchoPsychFX (JUCE VST3/AU). The main differences:

Echo Chamber (WASM) EchoPsychFX (JUCE)
Delay Ring buffer, linear interp juce::dsp::DelayLine, Lagrange interp
Reverb Algorithmic Schroeder Algorithmic + convolution option
Parameter smoothing Manual one-pole juce::SmoothedValue
Threading Main thread (ScriptProcessor) Dedicated audio thread
Format WebAssembly VST3 / AU / CLAP

The C++ DSP classes (RingBuffer, CombFilter, AllpassFilter, Smoother) are designed to be portable — they compile cleanly for JUCE, Wwise, or Emscripten targets with no platform-specific dependencies.


License

MIT — Aliz Pasztor · alizpasztor.com · Trailblaiz

About

Real-time audio recorder with delay and reverb — DSP core in C++, compiled to WebAssembly via Emscripten, UI in Web Audio API.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages