Building From Source

Use this procedure to build the s3g-dsp CLAP plugins from source. Because this is pre-release software, plugin names, parameters, saved states, and included plugins may change.

The supported build and host environment is macOS with REAPER.

Requirements

Smoke Test

The dev preset builds the DSP smoke tests without CLAP bundles:

cmake --preset dev
cmake --build --preset dev
./build/s3g_dsp_smoke
./build/s3g_ambi_effect_displacement_smoke
./build/s3g_ambi_imprint_safety_smoke
./build/s3g_ambi_ray_encoder_smoke
./build/s3g_psd_raw_field_smoke
./build/s3g_psd_raw_field_parameter_audit

Build CLAP Plugins

Build the CLAP plugins:

cmake --preset clap
cmake --build --preset clap

By default, the build fetches CLAP headers with CMake. To use an existing local CLAP checkout, configure manually with S3G_FETCH_CLAP=OFF and S3G_CLAP_INCLUDE_DIR.

cmake -S . -B build-clap \
  -DS3G_BUILD_CLAP_PLUGIN=ON \
  -DS3G_FETCH_CLAP=OFF \
  -DS3G_CLAP_INCLUDE_DIR=/path/to/clap/include
cmake --build build-clap

WORLD speech vocoder support is enabled by default for Ambi Encoder Vox and fetched at a pinned revision. Disable it only when a build without Ambi Vox WORLD analysis is required:

cmake -S . -B build-clap \
  -DS3G_BUILD_CLAP_PLUGIN=ON \
  -DS3G_ENABLE_WORLD=OFF

Audit CLAP Realtime Cost

Timing measurements must use optimized plugins and an optimized audit executable. The dedicated clap-release preset creates a separate Release build without changing the ordinary development or CLAP build directories:

cmake --preset clap-release
cmake --build --preset clap-release --target audit_clap_realtime_representative

The representative target is a focused, report-only manual check spanning a simple 24-channel baseline, zero-input generators, the zero-audio-port NIM Gesture utility, the 64-voice/64-channel Ambi Insect encoder, a wide Ambi decoder, 24-channel Spectral Topology, and 128-channel transform and matrix processors. It intentionally applies no portable pass/fail timing threshold. To audit one product or retain machine-readable results, invoke the executable directly:

./build-clap-release/s3g_clap_realtime_audit \
  --json /tmp/nim-realtime.json \
  --sample-rates 48000,96000 \
  --blocks 64,128,256 \
  build-clap-release/plugins/clap_no_input_mixer/s3g_no_input_mixer.clap \
  org.s3g.s3g-dsp.no-input-mixer-8ch

Each configuration measures a baseline and, where the plugin exposes suitable continuous automatable parameters, a same-block automation-burst stress case. Use --event-burst 0 to measure only steady processing or choose a different burst size. The console's DATA label means that processing completed with valid finite output; inspect the deadline percentage and miss count to judge realtime risk.

After building the full Release tree, the manifest runner can audit every canonical bundle in manifest order and combine the individual reports. --filter accepts a regular expression for narrower families, and every child audit has a timeout so one faulty plugin cannot stall the run indefinitely:

cmake --build --preset clap-release
python3 scripts/run-clap-realtime-audit.py \
  --audit-executable build-clap-release/s3g_clap_realtime_audit \
  --build-root build-clap-release/plugins \
  --full-sweep \
  --output /tmp/s3g-clap-realtime-audit.json

--full-sweep covers 48 and 96 kHz at 32, 64, 128, and 256 frames. During optimization, --affected-only --automation-ladder narrows the run to the processors previously observed missing a deadline and compares 1, 4, 8, 16, and 64 simultaneous events with the same events distributed through a block. The equivalent Release target is:

cmake --build --preset clap-release \
  --target audit_clap_realtime_weak_points

The manual release gate runs at least 10,000 measured blocks per scenario and requires p99 processing time no greater than 75 percent of the buffer deadline on the audit machine. This means at least 99 percent of measured callbacks retain 25 percent timing headroom. Wall-clock deadline misses in the excluded one-percent tail remain in the JSON and console diagnostics, and the gate additionally rejects any scenario whose miss rate exceeds one percent. The criteria are identical in every profile; only the documented supported buffer configurations differ:

