Skip to content

About

sound moves with the tide

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

TIDE ENGINE — Polyrhythmic Multi‑Head Delay (Time)

Four independent “tape heads” orbit a shared circular buffer at rational divisions (e.g., 3/5/7/11 of a bar). Each head has its own tone, feedback, pan, and mute; cross‑feedback ties them into a playable lattice. Quantize to MIDI clock or tap; humanize with micro‑jitter; momentary Flam and Reverse inject accents.

Newcomer guide

Tide Engine is a playable, polyrhythmic delay instrument. Think of four tape heads circling the same river of audio, occasionally cross-feeding into each other. The goal is a stable, stage-safe system that can still be pushed into chaos on purpose.

Real-time safety rules (audio thread):

  • No heap allocs, no locks, no logging, no I/O.
  • Bounded work per block; no unbounded loops.
  • Denormal-safe processing (flush tiny filter states).

Memory story:

  • Long delay storage expects external PSRAM.
  • Power-of-two ring sizes keep wrap cheap and deterministic.
  • Default ring length is 2^19 samples/channel (~11.9s at 44.1kHz, ~2.0 MiB stereo int16).

Suggested first experiment:

  1. Set Head A to 1/1, Head B to 1/3, mute C/D.
  2. Set mix around 0.45 and xfeed around 0.25.
  3. Listen for the braid where the two divisions interlock and start to pull the stereo field.

For the full signal flow, see docs/ARCHITECTURE.md and docs/SIGNAL_FLOW.md. The diagram lives in docs/SIGNAL_FLOW.md.

Platform

Teensy 4.x + SGTL5000 (Teensy Audio Library), 44.1 kHz / 128‑sample blocks.

Stage-Safe Rules

Anything reachable from AudioStream::update() must be real-time safe:

  • No heap allocation (new, malloc, std::vector growth, String).
  • No locks/mutexes or spin-wait atomics.
  • No logging/I/O (Serial/printf) or file access.
  • Bounded CPU per block; no unbounded loops.

Memory/PSRAM

  • Long delay storage expects external PSRAM.
  • Use a power-of-two ring length so wrap is a bitmask (idx & (size - 1)).
  • Delay buffers are preallocated once at init and never resized.
  • Default ring length: 2^19 samples/channel (~11.9s at 44.1kHz, ~2.0 MiB stereo int16).

For deeper context, see docs/ARCHITECTURE.md and docs/PARAMS.md.

Quick Start

  • Learn the engine in examples/teaching_rig/teaching_rig.ino (polyrhythmic heads, ping-pong xfeed, flutter-heavy shimmer). The teaching rig now cycles its preset bank at runtime from a button on pin 3.
  • For wiring only, start with examples/minimal/minimal.ino.
  • Select your Teensy 4.0/4.1 board and upload.
  • Audio in/out via Audio Shield. Adjust AudioMemory() as needed.
  • teensy/main.cpp now supports tap tempo on pin 3; when built with a MIDI-capable Teensy USB type it also follows USB MIDI clock.

PlatformIO

  • Install PlatformIO Core.
  • Build the project: pio run -e teensy40 or pio run -e teensy41.
  • PlatformIO uses teensy/main.cpp as the entrypoint; examples/minimal/minimal.ino stays as a sketch-only demo.

Lint

  • ./scripts/lint_audio_thread_allocs.sh enforces the no-heap rule in audio-thread code.
  • ./scripts/smoke_desktop_renderer.sh compiles the desktop renderer, lists the preset bank, and renders an impulse through a preset.

Folders

  • core/ — DSP core (engine, heads, clock, xfeed, smoothing, limiter, saturation).
  • core/TidePresetJson.* — desktop preset JSON load/save around the canonical TidePreset surface.
  • desktop/ — offline desktop tools built directly on the core DSP.
  • teensy/ — Teensy adapter (AudioStream wrapper + entrypoint).
  • examples/ — minimal wiring sketch.
  • presets/ — starter presets JSON.
  • docs/ — control map CSV.
  • scripts/ — repo lint checks.

Tests

  • Core DSP tests run on desktop via PlatformIO: pio test -e native
  • See test/README.md for test purpose and regression notes.

Desktop Renderer

  • Build: c++ -std=c++17 -Icore desktop/tide-render.cpp core/*.cpp -o tide-render
  • List presets: ./tide-render --list-presets
  • Render an impulse through a preset: ./tide-render --preset-name "Spring Tide" --impulse 128 --tail-seconds 4 --output out.wav
  • Render a WAV through a preset: ./tide-render --preset-index 1 --input in.wav --output out.wav

Desktop CMake

  • Configure the shared desktop build: cmake -S . -B build
  • Build the offline renderer from CMake: cmake --build build --target tide-render

JUCE VST3

  • The JUCE plugin shell lives in desktop/juce_plugin/ and wraps the same TideEngineCore used by the Teensy build and offline renderer.
  • The current desktop/VST3 manufacturer metadata is BSSoundStudio.
  • The plugin embeds presets/presets.json as its default preset bank and can load/save JSON preset banks from the editor.
  • The plugin can follow the host BPM via the Tempo Source control while still keeping the manual Tempo knob as the fallback/internal tempo.
  • Each head also exposes a plugin-only Division choice parameter so DAW automation can step through common musical ratios without automating raw numerator/denominator pairs.
  • Transport Phase can restart modulation/random timing on host play, and optionally on transport jumps/loops, without doing an unbounded delay-buffer clear in the audio thread.
  • Build with an installed JUCE package or a JUCE checkout: cmake -S . -B build-vst3 -DTIDE_BUILD_JUCE_PLUGIN=ON -DJUCE_DIR=/path/to/JUCE
  • Then build the plugin target: cmake --build build-vst3 --config Release --target TideEngineVST3
  • If you prefer a vendored checkout, place JUCE at extern/JUCE/ and omit JUCE_DIR.
  • Smoke-test the JUCE path locally: JUCE_DIR=/path/to/JUCE ./scripts/smoke_juce_vst3.sh

License

MIT — see LICENSE.

About

sound moves with the tide

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages