Max/MSP externals for higher-order ambisonics (AmbiX: ACN ordering, SN3D
normalization), built as thin wrappers over the AmbiTap library
(ambitap::dsp / ambitap::analysis processors). A Cycling '74 Min-DevKit
package: one external per folder under source/projects/.
Early scaffold. Objects landed so far (multichannel, order as a creation arg):
ambitap.encode~— mono source → higher-order ambisonics. Order is a creation argument (ambitap.encode~ 3); the output is a single multichannel signal of(order+1)²channels (one patch cord for the HOA bus). Attributes:azimuth,elevation,gain. Wrapsambitap::dsp::encoder.ambitap.rotate~— HOA bus → rotated HOA bus, multichannel in and out ((order+1)²channels each). Attributes:yaw,pitch,roll(Euler, Z-Y-X). Wrapsambitap::dsp::rotator; the SH-rotation matrix rebuilds on a worker thread (async_rebuilder) and the audio path reads it wait-free.ambitap.decode~— HOA bus → loudspeaker feeds. Creation args<order> <layout>(layouts: stereo, quad, surround_5_1, hexagon, surround_7_1, cube, octagon, surround_7_1_4); the layout fixes the output channel count. MC out (→mc.dac~). Attributesdecoder_type(mode_match / allrad / epad) andmax_rerebuild the matrix off-thread. Wrapsambitap::dsp::decoder.ambitap.binaural~— HOA bus → binaural stereo (two outlets) via SH-domain HRTF convolution (built-in MIT KEMAR, orders 1–5, or a user SOFA file), with internal head-tracking. Attributes:volume,hrtf_dataset(ls/magls),sofa(path to a SOFA file — measurements are projected onto the SH basis at this object's order and resampled to the host rate; empty reverts to KEMAR),yaw/pitch/roll. The convolver bank is allocated for the host vector size and sample rate indspsetup. Wrapsambitap::dsp::binaural_renderer; linksAmbiTap::fft(Ooura) for the partitioned convolution and libmysofa (fetched by the AmbiTap submodule's CMake withAMBITAP_ENABLE_SOFA=ON) for SOFA.ambitap.bed2hoa~— channel-based surround bed (5.1 / 7.1 / 7.1.4 / …) → HOA, encoding each speaker feed at its canonical direction. Creation args<order> <layout>(same layout set asambitap.decode~; no LFE — route it around the bus). Static encoding matrix; MC in/out.ambitap.mirror~— LR / FB / UD sign-flip mirror (flip_lr/flip_fb/flip_ud). MC in/out.ambitap.format~— FuMa ↔ AmbiX conversion (orders 0–3;direction). MC in/out.ambitap.vmic~— virtual mic: MC HOA → mono directional extraction (azimuth/elevation/max_re).ambitap.directional~— per-direction gain (azimuth/elevation/gain). MC in/out.ambitap.doppler~— variable propagation delay (distance/speed_of_sound/max_distance). MC in/out.ambitap.compress~— spatial-image-preserving compressor, W-keyed (threshold/ratio/attack/release/makeup_gain). MC in/out.ambitap.energyvec~— active-intensity DOA: MC HOA → x / y / z signals (smoothing_time).ambitap.panbin~— direct per-source binaural panner: mono + (azimuth/elevation) → stereo through a per-direction HRTF (order-5 KEMAR SH set), no ambisonic bus, no order-limited blur. Direction changes crossfade click-free via a lock-free convolver-pair handoff; the audio path never allocates. Complements encode~ → binaural~ at small source counts.ambitap.distance~— distance cues for an HOA bus: Doppler delay → 1/r gain (attenuationexponent) → air-absorption low-pass → near-field compensation (dsp::nfc, per-order shelving). Attributes:distance,reference_distance,attenuation,air_absorption,speed_of_sound,max_distance,doppler/nfctoggles. MC in/out.ambitap.xtc~— transaural crosstalk cancellation: stereo/binaural → two loudspeakers at a known symmetric geometry (spandegrees,distancemeters), designed per-geometry from the KEMAR plant viadsp::xtc(regularized 2×2 inversion; gates X1–X6 of the library'sdocs/PERCEPTUAL-VERIFICATION.mdpass in its test suite). Attributes:span,distance,regularization,bypass(ramped, for the listening protocol's A/B). 512-sample latency; output sits ~12 dB below bypass (the gain-ceiling makeup) — loudness-match upstream when comparing.ambitap.room~— shoebox room: mono source → HOA bus carrying direct path + image-source early reflections + a 16-line SH-domain FDN tail (dsp::room, the architecture selected and verified by the library's R1–R10 harness). Creation arg<order>(max 3). Attributes:dim_x/y/z,source_x/y/z,listener_x/y/z,rt60(plusrt60band <hz> <sec>andreflections <6 floats>messages),direct/er/tailtoggles,gain. Fixed ~53 ms latency at 48 kHz (injection alignment inherent to the verified design;latency_samples()exposed for hosts that compensate).
AmbiTap-Max/
├── CMakeLists.txt package build (min-devkit convention)
├── package-info.json
├── source/
│ ├── min-api/ → Cycling '74 min-api (dev: symlink; repo: submodule)
│ ├── min-lib/ → Cycling '74 min-lib (dev: symlink; repo: submodule)
│ └── projects/<object>/ one external each (.cpp + CMakeLists.txt)
└── externals/ built .mxo bundles
AmbiTap is found at the sibling ../AmbiTap by default; override with
-DAmbiTap_ROOT=/path/to/AmbiTap.
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build
# externals land in externals/ (e.g. ambitap.encode_tilde.mxo → object ambitap.encode~)Requires the Cycling '74 min-api/min-lib under source/ (currently symlinked
from an installed Min-DevKit; will be git submodules once this is a repo) and a
sibling AmbiTap checkout.
To use the objects in Max, make Max see this package — symlink (or copy) the
AmbiTap-Max folder into ~/Documents/Max 9/Packages/:
ln -s "$PWD" ~/Documents/Max\ 9/Packages/AmbiTap-MaxThen the externals load and help/<object>.maxhelp opens from each object.
.github/workflows/ci.yml builds all externals universal on macOS and checks
they came out fat. Both this repo and the AmbiTap submodule are public, so the
recursive checkout needs no token.
The product plan lives in the AmbiTap library repo: docs/ROADMAP.md
(wrappers + object line). That document is the authority on what gets built
next. Status of this repo against it:
- Wave 1 (encode / rotate / decode / binaural) — code complete, plus seven
further objects (mirror, format, vmic, directional, doppler, compress,
energyvec). All register MC outputs via the
multichanneloutputspattern where the output is an HOA bus. Channel negotiation still needs in-Max verification (loadambitap.encode~ 5, confirm 36 channels into anmc.*object) — none of these objects has been exercised in a running Max. - Wave 2 — code complete:
binaural~loads user HRTFs via thesofaattribute (librarysofa_reader+decompose_sh, resampled to the host rate), andambitap.bed2hoa~encodes 5.1/7.1/7.1.4 beds into the HOA domain. Like Wave 1, still needs in-Max verification. - Wave 3 (object line) — code complete.
panbin~,distance~(librarydsp::nfc),xtc~(librarydsp::xtc, gates X1–X6 green), androom~(librarydsp::room, whose C++ render passes the same R1–R10 harness that selected its FDN architecture). The perceptual objects (xtc~,room~) still owe the listening pass (bypass rule) from the library'sdocs/PERCEPTUAL-VERIFICATION.mdbefore they ship in a release. - UI:
jit.matrixsoundfield heatmap, JSUI direction picker / polar meter (see the portability plan's UI section).
Note the externals compile as C++20 (AmbiTap requires it); each project's
CMakeLists re-raises CXX_STANDARD after min-posttarget.cmake pins it to 17.