The spectral floors include the dense automation case, whose FFT-hop work is concentrated into particular callbacks. Smaller buffers remain available for manual, report-only investigation, but are not claimed as realtime-safe release configurations. The release target completes all nine strict profiles before returning failure, then measures maximum-density Water and Insect at 96 kHz as a report-only advisory. It writes a separate clap-realtime-release-gate-*.json file for every profile plus clap-realtime-release-gates-summary.json, so an early scheduling miss cannot hide later results. Full-density 96 kHz Water/Insect operation is deliberately not a release claim.

cmake --build --preset clap-release \
  --target audit_clap_realtime_release_gate

cmake --build --preset clap-release \
  --target audit_no_input_mixer_controller_realtime

The NIM controller gate covers 104 scenarios: 48 and 96 kHz at 32, 64, 128, and 256 frames, with baseline processing; 1, 4, 8, 16, and 64 simultaneous and sample-distributed parameter events; independent lock-free control publication; and synthetic MIDI 1 traffic matching BU16 Note On, poly pressure, and Note Off gestures plus E16 NRPN CC, command-note, and program-change messages. This verifies the CLAP-facing control paths; it does not measure CoreMIDI, USB hardware, or Cocoa event latency.

This is an offline processor-cost audit: it compares measured CLAP processing time with each sample-rate and block-size deadline. Results are machine-dependent and vary with CPU model, power mode, thermal state, background load, and compiler. The p99 boundary is deliberately robust to the rare operating-system preemptions that an ordinary userspace wall-clock timer cannot distinguish from plug-in execution; every extreme sample and deadline-miss count is still reported rather than discarded. Run the gate on a quiet reference machine without concurrent builds or audits and retain the JSON. Repeat a focused profile when p99 rises or misses cluster: repeatable elevated p99 is a DSP failure and must not be waived, while a miss that moves between unrelated scenarios is scheduler evidence. The one-percent tail allowance is not an audio-xrun allowance. A missed offline deadline identifies risk, but passing does not prove that a host will never underrun, and the guarded live Core Audio audit remains the check for actual callback delivery and HAL overloads.

Probe Realtime Allocations

On macOS, an injected probe can count malloc, calloc, realloc, aligned allocations, and free while each measured CLAP process() call is running. It remains opt-in for ordinary builds, while the clap-release preset enables it so the complete release target cannot silently make a non-allocation claim without evidence:

cmake --preset clap-release
cmake --build build-clap-release \
  --target run_realtime_alloc_probe_tests

python3 scripts/run-clap-realtime-audit.py \
  --audit-executable build-clap-release/s3g_clap_realtime_audit \
  --build-root build-clap-release/plugins \
  --allocation-probe-library build-clap-release/libs3g_realtime_alloc_probe.dylib \
  --allocation-gate \
  --output build-clap-release/clap-allocation-all.json

The probe test target checks the allocator hooks directly and runs a test-only CLAP that deliberately allocates and frees memory inside every measured process() block, confirming that a broken or no-op hook cannot create a false all-zero pass. --allocation-probe-library injects the dylib only into each audit child and implies measurement; --allocation-gate requires a complete probe report for every scenario and zero allocation, deallocation, failure, or invalid-alignment operations without imposing the separate 10,000-block timing threshold. The report separates blocks containing allocations, deallocations, total requested bytes, failures, and the maximum operations in one block. The probe measures only the thread that calls process(); plugin worker-thread activity and direct malloc_zone_* calls are outside its scope. Use it with a local non-hardened diagnostic executable, not a shipped or signed host.

The offline timing and allocation audits do not open or reconfigure an audio device. Use the guarded live audit below to observe actual Core Audio callback delivery and HAL signals.

Build Standalone Apps

macOS standalone app targets are built separately from the CLAP plugin bundle targets:

cmake --preset apps
cmake --build --preset apps

The standalone app targets are:

build-apps/apps/multi_loop_processor_standalone/s3g Processor Multi Loop.app
build-apps/apps/no_input_mixer_standalone/s3g No Input Mixer.app
build-apps/apps/vox_builder/s3g Vox Builder.app

No Input Mixer embeds the same No Input Mixer, Stereo Autogain, and Quad Autogain CLAP implementations used by the plugin builds. It offers stereo, quad, and direct eight-channel Core Audio output and launches monitoring-muted. Check its processor chain with cmake --build build-apps --target audit_no_input_mixer_standalone.

To build a separately installable No Input Mixer app ZIP from the exact Release target, validate the staged package and an independent extraction, exercise the installer in an isolated dry run, and emit a SHA-256 checksum:

./scripts/package-macos-nim-app-prerelease.sh

The app packager uses the same clean-source policy as the CLAP packager. It refuses a dirty tree unless S3G_PACKAGE_ALLOW_DIRTY=1 is supplied for an explicitly non-final test artifact. The packaged installer writes to ~/Applications/s3g No Input Mixer.app without sudo, verifies the app before replacement, and backs up only an identity-matched prior copy.

Vox Builder uses the same pinned WORLD dependency as Ambi Encoder Vox. Its segmentation core can be checked independently with ./build-apps/s3g_vox_builder_smoke.

Audit NIM Core Audio

The NIM standalone includes default-off realtime diagnostics. Choose Show Realtime Diagnostics from the application menu to begin a fresh observation window; Reset clears it and Copy Realtime Diagnostics Report copies the device configuration and counters. The report distinguishes callback-duration overruns, late callback starts, independent host-clock and audio-sample-position discontinuities, HAL overload and abnormal-stop notifications, render errors, embedded CLAP-stage errors, non-finite output, and MIDI drops. A late-start count is advisory scheduler information rather than an xrun by itself; HAL overloads, abnormal stops, sample-position faults, and callbacks that exceed their own period are stronger failure signals.

For repeatable headless testing, build the guarded diagnostic and enumerate devices first. Listing reads device properties only: it does not construct the DSP engine or open AUHAL.

cmake --build build-apps \
  --target s3g_no_input_mixer_coreaudio_diagnostic
./build-apps/apps/no_input_mixer_standalone/s3g_no_input_mixer_coreaudio_diagnostic \
  --list-devices --json /tmp/s3g-coreaudio-devices.json

A live run requires an exact UID, a duration, and the explicit acknowledgement flag. It leaves the selected device's sample rate, buffer size, and default-device status unchanged. The complete Gesture → NIM → selected output-mode chain runs, but monitoring remains muted and the diagnostic verifies that no nonzero sample reaches Core Audio:

./build-apps/apps/no_input_mixer_standalone/s3g_no_input_mixer_coreaudio_diagnostic \
  --device-uid 'DEVICE_UID_FROM_LIST' \
  --mode stereo \
  --warmup-seconds 5 \
  --duration-seconds 60 \
  --confirm-live-audio \
  --json /tmp/s3g-nim-coreaudio.json

Use --mode quad or --mode direct8 only with a device exposing enough outputs. The diagnostic opens the device's full output layout to reproduce the standalone wrapper's channel cost. Starting AUHAL can awaken or contend for hardware even while muted, so stop other critical audio work first. Realtime findings remain report-only unless --strict is supplied.

Install Local Build

Preview and then install the CLAP products:

./scripts/install-clap-bundles.sh --dry-run
./scripts/install-clap-bundles.sh

# Optional: rename verified current installs without replacing their binaries.
./scripts/install-clap-bundles.sh --canonicalize-only

The default install location is:

~/Library/Audio/Plug-Ins/CLAP/s3g-dsp/

The install script reads scripts/clap-bundles.tsv and copies every manifest-listed product from build-clap/plugins/ under canonical family-first filenames. For example, Modal and Fault install as s3g_ambi_encoder_modal_16.clap and s3g_processor_fault_8ch.clap. These filesystem names mirror the host-browser families, while the CLAP IDs remain stable for saved-project compatibility.

A normal run validates the complete source and destination set before changing the destination, then stages and copies the manifest-listed bundles. On the first upgrade from the former flat layout, it identity-checks recognized s3g-dsp bundles in the parent CLAP folder and moves those old copies to ~/Library/Application Support/s3g-dsp/CLAP Backups/<timestamp-pid>/Previous CLAP Root/ after installing the current set. --canonicalize-only instead moves verified current installations into the nested folder without replacing their binaries. The installer uses scripts/clap-legacy-bundles.tsv to recognize earlier filenames; unrelated plugins are not pruned. The --dry-run form reports the planned installs, migrations, and alias backups without writing anything.

After a successful run, ~/Library/Application Support/s3g-dsp/clap-install-receipt.tsv records the filenames owned by this package so later updates can distinguish them from other CLAP products.

Validate

CTest now registers the DSP smokes, numerical regressions, structural audits, installer migration test, focused CLAP ownership probes, and tagged state-compatibility fixtures exported by the released v0.5.0-pre binaries. Those fixtures contain deterministic non-default states plus old-release parameter ID/value sidecars. They cover representative panners, environmental and generative encoders, spectral processors, and larger state schemas; each current plugin must reproduce the released values, keep every parameter finite and in range, migrate and reload without value loss, and produce deterministic re-saved bytes. Build first, then run all tests or select the non-NIM release label:

cmake --build --preset clap-release
ctest --test-dir build-clap-release --output-on-failure
ctest --test-dir build-clap-release --output-on-failure -L non_nim

The sanitizer preset provides a separate RelWithDebInfo tree with AddressSanitizer and UndefinedBehaviorSanitizer enabled. Functional audio, finiteness, clipping, and discontinuity assertions remain active there; wall-clock realtime budgets are enabled only in the uninstrumented Release configuration so sanitizer overhead cannot create a false performance failure:

cmake --preset clap-sanitize
cmake --build --preset clap-sanitize
ctest --test-dir build-clap-sanitize --output-on-failure -L non_nim

Check the canonical CLAP inventory, installer migration behavior, static documentation, and advisory GUI conventions directly when investigating one layer:

python3 scripts/check-clap-bundle-manifest.py \
  --build-root build-clap-release/plugins \
  --defer-descriptor-version
./tests/clap_bundle_installer_smoke.sh
python3 scripts/check-docs.py
./scripts/audit-gui-style.sh

If clap-validator is installed, the manifest runner validates each bundle in an isolated subprocess with a per-bundle timeout, retains an individual log, and reports every failure instead of allowing one crash or hang to hide the rest:

python3 scripts/run-clap-validator-manifest.py \
  --validator "$(command -v clap-validator)" \
  --build-root build-clap-release/plugins \
  --output build-clap-release/clap-validator-manifest.json

For a single bundle, invoke the validator directly. For example:

clap-validator validate --only-failed \
  build-clap/plugins/clap_macro_shred/s3g_macro_shred_mono.clap \
  build-clap/plugins/clap_macro_shred/s3g_macro_shred.clap \
  build-clap/plugins/clap_macro_shred/s3g_24ch_macro_shred.clap \
  build-clap/plugins/clap_psd_raw_field/s3g_fault.clap

On a quiet macOS release machine, the following target first runs the allocation-hook self-test and known-bad positive-control CLAP, then builds the complete manifest and runs the non-NIM CTest label, manifest and Objective-C audits, an isolated exact-inventory validator sweep, an exact 96-plugin non-NIM allocation gate, all strict realtime profiles with allocation evidence, and the 96 kHz environmental advisory sequentially. It refuses empty CTest selections and stale, malformed, partial, or mismatched JSON evidence. Each phase keeps its own log and the summary is written to build-clap-release/non-nim-release-checks.json:

cmake --build --preset clap-release \
  --target release_checks_non_nim

Package a Pre-release

After a complete CLAP build, create the macOS zip used for GitHub pre-releases:

./scripts/package-macos-clap-prerelease.sh

The archive is written to dist/ and includes every manifest-listed CLAP product, Install s3g-dsp CLAPs.command, the active and legacy manifests, VOT wavetables, the Ambi Vox demo voicebank, the project license, and third-party notices. Packaging rebuilds and consumes build-clap-release/plugins/ by default, verifies that its CMake cache names this source tree and a Release configuration, and refuses a dirty source tree. Use S3G_CLAP_BUILD_DIR to select another verified Release build of this source tree; S3G_PACKAGE_ALLOW_DIRTY=1 is reserved for explicitly non-final test packages. Packaging and local installation consume the same manifest, so the archive inventory, installed filenames, and generated product list cannot drift independently.

Before signing, the packager synchronizes each staged bundle's version metadata from its runtime CLAP descriptor; component versions remain independent of the suite archive version. It then requires exactly 98 manifest IDs, arm64-only executables, matching descriptor, plist, ID, and host-name metadata, valid bundle signatures, and a successful isolated installer dry-run. The same checks run again after extracting the finished zip, and a .zip.sha256 checksum is written beside it. Bundles are ad-hoc signed by default for integrity verification. Set S3G_CODESIGN_IDENTITY to a Developer ID Application identity to enable hardened runtime and a secure timestamp, and set S3G_RELEASE_VERSION and S3G_RELEASE_DATE to override the pre-release metadata. A public release that opens without manual Gatekeeper approval must also be submitted to Apple notarization; a separately signed and notarized installer package is the preferred distribution path.

Cleanup

Reconfiguring an existing CMake directory does not necessarily remove artifacts produced under earlier target names. The manifest-driven installer and packager ignore unlisted build artifacts, and the installer backs up recognized aliases already present in the user plugin folder. Use --dry-run to inspect that cleanup before applying it; do not remove every s3g-named bundle because the folder may also contain products from other s3g projects.