diff --git a/.github/workflows/fix_style.yml b/.github/workflows/fix_style.yml
index 38a723bd7..9a4d168e9 100644
--- a/.github/workflows/fix_style.yml
+++ b/.github/workflows/fix_style.yml
@@ -31,6 +31,8 @@ jobs:
"cube/",
"utils/",
"resources/",
+ # Immutable candidate reference archive; validated by its original hashes.
+ "field/HydraPulse/references/r0/",
)
output = subprocess.check_output(
[
diff --git a/.github/workflows/hydrapulse.yml b/.github/workflows/hydrapulse.yml
new file mode 100644
index 000000000..558b7fcb1
--- /dev/null
+++ b/.github/workflows/hydrapulse.yml
@@ -0,0 +1,105 @@
+name: HydraPulse qualification
+on:
+ push:
+ paths:
+ - 'field/HydraPulse/**'
+ - '.github/workflows/hydrapulse.yml'
+ pull_request:
+ paths:
+ - 'field/HydraPulse/**'
+ - '.github/workflows/hydrapulse.yml'
+ workflow_dispatch:
+permissions:
+ contents: read
+concurrency:
+ group: hydrapulse-${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+jobs:
+ host:
+ runs-on: ubuntu-24.04
+ strategy:
+ fail-fast: false
+ matrix:
+ mode: [release, ubsan, asan-ubsan]
+ defaults:
+ run:
+ working-directory: field/HydraPulse
+ steps:
+ # Match root checkout behavior: early auth cleanup traverses orphan legacy gitlinks.
+ - uses: actions/checkout@v7
+ - name: Repository and Python checks
+ run: |
+ python3 -S tools/generate_presets.py --check
+ python3 -S tools/validate_repo.py
+ python3 -S ../../DAISY_QAE/validate_daisy_code.py .
+ python3 -S -m unittest discover -s tests/python -v
+ - name: Configure
+ env:
+ HPF_MODE: ${{ matrix.mode }}
+ CXX: clang++
+ run: |
+ case "$HPF_MODE" in
+ release) flags="-DCMAKE_BUILD_TYPE=Release" ;;
+ ubsan) flags="-DCMAKE_BUILD_TYPE=Debug -DHPF_UBSAN_ONLY=ON" ;;
+ asan-ubsan) flags="-DCMAKE_BUILD_TYPE=Debug -DHPF_ENABLE_SANITIZERS=ON" ;;
+ *) exit 1 ;;
+ esac
+ cmake -S . -B build-ci -DBUILD_TESTING=ON $flags
+ - name: Build and test
+ run: |
+ cmake --build build-ci --parallel 2
+ ctest --test-dir build-ci --output-on-failure
+ - name: Sonic replay
+ if: matrix.mode == 'release'
+ run: |
+ ./build-ci/render_scenarios renders-a
+ ./build-ci/render_scenarios renders-b
+ python3 -S tools/validate_audio.py renders-a --repeat renders-b
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: hydrapulse-host-${{ matrix.mode }}
+ path: |
+ field/HydraPulse/build-ci/Testing/Temporary/LastTest.log
+ field/HydraPulse/renders-a/
+ if-no-files-found: error
+ retention-days: 14
+ arm:
+ runs-on: ubuntu-24.04
+ defaults:
+ run:
+ working-directory: field/HydraPulse
+ steps:
+ # Match root checkout behavior: early auth cleanup traverses orphan legacy gitlinks.
+ - uses: actions/checkout@v7
+ - uses: actions/checkout@v7
+ with:
+ repository: electro-smith/libDaisy
+ ref: 85172e2b5c9abea1bf56dcd5d2c7b3f5717eb66b
+ path: libDaisy
+ submodules: recursive
+ persist-credentials: false
+ - name: Provision ARM compiler
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends gcc-arm-none-eabi binutils-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
+ arm-none-eabi-g++ --version
+ dpkg-query -W gcc-arm-none-eabi binutils-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
+ - name: Verify dependency and build both images
+ run: |
+ python3 -S tools/verify_dependencies.py ../../libDaisy
+ python3 -S tools/build_target.py --libdaisy ../../libDaisy --app-type BOOT_NONE --jobs 2
+ - uses: actions/upload-artifact@v4
+ if: success()
+ with:
+ name: hydrapulse-arm-BOOT_NONE
+ path: field/HydraPulse/artifacts/
+ if-no-files-found: error
+ retention-days: 14
+ - uses: actions/upload-artifact@v4
+ if: failure()
+ with:
+ name: hydrapulse-arm-failure-logs
+ path: field/HydraPulse/artifacts/**/build.log
+ if-no-files-found: warn
+ retention-days: 14
diff --git a/field/HydraPulse/.clang-format b/field/HydraPulse/.clang-format
new file mode 100644
index 000000000..f42d87300
--- /dev/null
+++ b/field/HydraPulse/.clang-format
@@ -0,0 +1,6 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+ColumnLimit: 110
+BreakBeforeBraces: Allman
+AllowShortFunctionsOnASingleLine: Empty
+SortIncludes: true
diff --git a/field/HydraPulse/.gitattributes b/field/HydraPulse/.gitattributes
new file mode 100644
index 000000000..a720e53b2
--- /dev/null
+++ b/field/HydraPulse/.gitattributes
@@ -0,0 +1,3 @@
+# Preserve archived source and raw acquisition bytes across checkouts.
+/references/r0/** -text
+/evidence/**/*.log -text -diff
diff --git a/field/HydraPulse/.gitignore b/field/HydraPulse/.gitignore
new file mode 100644
index 000000000..315a7be97
--- /dev/null
+++ b/field/HydraPulse/.gitignore
@@ -0,0 +1,14 @@
+/build/
+/build-*/
+/.deps/
+/artifacts/
+/renders/
+__pycache__/
+*.pyc
+*.o
+*.elf
+*.bin
+*.hex
+*.map
+*.lst
+.DS_Store
diff --git a/field/HydraPulse/AGENTS.md b/field/HydraPulse/AGENTS.md
new file mode 100644
index 000000000..0ac470e97
--- /dev/null
+++ b/field/HydraPulse/AGENTS.md
@@ -0,0 +1,32 @@
+# HydraPulse development contract
+
+Work within `field/HydraPulse` and the additive `.github/workflows/hydrapulse.yml`.
+Read parent repository instructions before changing this subtree. Do not modify sibling
+examples, the root dependency gitlinks, root license, or the old HydraPulse repository
+as a side effect. Use the existing root dependency at `../../libDaisy`, verified
+against `deps.lock.json`. Qualification must run in an isolated clean checkout;
+never force-rebuild a shared user's dependency tree with different compiler flags.
+
+The firmware audio callback is the only writer of Engine, Controller and sampled control
+state. Foreground MIDI sends fixed-size messages through SPSC queues; foreground display
+and logs consume snapshots. Never introduce logging, display/I2C transfers, file I/O,
+heap allocation, mutex waits or unbounded queue drains into the callback.
+
+Keep 16 step keys and Tune / Decay / Character / Level. Changes to input gestures must
+update Controller tests, the manual, control map and Mermaid sources in the same change.
+Keep the two images separate: Field Truth is permanent diagnostics; Beat is an instrument
+candidate. Neither host tests nor an ARM build can close the physical P0 gate.
+
+Run the local CMake/CTest suite, Python tests, offline repository validator, preset
+generation check and deterministic host render comparison. Run a real ARM build through
+`tools/build_target.py` before calling an image build-verified. The adapter stub is only a
+syntax/ownership aid; never package it as firmware. Preserve failures and NOT_RUN states.
+
+No automatic hardware flashing, force push, global dependency update, commercial manual
+redistribution or invented hardware evidence. A `.dvpe` project requires a real schema,
+known block IDs, import/export roundtrip and generated-code tests; otherwise omit it.
+
+The user has authorized a normal non-destructive integration into Denys/DaisyExamples.
+Use the repository's existing branch/PR policy, compare every destination file, test the
+actual merged tree, and report exact commits and CI runs. Ask only for material conflicts,
+destructive replacement, credential actions, public-release licensing, or physical flashing.
diff --git a/field/HydraPulse/CMakeLists.txt b/field/HydraPulse/CMakeLists.txt
new file mode 100644
index 000000000..0e1464aff
--- /dev/null
+++ b/field/HydraPulse/CMakeLists.txt
@@ -0,0 +1,44 @@
+cmake_minimum_required(VERSION 3.20)
+project(HydraPulseField VERSION 0.2.0 LANGUAGES CXX)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+option(HPF_ENABLE_SANITIZERS "Address + undefined behavior sanitizers" OFF)
+option(HPF_UBSAN_ONLY "Undefined behavior sanitizer only" OFF)
+option(HPF_ADAPTER_CONTRACT_CHECK "Compile adapters against an explicit host stub, NOT ARM" ON)
+add_library(hydrapulse_core INTERFACE)
+target_include_directories(hydrapulse_core INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
+if(MSVC)
+ target_compile_options(hydrapulse_core INTERFACE /W4 /permissive- /fp:precise)
+else()
+ target_compile_options(hydrapulse_core INTERFACE -Wall -Wextra -Wpedantic -Wconversion -Wshadow
+ -Werror -fno-fast-math -ffp-contract=off)
+endif()
+if(HPF_ENABLE_SANITIZERS OR HPF_UBSAN_ONLY)
+ if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
+ message(FATAL_ERROR "Requested sanitizers require GCC or Clang")
+ endif()
+ if(HPF_ENABLE_SANITIZERS)
+ set(HPF_SANITIZERS address,undefined)
+ else()
+ set(HPF_SANITIZERS undefined)
+ endif()
+ target_compile_options(hydrapulse_core INTERFACE -fsanitize=${HPF_SANITIZERS} -fno-omit-frame-pointer)
+ target_link_options(hydrapulse_core INTERFACE -fsanitize=${HPF_SANITIZERS} -fno-omit-frame-pointer)
+endif()
+add_library(hydrapulse_engine STATIC src/dsp/Voices.cpp src/app/Engine.cpp src/app/Controller.cpp)
+target_link_libraries(hydrapulse_engine PUBLIC hydrapulse_core)
+if(HPF_ADAPTER_CONTRACT_CHECK)
+ foreach(app IN ITEMS FieldTruth HydraPulse)
+ add_library(contract_${app} OBJECT firmware/${app}.cpp)
+ target_include_directories(contract_${app} BEFORE PRIVATE tests/adapter_stub)
+ target_link_libraries(contract_${app} PRIVATE hydrapulse_core)
+ endforeach()
+endif()
+include(CTest)
+if(BUILD_TESTING)
+ find_package(Threads REQUIRED)
+ add_subdirectory(tests/native)
+endif()
+add_executable(render_scenarios tools/render_scenarios.cpp)
+target_link_libraries(render_scenarios PRIVATE hydrapulse_engine)
diff --git a/field/HydraPulse/LICENSE_STATUS.md b/field/HydraPulse/LICENSE_STATUS.md
new file mode 100644
index 000000000..28fccdf35
--- /dev/null
+++ b/field/HydraPulse/LICENSE_STATUS.md
@@ -0,0 +1,8 @@
+# License status
+No new HydraPulse root license has been selected by the owner.
+The active source ledger preserves this as UNSELECTED rather than inferring a license from
+a destination repository or its libraries. libDaisy is an external dependency; retain its
+license notices and review its bundled/transitive notices before redistribution.
+No commercial reference manual or manually transcribed manufacturer preset is included.
+The 29 R0 files are historical first-party project continuity material.
+This file is a project decision record, not a substitute for a license grant.
diff --git a/field/HydraPulse/Makefile b/field/HydraPulse/Makefile
new file mode 100644
index 000000000..a829a8115
--- /dev/null
+++ b/field/HydraPulse/Makefile
@@ -0,0 +1,39 @@
+# Run from DaisyExamples/field/HydraPulse. Default is the diagnostic image, not the instrument.
+APP ?= truth
+LIBDAISY_DIR ?= ../../libDaisy
+CPP_STANDARD = -std=gnu++17
+OPT = -Os -fno-fast-math -ffp-contract=off
+APP_TYPE ?= BOOT_NONE
+override BUILD_DIR = build-$(APP)
+C_INCLUDES += -I.
+BUILD_ID ?= unrecorded
+C_DEFS += -DHPF_BUILD_ID=\"$(BUILD_ID)\"
+
+ifeq ($(APP),truth)
+TARGET = HydraPulseTruth
+CPP_SOURCES = firmware/FieldTruth.cpp
+else ifeq ($(APP),beat)
+TARGET = HydraPulseBeat
+CPP_SOURCES = firmware/HydraPulse.cpp src/dsp/Voices.cpp src/app/Engine.cpp src/app/Controller.cpp
+else
+$(error APP must be truth or beat)
+endif
+
+SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core
+ifeq ($(wildcard $(SYSTEM_FILES_DIR)/Makefile),)
+$(error libDaisy core/Makefile missing; use tools/build_target.py with an inspected checkout)
+endif
+include $(SYSTEM_FILES_DIR)/Makefile
+# BUILD_DIR is an explicit GNU make override so libDaisy cannot merge variant objects.
+
+# Keep public BSP enum layouts consistent with the libDaisy library build.
+# The upstream application makefile adds -fshort-enums; our library qualification
+# build uses default-width enums. The later option wins without editing upstream.
+CPPFLAGS += -fno-short-enums
+
+# The pinned upstream SWD recipe hardcodes ./build. Keep the actual variant path.
+# This intentional recipe override is local; never edit libDaisy for this project.
+ifeq ($(APP_TYPE),BOOT_NONE)
+program: $(BUILD_DIR)/$(TARGET).elf
+ $(OCD) -s $(OCD_DIR) $(OCDFLAGS) -c "program $(BUILD_DIR)/$(TARGET).elf verify reset exit"
+endif
diff --git a/field/HydraPulse/PACKAGE_MANIFEST.json b/field/HydraPulse/PACKAGE_MANIFEST.json
new file mode 100644
index 000000000..2afadfc5e
--- /dev/null
+++ b/field/HydraPulse/PACKAGE_MANIFEST.json
@@ -0,0 +1,165 @@
+{
+ "schema_version": 1,
+ "candidate": "HydraPulse Field 0.2.0",
+ "date": "2026-09-07",
+ "target_repository": "Denys/DaisyExamples",
+ "target_subtree": "field/HydraPulse",
+ "excluded_self": "field/HydraPulse/PACKAGE_MANIFEST.json",
+ "files": {
+ ".github/workflows/hydrapulse.yml": "e8945d035012799f75a632570ac07b729fa21848f6c956c9be0778ff78e18e4d",
+ "field/HydraPulse/.clang-format": "dbd6eedc4a17cade34370a79ada82195b866baa7bba4fb5f315a606c44b5a3b1",
+ "field/HydraPulse/.gitignore": "24c2d7c327d25c07399490ed3489243535ce1744247208c2730ed49fcea6310e",
+ "field/HydraPulse/AGENTS.md": "fe3d0acaecb783fe8870d98e13812faf72d5961ada7b1b8bf5c0cc700d043265",
+ "field/HydraPulse/CMakeLists.txt": "20f6068e888802a8b4bc8dc9edd4eff8571426c179760b3b55d2d715f99323d2",
+ "field/HydraPulse/LICENSE_STATUS.md": "b4b6e702d92569d6f0354c29f68192e96a5add6687923df450d973459c68eab8",
+ "field/HydraPulse/Makefile": "b1a647c995c3359d4c100f3a715ac1ec050c04ad42eb774b26eb69e523deee4e",
+ "field/HydraPulse/README.md": "8b81351a27c0af277c5260950a559a965b5f0f902744272414ffa5d852aaa24c",
+ "field/HydraPulse/deps.lock.json": "c59db8c327b09cfe2cee78d0d32fb5f8af68218c282c13ede085c0e401b4f61d",
+ "field/HydraPulse/docs/00_EXECUTION_PLAN.md": "9dd34d996c7dd857d1a1c3f5c91155d8c65b7a5c8f1702106c94007dd953a903",
+ "field/HydraPulse/docs/01_FOUNDATION_REVIEW.md": "9b1a7de3b1e7ca20128d93ef7d54c5c697779873de6380758bbb712d5d080718",
+ "field/HydraPulse/docs/CODEX_EXECUTION.md": "3432989c91fac99d23e2aae8805124aaa425abe9a51b5d8dd9206a5a3c15382e",
+ "field/HydraPulse/docs/DVPE_INTEGRATION.md": "32456fee13b708b65163185ffffa8e100b9bc87603d09895aca50d50a5e5bf3b",
+ "field/HydraPulse/docs/PRESETS.md": "9fb8f91f4aa3221fca2a4d3d1532628d5310e144cfc8ac9b04235ddb583b8c4f",
+ "field/HydraPulse/docs/QA_PLAN.md": "aee8fb023b39655e81eb9ca849806284e6a0a76fb51c838a355bb1e93bd1bd69",
+ "field/HydraPulse/docs/QUICK_REFERENCE.md": "837bc6c1be6e1fb6953ab8b3bbc6a86dbd94155196857ca85518d10e61307964",
+ "field/HydraPulse/docs/USER_MANUAL.md": "a5458221302b0b10338bf2de6aab8822ab54e3a677402c2e46d657067578269f",
+ "field/HydraPulse/docs/VALIDATION.md": "14928e63506a78387d88487908ceed0dd8a7d591a7b1deea5173566577b543cd",
+ "field/HydraPulse/docs/system/01_context.md": "31ae9a6426ac4f86a524ee6dbbcf8c10373e818591b3f58dd1d90f0252b0ccb5",
+ "field/HydraPulse/docs/system/01_context.mmd": "ad6737ebb457e75f65bd445b4525b192895e6591c7fdf353b4fc2864fb6004a8",
+ "field/HydraPulse/docs/system/02_firmware_ownership.md": "35d5594ab7775c1d1a2da89d9e43552344720cd9bf7de9f102efb46220e5406b",
+ "field/HydraPulse/docs/system/02_firmware_ownership.mmd": "2702c0cf706896e206931e5e66d489a8e7dae76e23d431071f75a1ec9497437b",
+ "field/HydraPulse/docs/system/03_synthesis.md": "c46e1c551bec1826166bc08c272bdd04fde23b9ee2581b310a4d6987bc6db309",
+ "field/HydraPulse/docs/system/03_synthesis.mmd": "0d4a2e3c67783ec23923438c0d81a0917b15eb32179a5dad2f590a764c6dba5a",
+ "field/HydraPulse/docs/system/04_clock_transport.md": "3eacfcad045415b29579ed0b00f9d8a678e48e3140354bdafe2ddf2b47773e87",
+ "field/HydraPulse/docs/system/04_clock_transport.mmd": "0bada6e5b4671bccf54a5fb74573623fbd83d85b22b2ece2b4daa5b4c51335f0",
+ "field/HydraPulse/docs/system/05_controls_menu.md": "0c9947048d9132403ddbb67bb17ee9ae23029eba77249cb7b240706731bcefeb",
+ "field/HydraPulse/docs/system/05_controls_menu.mmd": "1c8f1de62899f5a3cc8fa8d57e25ea155a266ca56fd553839a12ad5066844a8d",
+ "field/HydraPulse/docs/system/06_field_hardware.md": "5e61c9584e9fdb5235074616483e5df8eb0201d6894d69795c64423ed6aa3438",
+ "field/HydraPulse/docs/system/06_field_hardware.mmd": "88881721107531e9447df4f8c2b8f124994a903fd10b22093461e11322fdc843",
+ "field/HydraPulse/docs/system/07_preset_transaction.md": "ede16b35adf68ba95340154eaf4276cba18f0485cd9c2986306514c973ba332e",
+ "field/HydraPulse/docs/system/07_preset_transaction.mmd": "22d654d599cdbe3e7979792e0644241a92707548e56546ade30e003008b0da04",
+ "field/HydraPulse/docs/system/08_verification.md": "ec5c7f584a236e16e5d258f2e761db6a6cae2fa6e04fa6d8bd89add2b02a568a",
+ "field/HydraPulse/docs/system/08_verification.mmd": "ca8f26b9024efb8eac1ced81166abddb99bceb9d211e2d9946687a317d257b5e",
+ "field/HydraPulse/docs/system/README.md": "2593faf9cb148daaa72b439e38e189b7dc6bb950240faf1edc0d78d9015522b9",
+ "field/HydraPulse/evidence/local/asan-ubsan.log": "d36990fe311106e514b3046e5635b280b952e8e7bb700e183c65e26e79d2ea83",
+ "field/HydraPulse/evidence/local/capabilities.json": "132a38a248de9991966c1b36d0d2c6bd4498c0cc5ea0fbae88448e3a24ec00c0",
+ "field/HydraPulse/evidence/local/document-links.log": "6d2069f9bca89bda882d98becf3c651d86fda2f21edf1b91ce9cdbf733726d79",
+ "field/HydraPulse/evidence/local/final-build-clang-asan.log": "6296aaa9c683d31fdc738fec68d41f990b0ce70b8b7e102cb174b18badb151f7",
+ "field/HydraPulse/evidence/local/final-build-clang.log": "7ef326583bfeeb8b03cd438cb791ada3951c5dc218e61c321d2f368527e7a153",
+ "field/HydraPulse/evidence/local/final-build-release.log": "0788a8c766547e43f070ff888e189d11e77948e67a8e9a78247bfed47bf245b8",
+ "field/HydraPulse/evidence/local/final-build-ubsan.log": "0ead8cfffde52dc592b1a32c93501aa88371ab51fd9a9b46ccf8360fc8c0feed",
+ "field/HydraPulse/evidence/local/final-configure-clang-asan.log": "1d62166443731be95775fd14a2f2641f7dfcf567df2f41da7ace712adb5dc1c6",
+ "field/HydraPulse/evidence/local/final-configure-clang.log": "4ec740d27f32281e019e7cdb465ecdd1ffa76b95b87c15e3a0ab988ffdc42340",
+ "field/HydraPulse/evidence/local/final-configure-release.log": "3895fc44bebefd73c72d121721d2b4dacd12c507e1572bf36360533e15dd52c6",
+ "field/HydraPulse/evidence/local/final-configure-ubsan.log": "dc046e82237e7a08f895822eb72fc3b1667e75ecd9c034b828f385f78e6baeca",
+ "field/HydraPulse/evidence/local/final-ctest-clang-asan.log": "068252746efb8869e84ee52514657222d44947e2ebf49248ccbc8e0bc5782bd7",
+ "field/HydraPulse/evidence/local/final-ctest-clang.log": "cd87364bdc70a3cd70136c1c8c791e002e6bfc916add87db40c5a80a51f2c823",
+ "field/HydraPulse/evidence/local/final-ctest-release.log": "672ddb10e3f4091608de6c548ba3f5a2294094ce56d44855db111a229b451e51",
+ "field/HydraPulse/evidence/local/final-ctest-ubsan.log": "ce7a6d9868a153a7f79a6af9179b092588ed38fbf74e185bbe8e3631da64c10f",
+ "field/HydraPulse/evidence/local/final-dependency-preflight.log": "f99dcec15a602db6ae31fa61d424fc648507988bcdbc472d14fab1c4da3c390b",
+ "field/HydraPulse/evidence/local/final-evidence-template-validation.log": "347cf198a5332260cd8d04f9490b10b33261c2b5cd51a623d7bbba6045416a5e",
+ "field/HydraPulse/evidence/local/final-presets-validation.log": "6eaa321e1c9623a4d06c5005f30eab7dac6feb8d3972813ff46bf065cace3769",
+ "field/HydraPulse/evidence/local/final-python-tests.log": "b0c62459018d5aa072367bf4d409a8d9883c4b66eafcc9a5d09157efe7efc7be",
+ "field/HydraPulse/evidence/local/final-render-first.log": "fc8b2ac5e123a58a7f67c42e11df53c606eec6d67a0899ac467ca647a3b9fc81",
+ "field/HydraPulse/evidence/local/final-render-repeat.log": "7e8e16895679569c8a6fad41f179bbd84ff9b13ba0f77d01c1e9fcfe5e41daa4",
+ "field/HydraPulse/evidence/local/final-render-validation.log": "5dcdf9194cc29adc6fa42f06c1f7a87129348fbe92cefb2397ce4337cf401810",
+ "field/HydraPulse/evidence/local/final-repository-validation.log": "da13b22fddf1521f71296d9f071798aac3fd8fa527dc38aa4500d8e69e2a1533",
+ "field/HydraPulse/evidence/local/final-target-preflight.log": "517002176c7de8e35ff6d3a80ea52c22616b346376c05de67e60eae2a6503a5e",
+ "field/HydraPulse/evidence/local/manual-pdf-qa.log": "9d1ddd42b603d5c2e0933036f0cc31949df2e850d8bbf7fb112681b9a4338309",
+ "field/HydraPulse/evidence/local/negative-helper-expected-failure.log": "6337d5c499695e35f8c1c455bc5eb64f2647b24ee20aedfaf5d1b6541db709c9",
+ "field/HydraPulse/evidence/local/results.json": "b3a25186e8d89a26e7c08c92ebc075fa14103ae94752efc443bb27d8cae16aec",
+ "field/HydraPulse/evidence/local/sonic_metrics.csv": "1ade501fd93f431fab89ba64bb43ac79c8cc5fb960d69e7b6c676f278199fa37",
+ "field/HydraPulse/evidence/local/tool_versions.json": "f78c3c3126e78f6992a6976687579a06ab491d051110a7e856f8a2fec514e6fb",
+ "field/HydraPulse/evidence/local/transitions-after-repair.log": "78213608c383c5d619213b93548ff8dbca03f7d2e1de7e442ee66f23b9c988db",
+ "field/HydraPulse/evidence/local/transitions-before-repair.log": "1ff35fae0af232a353357bbecd14654b985ef0f86c5efc68717c6eb4883b0965",
+ "field/HydraPulse/evidence/local/workflow-structure.log": "788ebfb1954da0bb46cad8d091091a33d01b346456b3630b64bb55ddf9bfeb30",
+ "field/HydraPulse/firmware/FieldAdapter.h": "856bcc26f00812622b09ece8c87129bc38c40c835c2448a764b69555ff0c444b",
+ "field/HydraPulse/firmware/FieldTruth.cpp": "20ef270a84a44c7dac0d5691b65467230278f6426c095425809488f4f0684a6b",
+ "field/HydraPulse/firmware/HydraPulse.cpp": "17311d8cac722674c1d6f3fa9c5211808c1d7a8b868ff050ccf1822c7c70dbdf",
+ "field/HydraPulse/hardware/control_map.json": "d3507b7fdf0da8405dcd038fd0526cb716fa79253f639b4ca0416a0a0352f3db",
+ "field/HydraPulse/hardware/key_map.template.csv": "693c1b67ba9bcb206cbbb81cdd3bf3e1697fdb9b28db88a5fdfb7a81e6f05ac4",
+ "field/HydraPulse/hardware/p0_record.template.json": "38a78fa705f9100366dbc4619ca58ea845cee41db09293aa4c27bbec3effebd8",
+ "field/HydraPulse/presets/tutorials.json": "8d4e0bdb0334786f61650db7c288dcf2628ec224666b20e4c3f5dca28bc28157",
+ "field/HydraPulse/references/r0/.clang-format": "d87cc519b7567560cbfd6def04b86ccb168ee64154ad0472982d152441ef2c62",
+ "field/HydraPulse/references/r0/.github/workflows/ci.yml": "50c290ad468f10d8c6a1c4ce1e6cb13d77c63ddfdf01d8dbf9f0d35f40c4d95b",
+ "field/HydraPulse/references/r0/.gitignore": "dfd352f6bed34db1459fc7f9b9f29ba5e054ed844ed4f4c42107395decaade6b",
+ "field/HydraPulse/references/r0/CMakeLists.txt": "96c67cc212784b6be76e4b8a4a5f2b0fe705646df47334437cd06f33521763ce",
+ "field/HydraPulse/references/r0/README.md": "b5f45227bf5e589beaf6515ae4024b26f39b41b7b393b151cd102d6e89534fe8",
+ "field/HydraPulse/references/r0/deps.lock.json": "c8c410d73ecfc9d36defcfd94119eaedd008a00c032e4b7d4b52eb47c7a18ff7",
+ "field/HydraPulse/references/r0/docs/ARCHITECTURE.md": "066da0f7ad512c5a5810cf3b11bc7513f88fad59fcf3015ac180a66ec58882a7",
+ "field/HydraPulse/references/r0/docs/LOCAL_VALIDATION.md": "ae6d8c77902681245c4e4e8347f27e75ab7fef26c6e43aae5663c2c437b572da",
+ "field/HydraPulse/references/r0/docs/POC_SEQUENCE.md": "d822e4f7faff5a4771cf0992612d74cb90cda73b72d2c0c2a086cffb0d8d5769",
+ "field/HydraPulse/references/r0/docs/SOURCE_REVIEW.md": "a6d73983040559104770d2f002aec951b31e67158e1d8fcf45f3d61ca44bbd17",
+ "field/HydraPulse/references/r0/docs/STATUS.md": "6e3a9c3dd9b22299a7dc3f9a8fff24ae7a20b028fc54a3f05494780118c6d7a3",
+ "field/HydraPulse/references/r0/docs/TEST_STRATEGY.md": "fe7c63c7e66fd3c088ce8f59036e35c184c408f5729897e66309df642eb562d2",
+ "field/HydraPulse/references/r0/evidence/README.md": "4593e28a37d679fecc26e11b287084d36f021adc327e9f0dd17b2b6f240b6535",
+ "field/HydraPulse/references/r0/firmware/field_truth/README.md": "c2f6e40ad2d254720d852a5387640a10ee345d5791c5f93d534de52ee331d5dd",
+ "field/HydraPulse/references/r0/hardware/README.md": "3898d4a726df36973ac4928d0634778380839d1072e8fe8b7881db0bafbb3132",
+ "field/HydraPulse/references/r0/src/core/ControlPickup.h": "807950b052488b66d531bc64bb58acac3b321d87f139de5befa8b866f3819903",
+ "field/HydraPulse/references/r0/src/core/Pattern16.h": "1e18985219ba1ea6c5b4d66eaf223098483b7457af945217b3a07145c8ea70a6",
+ "field/HydraPulse/references/r0/src/core/SampleAccurateStepClock.h": "3728d537a685f547f7f5537a8ceedb4f7f6311a311f2f171c73d4262a329204f",
+ "field/HydraPulse/references/r0/src/core/StepSequencer.h": "ee6dd997505023870b534f3320df8b50a2fbc3770c28d4b1758a6942bd81b55f",
+ "field/HydraPulse/references/r0/tests/native/CMakeLists.txt": "ca1f7ae459de3506ca25d26ef9b6947383537a1ade9e6f1635f06aae1b9ed1b0",
+ "field/HydraPulse/references/r0/tests/native/test_control_pickup.cpp": "6355a247d778aba2c3cac7fbe177540a0dee03e097bd2aa1853ea7f610ae359b",
+ "field/HydraPulse/references/r0/tests/native/test_pattern16.cpp": "6d98ffccbd3a2bbc851a4796a935c58bbc9ab4298aaddc545a724395e39189f9",
+ "field/HydraPulse/references/r0/tests/native/test_sample_clock.cpp": "fd7e9b823fb0dbbade0c54ef702896b1da5b5ba963f45757a2f9c395d76781ec",
+ "field/HydraPulse/references/r0/tests/native/test_step_sequencer.cpp": "0574a40d3c9773cada1f7453fcd834b46e2fec4635eeb27d5ead224283b90bfd",
+ "field/HydraPulse/references/r0/tests/native/test_support.h": "72045eda7d406fb675629284c4886dbb5b9e6487bcbc9d04fbaa52b8dbcb49e0",
+ "field/HydraPulse/references/r0/tests/python/test_validators.py": "c1aace70635229dd62916b6be9a5b4d643121e4207df1e007965fe8656ba5401",
+ "field/HydraPulse/references/r0/third_party/manifest.json": "00ed13b47d1ebf5a53421c9db220cdf66b4c29f221357ac65033558f4ff68bbc",
+ "field/HydraPulse/references/r0/tools/validate_provenance.py": "b5cea9dac8e18fa7fb4abbaa22634a6900f152e3df9e254945c548606d76b099",
+ "field/HydraPulse/references/r0/tools/validate_repo.py": "b30de859914e5ffb9175d1eeeab5d987054fa5885e7c29b23aea765d1baa15ce",
+ "field/HydraPulse/references/r0_manifest.json": "133b1182a18d361d5cb7a2fd0d4a2ea86da80669467da0dc67474f57bbb16ed0",
+ "field/HydraPulse/src/app/Controller.cpp": "9feb520976e966e5453745a68774d3684b028f6d0c9824368aa53a0e57bf3987",
+ "field/HydraPulse/src/app/Controller.h": "bc0f96e53567fdc6ef3ecd5a1582a2fffdcaf1406ce60fbe347dbb2d2fdb7407",
+ "field/HydraPulse/src/app/Engine.cpp": "e35bbb8cbf94b7298509d321938d507f98d934dc0a5e14da39e1bbbfdbb07987",
+ "field/HydraPulse/src/app/Engine.h": "560547d9a02df26a4bdb042982ef8d96a3bc5c71a81dbb99520157ddea599170",
+ "field/HydraPulse/src/app/Presets.generated.h": "1bf775a4de76128d88ab42fc095a44b62de60070115b8d9c3a5a2ded8815ac43",
+ "field/HydraPulse/src/app/Types.h": "2668c6c1aa27955f8471a81e26c939aa1f94c1bd4c22930e79d6e3167035bd8d",
+ "field/HydraPulse/src/core/ControlPickup.h": "ee550a1ee797376b78d0ab5a7fc108add3699fc2d7dad928f1e550afcea8f87a",
+ "field/HydraPulse/src/core/GrooveClock.h": "5e37b5c3b08eba016f2bc382f81fdb802a37d67731e6422d7e291c37ef7e5a90",
+ "field/HydraPulse/src/core/OutputTestArm.h": "dd9dda4f7d7769cf9c7df7254c899b12c4b3c278574c1292cebbeb3ce8891063",
+ "field/HydraPulse/src/core/Pattern16.h": "1e18985219ba1ea6c5b4d66eaf223098483b7457af945217b3a07145c8ea70a6",
+ "field/HydraPulse/src/core/SampleAccurateStepClock.h": "3dd3293680f014081d94dd8c4d158e3e13653353870f6bf766a6ea50ddd589b1",
+ "field/HydraPulse/src/core/SpscQueue.h": "b99421411f208fe69e4a100bb5a3615b48c39d17a99407c13f4ef8f54f13d42a",
+ "field/HydraPulse/src/core/StepSequencer.h": "ee6dd997505023870b534f3320df8b50a2fbc3770c28d4b1758a6942bd81b55f",
+ "field/HydraPulse/src/dsp/Primitives.h": "3e9aa5e019094be38aa5c96b2babe35934cc6d44ceadede3db094012110f9fac",
+ "field/HydraPulse/src/dsp/Voices.cpp": "183ced39a1bdcea96baee2cf7d1bc42b8a8b5ccbb033e1df1690e078dedae6e8",
+ "field/HydraPulse/src/dsp/Voices.h": "644c98395f04d5add62ae352cb1a9d1e7e39b38ea42c92b24d5a235f00a3ed6b",
+ "field/HydraPulse/tests/adapter_stub/daisy_field.h": "c0fe8d18c05028522b75b5053c965d15c896d12a3154db9c8c5a8135e5e6fda6",
+ "field/HydraPulse/tests/adapter_stub/util/CpuLoadMeter.h": "94f88dbe49f93b009e7b0584e1cb551e9507aff1816b9038517e2e326cd0d80f",
+ "field/HydraPulse/tests/native/CMakeLists.txt": "1e80110504726594ce29cd4d5ad26e5d1ddb717b49902fd90bc719519b6c84e2",
+ "field/HydraPulse/tests/native/test_adapter_midi.cpp": "1c9973246efd21145b66364d1b1963ff11381c387c33f0cdae31be34f62d885e",
+ "field/HydraPulse/tests/native/test_control_pickup.cpp": "6355a247d778aba2c3cac7fbe177540a0dee03e097bd2aa1853ea7f610ae359b",
+ "field/HydraPulse/tests/native/test_controller.cpp": "adc348c0c7c4bc00fafa39e69650168a7d606a7cb39fe360549227a957e8fc53",
+ "field/HydraPulse/tests/native/test_dsp.cpp": "821df191793e1a7cf9b676579d083f2aa5b0fe0d090e5a7718a6afd315dd91f1",
+ "field/HydraPulse/tests/native/test_engine.cpp": "bb7e5459557bfcc5e53e55e6cb42ff728901d400a01685d451db888e7293cbea",
+ "field/HydraPulse/tests/native/test_groove_clock.cpp": "e5dc1980de781f76fc58e6ecf229b4ac7c3c36159311da4f8c0939b14f328203",
+ "field/HydraPulse/tests/native/test_near_rejects_nan.cpp": "e42bb917cf45d1cf6788505b9c87205028e61f115aee50b4d1d1188413d58e3c",
+ "field/HydraPulse/tests/native/test_no_allocation.cpp": "5333eeabfc7c4f32d9044ecdd9f0fb20beceee8cba72a12de425420ad83bea30",
+ "field/HydraPulse/tests/native/test_pattern16.cpp": "6d98ffccbd3a2bbc851a4796a935c58bbc9ab4298aaddc545a724395e39189f9",
+ "field/HydraPulse/tests/native/test_regressions.cpp": "3d1834e0e8517fdc5d4a986ea416a1a7bf8cb2b69b71c4b81ac8b125e34810f0",
+ "field/HydraPulse/tests/native/test_sample_clock.cpp": "fd7e9b823fb0dbbade0c54ef702896b1da5b5ba963f45757a2f9c395d76781ec",
+ "field/HydraPulse/tests/native/test_spsc.cpp": "f8dc494d513912281c1605017072d9b4d1c986e460e21ef0552a6b5931dda8a2",
+ "field/HydraPulse/tests/native/test_step_sequencer.cpp": "0574a40d3c9773cada1f7453fcd834b46e2fec4635eeb27d5ead224283b90bfd",
+ "field/HydraPulse/tests/native/test_support.h": "d8b62b261d286bb0c601ad54aa8dcd199ec54a5a70aad6977f1f1db4311d263b",
+ "field/HydraPulse/tests/native/test_transitions.cpp": "7da9944f5e59aa931c1ff20906c3432e1cac839ef60ce4f72cb1731e64090651",
+ "field/HydraPulse/tests/python/test_tools.py": "f3ffc52baaa02519773d2fcd583c269358097ceaa4cdafc670c11624a7e9e549",
+ "field/HydraPulse/third_party/manifest.json": "8d9e845d90369d87bf86e7f2713a87e24cfd707c3aa3b861e6736b8ff0367418",
+ "field/HydraPulse/tools/build_target.py": "880efef9d9daffc542a47ee2c76ca3dc71d1e436494a4ecbfb0a7ae51a40fcfc",
+ "field/HydraPulse/tools/common.py": "88afb84d7d32963ff313857e29d25a83050b2982c5a360854b42c34bcb4192fb",
+ "field/HydraPulse/tools/generate_presets.py": "eaacb02bdf4e3eb94ce020500a2b8947dd079b9f9d2ab895230f37192749af1b",
+ "field/HydraPulse/tools/install_additive.py": "1e335c11e2f848ace8ed566f8cdca347e9f9a66895dc02ea3c3fba7a72507a8e",
+ "field/HydraPulse/tools/parse_telemetry.py": "f6ffb0f82fba6c102eca9f7b876cf878c483c9b04c53ab8ce189417f38c815b5",
+ "field/HydraPulse/tools/render_scenarios.cpp": "b250b3c8a8727aa4ab1b9d3d2124f5bee14bd375d7df3baeeb5f9de683289315",
+ "field/HydraPulse/tools/validate_audio.py": "9f4473173f0694b52519dac98432fd7bfbb8b48eb9d9c10fa81a3af085a152c5",
+ "field/HydraPulse/tools/validate_evidence.py": "0d664015ee83a2c5a25990fbb162751d0f733827cfade11752f1bc008e381095",
+ "field/HydraPulse/tools/validate_repo.py": "1d66560fcd3ac71eef6ae117c942804972860b0318ba9a4b6588d64a697b392b",
+ "field/HydraPulse/tools/verify_dependencies.py": "84a8797cb223fb185fc7d186a2a51e3385ce9be2a7861a0d5d3eede3002e3f1c"
+ },
+ "payload_sha256": "89ffbc60a5d49f5b77b23075d2d2ebe6a0211e9266cfd47f2fb5fd3b7cf6e1a3",
+ "runtime_source_sha256": "7aa39d796ad68faea522189185c2f451230aa92a77b5996aa73af942c78d41f8",
+ "publication": "NOT_RUN",
+ "arm_build": "NOT_RUN",
+ "physical_qualification": "NOT_RUN"
+}
diff --git a/field/HydraPulse/README.md b/field/HydraPulse/README.md
new file mode 100644
index 000000000..0d20442b3
--- /dev/null
+++ b/field/HydraPulse/README.md
@@ -0,0 +1,100 @@
+# HydraPulse Field | 0.2.0 candidate
+
+A direct, four-voice synthesized drum machine for stock Electro-Smith Daisy Field.
+**Select a drum → tap 16 steps → edit the sound → Play.**
+
+This subtree is designed for `Denys/DaisyExamples/field/HydraPulse`. It is an additive
+development candidate, not a hardware-qualified release. The original 29-file R0 package
+is preserved without changes under `references/r0`; the old dedicated repository is not deleted.
+
+## What is implemented
+
+Two separate firmware sources share a host-testable foundation:
+
+| Image | Purpose | Source |
+|---|---|---|
+| HydraPulseTruth | Stereo passthrough, raw key events, knobs/CV/Gate/MIDI telemetry, momentary output tests, OLED/LED diagnostics and callback instrumentation | `firmware/FieldTruth.cpp` |
+| HydraPulseBeat | Hammer kick, Crack snare, Steel metallic hat, Arc phase-modulated percussion; 16-step A/B/Fill banks, accents, mutes, swing, drive, soft pickup, stop/panic and eight original tutorials | `firmware/HydraPulse.cpp` |
+
+One Home screen and a momentary Shift palette replace a nested menu tree. All state
+changes that affect sound are owned by the audio callback. Display and telemetry are
+foreground-only. The DSP uses original bounded algorithms and no DaisySP runtime dependency.
+
+Key and switch LEDs provide feedback in the instrument. Knob LEDs are not implemented
+and remain dark in both images; this does not indicate a knob input fault.
+
+Current integration evidence is recorded in [the integration log](docs/INTEGRATION_2026-09-08.md).
+The supplied package's historical results remain in [candidate validation](docs/VALIDATION.md).
+A software or flash verification result does not qualify physical controls or audio.
+
+## Start with the host build
+
+Requirements: CMake 3.20+, a C++17 compiler, Python 3.10+, and Git. The scripts use only
+Python's standard library. Linux/WSL is the reference command environment.
+
+```sh
+cmake -S . -B build-host -DCMAKE_BUILD_TYPE=Release
+cmake --build build-host --parallel 2
+ctest --test-dir build-host --output-on-failure
+python3 -S -m unittest discover -s tests/python -v
+python3 -S tools/validate_repo.py
+python3 -S tools/generate_presets.py --check
+./build-host/render_scenarios renders/first
+./build-host/render_scenarios renders/repeat
+python3 -S tools/validate_audio.py renders/first --repeat renders/repeat
+```
+
+The host adapter object builds use an explicit stub. They catch C++ integration mistakes,
+not compatibility with an actual libDaisy revision, interrupt behavior or electrical behavior.
+
+## Build the two target images
+
+Use DaisyExamples' existing root libDaisy at `../../libDaisy`, verified against
+`deps.lock.json`. Run qualification in a clean isolated checkout; the helper rebuilds
+the library with explicit qualification flags. See [Codex execution](docs/CODEX_EXECUTION.md).
+
+```sh
+python3 -S tools/verify_dependencies.py ../../libDaisy
+python3 -S tools/build_target.py --libdaisy ../../libDaisy
+```
+
+The default build is `BOOT_NONE`. Successful builds must fit the project's 128 KiB
+internal-flash qualification policy. Oversized images fail; changing boot layout requires
+a deliberate bootloader decision. `BOOT_SRAM` and `BOOT_QSPI` are explicit build options,
+not assumptions about the board in front of you. The build tool never flashes hardware.
+
+Do not use a host executable as a firmware image. Do not use an old successful CI result
+for the new source. The build helper records its transcript during the build and emits a successful
+qualification manifest with source/image hashes, tool versions and dependency gitlinks
+only after both real ARM images pass its checks.
+
+## Development and playing references
+
+- [Detailed plan](docs/00_EXECUTION_PLAN.md) and [foundational findings](docs/01_FOUNDATION_REVIEW.md).
+- [Eight-file Mermaid system reference](docs/system/README.md), covering ownership,
+ algorithms, timing, controls, hardware, presets and evidence gates.
+- [User manual](docs/USER_MANUAL.md), [tutorial presets](docs/PRESETS.md) and
+ [quick reference](docs/QUICK_REFERENCE.md).
+- [Automated and bench QA](docs/QA_PLAN.md), [Codex integration](docs/CODEX_EXECUTION.md),
+ [DVPE decision](docs/DVPE_INTEGRATION.md).
+- [Source ledger](third_party/manifest.json), [dependency pin](deps.lock.json) and
+ [R0 preservation inventory](references/r0_manifest.json).
+
+## Deliberately not implemented
+
+No persistent user save, sample engine, 64-step paging, deep parameter locks, external
+clock following, external audio lane, USB MIDI transport or Hydrasynth-specific
+integration. Beat's CV and Gate outputs are commanded off and its audio inputs are ignored.
+Those ports are exercised by Field Truth first. A/B/Fill is present as a candidate;
+P0 hardware acceptance remains open.
+
+P0 must establish controls, audio, CV/Gate, MIDI and timing on the actual Field. Only then
+promote Beat to the P1 hardware trial. Longer patterns, more voices and two-box integration
+follow measured standalone performance, not the number of folders in this repository.
+
+## License and references
+
+The owner's root license choice remains unselected; this package does not invent one.
+See `LICENSE_STATUS.md`. Commercial manuals, their diagrams and factory patterns are
+not redistributed. Their instructional approaches informed the original manual and
+tutorials, not the implementation of their products.
diff --git a/field/HydraPulse/deps.lock.json b/field/HydraPulse/deps.lock.json
new file mode 100644
index 000000000..35ce1a836
--- /dev/null
+++ b/field/HydraPulse/deps.lock.json
@@ -0,0 +1,17 @@
+{
+ "schema_version": 2,
+ "date": "2026-09-08",
+ "strategy": "use the existing DaisyExamples root libDaisy gitlink; no global repin",
+ "dependencies": [
+ {
+ "name": "libDaisy",
+ "repository": "https://github.com/electro-smith/libDaisy",
+ "ref": "85172e2b5c9abea1bf56dcd5d2c7b3f5717eb66b",
+ "license_file": "LICENSE",
+ "license_blob_sha1": "1c3a251694f1d1842c3f114508d36ca63cddf275",
+ "pin_evidence": "gitlink inspected at Denys/DaisyExamples master 041bfcbb7975316606ece8f1ccbacbab9c0e1482; exact clean tree and LICENSE inspected locally on 2026-09-08.",
+ "current_run_status": "Exact checkout inspected; tools/build_target.py verifies it before and after each qualification build.",
+ "role": "only direct firmware dependency; no DaisySP required by these original algorithms"
+ }
+ ]
+}
diff --git a/field/HydraPulse/docs/00_EXECUTION_PLAN.md b/field/HydraPulse/docs/00_EXECUTION_PLAN.md
new file mode 100644
index 000000000..72072f490
--- /dev/null
+++ b/field/HydraPulse/docs/00_EXECUTION_PLAN.md
@@ -0,0 +1,85 @@
+# HydraPulse Field: foundation review and implementation plan
+Date: 2026-09-07. Candidate version: 0.2.0-dev. Target: Denys/DaisyExamples, field/HydraPulse.
+This plan is followed by implementation in this package, not a request for another planning session.
+
+## 1. Preserve and establish the baseline
+Verify the supplied R0 ZIP checksum. Preserve every one of its 29 files under references/r0.
+Inspect clock, pickup, sequencer, tests, manifests and existing CI before changing them.
+Use a new additive field/HydraPulse folder. Do not delete Denys/HydraPulse-Field or rewrite history.
+On the connected Codex checkout, inspect the actual target HEAD/tree, nearest AGENTS.md, root README,
+.gitmodules, CI scripts, build configuration and destination conflicts before applying the package.
+The old remote SHA in the conversation is historical evidence, not a current HEAD assertion.
+
+## 2. Audit boundaries before features
+Check invalid numeric inputs, retuning semantics, sample timing, callback ownership,
+cross-context communication, key mapping, mute/retrigger transitions and actual QA coverage.
+Attach every material finding to a failing test or a specific absent implementation.
+Retain the tested Pattern16 and StepSequencer interfaces; repair clock/pickup input handling.
+Do not confuse a provenance validator's string checks with upstream verification.
+
+## 3. Write the system reference before integration
+Produce individually editable Mermaid files and companion explanations for system context,
+firmware ownership, audio algorithms, clock/transport, control gestures, Field I/O, preset
+transactions and verification. Use actual source symbols and make deferred features explicit.
+Review diagrams against the resulting implementation and provide a renderer command for Codex.
+
+## 4. Implement reusable primitives and host tests
+Keep all musical/DSP logic independent of libDaisy. Implement finite ramps, deterministic noise,
+a sine lookup, bounded AD envelopes, a swing-aware integer clock, fixed-capacity SPSC queues,
+four voice algorithms, original tutorial presets and the direct-control state machine.
+Test invalid inputs, finite outputs, decay, replay, schedule error, transport, bank transitions,
+overflows, held-button gestures, parameter pickup and no-allocation processing.
+Do not add a generic plugin/browser/modulation ecosystem merely for hypothetical portability.
+
+## 5. Implement permanent P0 diagnostics
+Separate firmware target: passthrough; keyboard edge/seen masks; knob/CV reporting;
+Gate In counts; MIDI receive counts; manual DAC code selection; outputs normally off.
+Release-to-arm then hold SW1 for output tests; SW2 alone for LED/OLED test.
+Instrument the callback with the Seed testpoint and libDaisy CpuLoadMeter.
+Label timer load as callback-body instrumentation, not total interrupt occupancy.
+Preserve cold-start boot metadata for later serial connection; do not infer electrical reset cause.
+
+## 6. Implement Beat Core as a bench candidate
+Four synthesized voices: Hammer, Crack, Steel and Arc. Three 16-step banks: A, B and Fill.
+One home screen; hold Shift for direct actions; release returns home. No persistent menu stack.
+Four common sound controls, fixed tempo/swing/drive/master knobs, soft pickup after selection.
+A/B changes at the next bar, momentary Fill at the next step, smooth mutes and panic.
+Eight original tutorial presets, from blank to dense. Preset browsing never loads automatically.
+The user's new instruction authorizes producing this candidate without waiting for P0 hardware
+results. It does not authorize marking P0 passed: performance deployment still follows P0.
+
+## 7. Use verified Daisy infrastructure, avoid collateral changes
+Use libDaisy DaisyField BSP, core/Makefile and CpuLoadMeter. Respect GNU++17 selection explicitly.
+Keep original DSP native-testable; DaisySP is not required by these particular algorithms.
+Use the target repo's build/style/test tools only after inspecting their actual versions and scope.
+Use a private per-project dependency checkout rather than repinning siblings' submodules.
+Generate truth and beat binaries into distinct build directories. Never auto-flash in CI.
+DVPE is optional: omit .dvpe until serialization, block registry and export round-trip are verified.
+
+## 8. Validate and deliver
+Execute host GCC/Clang tests, sanitizers where supported, waveform scenarios, source validators,
+package checksum checks and an additive patch application test.
+Run the real ARM compiler and libDaisy linker in Codex; preserve ELF/BIN/MAP, toolchain identity,
+dependency SHAs and build logs. Fail clearly if compiler, libraries or supported flash size are absent.
+Render Mermaid and run relevant upstream regression tools in that checkout.
+A host peripheral stub only checks application syntax/contract; it is not an ARM build.
+Deliver code, manual, preset lessons, test instructions, review and measured local result files.
+Remote publishing requires a working authorized connector or authenticated Codex git session.
+No such route is exposed in this authoring run, so remote writes are not performed.
+
+## 9. Hardware and musical acceptance
+After P0 flash, measure all physical controls, CV/Gate electrical behavior, calibrated audio,
+MIDI input and timing under display/LED/serial load. Freeze measured control mapping.
+After Beat flash, test each tutorial lesson, 60-minute dense performance, rapid retriggers,
+transport/mute/preset changes, disconnected USB, queue pressure and near-endpoint controls.
+Record worst observed callback time; initial proposed target is <50% of the 1 ms period,
+with zero observed deadline overruns. This is an acceptance target, not a measured result.
+Use calibrated loopback and listening to assess clicks, aliasing, DC, headroom and voice clarity.
+No hardware test can be manufactured by desktop compilation.
+
+## 10. Deferred growth with explicit entry conditions
+After P0/P1 acceptance: persistent storage with power-fail tests, 64-step paging, additional three
+voices, controlled destruction, then timing/clock integration and the stereo external lane.
+Add complex locks/ratchets/probability only after A/B/Fill and direct operation remain usable.
+Keep Hydrasynth-specific MIDI/CV/audio integration deferred. Do not copy manual patch settings
+or commercial pattern-book content into presets.
diff --git a/field/HydraPulse/docs/01_FOUNDATION_REVIEW.md b/field/HydraPulse/docs/01_FOUNDATION_REVIEW.md
new file mode 100644
index 000000000..cdd2375c2
--- /dev/null
+++ b/field/HydraPulse/docs/01_FOUNDATION_REVIEW.md
@@ -0,0 +1,75 @@
+# Foundation review
+## Verdict
+R0 is a useful host-test bootstrap, not a hardware instrument or a target-qualified build.
+Its existing tests miss numeric and runtime integration failures. Preserve its research;
+repair those gaps before using it as a firmware foundation.
+
+| ID | Evidence in supplied R0 | Concrete failure | Resolution in this candidate |
+|---|---|---|---|
+| F01 | SampleAccurateStepClock::SetTempoBpm unconditionally calls Reset | Repeated tempo updates can indefinitely postpone the next step | Retune preserves phase; add repeated-retune regression |
+| F02 | Clock constructor accepts zero sample rate/zero step division; tiny BPM rounds to zero after mutation | Zero-threshold clock emits constantly or zero increment stalls | Validate construction, safe fallback and transactional setters |
+| F03 | ControlPickup::Clamp uses std::clamp directly | NaN input can poison the held value and defeat capture comparisons | Ignore nonfinite physical updates; sanitize initialization |
+| F04 | HPF_CHECK_NEAR only compares fabs(error)>tol | NaN comparison is false, so an invalid numerical result passes | Explicit finite/tolerance checks and negative self-test |
+| F05 | Python tests run validators only on a valid repository | Removing a required file or corrupting provenance is not tested | Negative fixture tests for required files, presets and evidence |
+| F06 | R0 has no firmware source, only field_truth/README.md | Host CI cannot detect a target API or linker failure | Two firmware entry points, explicit target-build script and separate gates |
+| F07 | No UI/audio ownership contract exists in executable code | Concurrent display/MIDI edits could race DSP and corrupt state | Audio owns engine/UI; fixed-capacity one-producer/one-consumer snapshots and MIDI commands |
+| F08 | No sound engine or rendered scenarios | Passing integer tests says nothing about runaway audio, tails or transitions | Deterministic DSP renderer; finite/bounds/decay/transition/no-allocation tests |
+| F09 | Manifest validation checks only SHA syntax and equality | A well-formed but unfetched commit looks reproducible | Check actual git HEAD and submodule state in target preflight |
+| F10 | P0 physical key order unmeasured | A diagram could label the wrong physical key or LED | One explicit mapping table and a bench acceptance gate; no measured-label claim |
+
+## Integration judgment
+Placing the project under field/HydraPulse improves compatibility with the two-level relative
+library layout documented by upstream DaisyExamples. It does not grant tools access and does
+not establish that Denys's fork contains a particular QA framework. That fork was not readable
+through this run's public web route. No claims about its current tree are made.
+
+libDaisy's inspected tests/CMakeLists.txt actually selects a limited set of sources with UNIT_TEST
+and GoogleTest; it is not a simulator of the whole Daisy Field. CpuLoadMeter exists and measures
+callback-body time with System ticks. The generic Makefile defaults to GNU++14 and BOOT_NONE:
+both facts require explicit attention for a C++17 project and flash-size verification.
+
+## What was not reproduced
+No root license was selected. No commercial manual was copied into the repository.
+The original APE_DRAFT ZIP and two .skill files are absent from this runtime: their contents
+are not reconstructed from the handoff. The available 29-file R0 ZIP is preserved byte-for-byte.
+No current remote merge, ARM build, DFU write, or physical measurement was performed here.
+
+## Design versus proof
+Soft ramps, finite envelopes and the absence of a resonant feedback path reduce specific failure
+mechanisms. They are not a proof of click-free analog output or inaudible aliasing. Pulse timing,
+codec behavior, boot transients, noise floor and analog levels remain measured-hardware gates.
+
+## Implementation review and repaired deltas
+
+F11. The first Arc candidate reset modulation-envelope depth at an active retrigger.
+A matched-trajectory host test measured 1.42942 full-scale first-sample deviation.
+Using a 1-ms envelope rise from the current depth reduced the tested maximum to
+0.0320582. Noise-filter coefficient updates also became 5-ms ramps; the corresponding
+parameter-event deviation fell from 0.210695 to 0.0263815. These are tested scenarios,
+not a universal perceptual click guarantee. `test_transitions` guards the repairs.
+
+F12. Repeated Stop could repeatedly restart a release ramp and postpone silence.
+Envelope Release now ignores an already active zero-target release. The repeated-release
+regression reaches exact silence within its fixed test window.
+
+F13. Long telemetry records can exceed libDaisy's documented `LOGGER_BUFFER=128`.
+Boot, output, timing and queue records are now split, with bounded string widths.
+A conservative format-width test covers full 32-bit numeric values including CR/LF/NUL.
+Source: https://electro-smith.github.io/libDaisy/logger_8h_source.html
+
+F14. The upstream core Makefile's OpenOCD `program` recipe hardcodes `./build/`,
+unlike `program-dfu`, which uses `BUILD_DIR`. This project uses separate build-truth
+and build-beat directories. The integration guide therefore uses the verified
+DFU recipe only after a boot-layout decision, never the incompatible `make program`.
+Source: https://raw.githubusercontent.com/electro-smith/libDaisy/master/core/Makefile
+
+F15. Showing only the Shift legend hid tutorial selection while its modifier was held.
+The Shift display now keeps the pending tutorial name and load action visible.
+No persistent nested menu was introduced.
+
+F16. The inspected upstream application core Makefile adds `-fshort-enums`, while the
+inspected library Makefile does not. Cross-library structures containing public enums
+must not depend on inconsistent compiler layouts. This project explicitly uses
+default-width enums in both builds, appends `-fno-short-enums` after the upstream
+application rules, and asserts the public MIDI enum width. The ARM build remains
+required to verify the complete ABI; source inspection alone does not establish it.
diff --git a/field/HydraPulse/docs/CODEX_EXECUTION.md b/field/HydraPulse/docs/CODEX_EXECUTION.md
new file mode 100644
index 000000000..644f2fb44
--- /dev/null
+++ b/field/HydraPulse/docs/CODEX_EXECUTION.md
@@ -0,0 +1,81 @@
+# Build, qualify and operate HydraPulse in DaisyExamples
+
+This integration uses the existing root libDaisy gitlink. Do not repin the shared
+submodules or rebuild a dirty/shared dependency tree with qualification flags.
+Use a clean isolated checkout of DaisyExamples; preserve the standalone repository.
+Read the repository and project AGENTS.md and `INTEGRATION_2026-09-08.md` first.
+
+## Host qualification
+
+Run from `field/HydraPulse` with CMake, GCC/Clang and Python 3.10+:
+
+```sh
+python3 -S tools/validate_repo.py
+python3 -S tools/generate_presets.py --check
+python3 -S -m unittest discover -s tests/python -v
+cmake -S . -B build-host -DCMAKE_BUILD_TYPE=Release
+cmake --build build-host --parallel 4
+ctest --test-dir build-host --output-on-failure
+cmake -S . -B build-ubsan -DCMAKE_BUILD_TYPE=Debug -DHPF_UBSAN_ONLY=ON
+cmake --build build-ubsan --parallel 4
+ctest --test-dir build-ubsan --output-on-failure
+CXX=clang++ cmake -S . -B build-sanitize -DCMAKE_BUILD_TYPE=Debug -DHPF_ENABLE_SANITIZERS=ON
+cmake --build build-sanitize --parallel 4
+ctest --test-dir build-sanitize --output-on-failure
+./build-host/render_scenarios artifacts/renders-a
+./build-host/render_scenarios artifacts/renders-b
+python3 -S tools/validate_audio.py artifacts/renders-a --repeat artifacts/renders-b
+```
+
+Clang needs its platform ASan runtime (Ubuntu 24.04: `libclang-rt-18-dev`). A runtime
+startup/link failure is not a sanitizer pass. The adapter stub checks source/API shapes;
+it cannot establish actual BSP compatibility, interrupt behavior or physical results.
+Use clang-format 10 on active C++; preserve `references/r0` byte for byte and regenerate
+`Presets.generated.h` through the generator. QAE warnings in host test utilities are
+not allocations in firmware; inspect firmware warnings against the adapter startup.
+
+## ARM qualification
+
+Inspect root `libDaisy` HEAD, status and recursive gitlinks. In a fresh checkout,
+initialize only root libDaisy and its recorded `tests/googletest` submodule. This pin's
+upstream host tests use `libDaisy/tests/Makefile`, not the newer CMake test layout.
+
+```sh
+python3 -S tools/verify_dependencies.py ../../libDaisy
+python3 -S tools/build_target.py --libdaisy ../../libDaisy --app-type BOOT_NONE --jobs 4
+```
+
+The helper force-rebuilds the library and two independent images, records tool versions,
+command lines, ELF/BIN/MAP, section sizes, hashes, and source/dependency identity.
+Truth uses `build-truth/HydraPulseTruth.*`; the instrument uses
+`build-beat/HydraPulseBeat.*`. No DaisySP is required. Keep default-width BSP enums,
+C++17, finite-math checks, and the 128 KiB internal-flash size gate. Never substitute
+host executables or bypass this gate by guessing a bootloader layout.
+
+## Flash and physical validation
+
+No CI workflow flashes hardware. Flash only when the user has authorized it, after
+identifying the actual device and matching the image to its successful manifest.
+For BOOT_NONE, internal flash starts at 0x08000000. Preserve a recovery image before
+replacing the current application. Other boot layouts require a separate device/layout
+decision. Preview `make -n APP=truth program` and verify its APP-specific ELF path.
+The intentional local `program` recipe override replaces the pinned upstream hardcoded
+`build/` path; GNU make reports an override warning. `make APP=truth program` is the
+explicit diagnostic flash action. Use `APP=beat` only after the diagnostic gate.
+
+Run Truth first. Record boot telemetry, OLED, 16 raw key indices, eight knobs, callback
+load/overruns, safe CV/Gate state, low-level stereo passthrough, clipping and silence.
+Do not flash the instrument until the physical diagnostic smoke test succeeds.
+Then load First Pulse, test Play/Stop and decay, repeated transport, all four voice
+parameters, tempo/steps/accent/voice/A-B/Fill/mutes/Panic in mono before enabling stereo.
+Retain actual operator observations and audio captures; host/ARM/flash verification
+cannot prove click-free analog sound or a complete P0 electrical qualification.
+
+## Publication and continuity
+
+Stage only HydraPulse and its narrowly necessary CI changes. Preserve the original
+candidate manifest as provenance, record every delta, and keep historical package
+results separate from current qualification. Publish through a normal branch/PR,
+verify actual CI runs and merge only passing reviewed changes. No force push or
+standalone repository changes. Report exact final commit, dependency, tests, artifacts,
+CI and hardware status. Hardware without retained observations remains NOT_RUN.
diff --git a/field/HydraPulse/docs/DVPE_INTEGRATION.md b/field/HydraPulse/docs/DVPE_INTEGRATION.md
new file mode 100644
index 000000000..c30f4701d
--- /dev/null
+++ b/field/HydraPulse/docs/DVPE_INTEGRATION.md
@@ -0,0 +1,65 @@
+# DVPE integration: deliberately no invented project
+
+## Current result
+
+No `.dvpe` file or DVPE block implementation is included. The two requested
+repositories are useful investigation targets:
+
+- https://github.com/Denys/DVPE_Daisy-Visual-Programming-Environment
+- https://github.com/Denys/DVPE
+
+A public README for the first project describes `.dvpe` save/load, C++/Makefile
+export and custom/nested blocks. That is not enough to establish the exact
+current serialization schema, valid block identifiers, Field control bindings,
+or generated-code behavior. A complete registry/schema/export round trip was
+not executed, and the other repository could not be qualified through the
+available retrieval route.
+
+The handwritten C++/Makefile implementation is the working development
+candidate. Omitting an unvalidated visual project is intentional.
+
+## Reusable primitives already implemented and host-tested
+
+| Candidate block | C++ source | Contract to preserve |
+|---|---|---|
+| Parameter ramp | `src/dsp/Primitives.h::Ramp` | Finite bounded target, fixed-duration linear transition, unchanged target does not restart |
+| Retrigger-safe one-shot | `src/dsp/Primitives.h::Envelope` | Attack from current amplitude, finite release, repeated release does not postpone silence |
+| Sample-domain groove clock | `src/core/GrooveClock.h` | Rational remainder, 16th-note tempo, pair-preserving swing, no tempo-reset drift |
+| Soft pickup | `src/core/ControlPickup.h` | Target crossing capture, finite input, explicit rearm |
+| Bounded event queue | `src/core/SpscQueue.h` | Single producer/consumer, N-1 capacity, release/acquire ownership, visible overflow |
+| Output-test arm | `src/core/OutputTestArm.h` | Release-to-arm, momentary hold, inhibit, not hardware fail-safe |
+| Four-voice percussion unit | `src/dsp/Voices.*` | Explicit init/trigger/render, four semantic controls, deterministic noise and finite tails |
+
+These are reusable C++ units, not registered DVPE blocks. A queue or output-arm
+primitive also needs a domain-appropriate editor interface; exposing internal
+concurrency semantics as arbitrary audio-rate graph wiring would be misleading.
+
+## Codex integration procedure after the standalone candidate is qualified
+
+1. Inspect the actual DVPE README, package manifests, schema/migration code,
+ block registry, board definitions, compiler/code generator, exporter,
+ known fixtures and tests at a recorded commit. Do not classify the project
+ from folder shape.
+2. Identify supported Field hardware and event/control/audio-rate scheduling.
+ Verify how initialization, per-block processing, per-sample processing,
+ persistent state, bounded memory and mutable parameters are represented.
+3. Map a single simple primitive, preferably Ramp, to a genuine existing block
+ definition pattern. Add complete parameter limits, rate semantics, help
+ text and error paths in the actual registry's format.
+4. Use the editor/serializer to create a small valid graph, save `.dvpe`,
+ reopen it, export code and compile against the pinned real library.
+ Compare its host numerical output with the C++ reference.
+5. Add tests for schema round trips, generated code, parameter validation and
+ mismatched execution rates. Ensure graph edits cannot introduce allocation,
+ logging or blocking in the callback.
+6. Only then compose the complete instrument graph if its scheduler/state
+ model can express transport, A/B/Fill, foreground/UI ownership and bounded
+ events without hidden unsafe glue. Otherwise keep the handwritten engine
+ and make only clearly supported blocks reusable.
+7. Record exact DVPE commit, package lock, graph schema version, generator
+ version, output diff and build results. Preserve existing projects and
+ migration compatibility; propose public registry changes separately.
+
+Do not create a fictional `.dvpe` with guessed JSON keys, omit unsupported nodes
+silently, or call a graph complete because it contains labels matching the
+instrument's modules.
diff --git a/field/HydraPulse/docs/INTEGRATION_2026-09-08.md b/field/HydraPulse/docs/INTEGRATION_2026-09-08.md
new file mode 100644
index 000000000..61bafdfd4
--- /dev/null
+++ b/field/HydraPulse/docs/INTEGRATION_2026-09-08.md
@@ -0,0 +1,119 @@
+# DaisyExamples integration - 2026-09-08
+
+Status: host and ARM qualification PASS locally and on branch CI. Both firmware flashes VERIFIED on two different Seed processors. Diagnostic listening FAILED/INCONCLUSIVE on the first Seed; operator reports working instrument output, sequencer, some sounds and controls on the replacement Seed. Full musical and stereo qualification remains incomplete.
+
+## Inspected inputs and preservation
+
+- Target master: `041bfcbb7975316606ece8f1ccbacbab9c0e1482`.
+- Branch: `codex/hydrapulse-field-integration`, in an independent clone under `.worktrees/hydrapulse-integration-20260908`.
+- ZIP: `HydraPulse_DaisyExamples_0.2.0_candidate_2026-09-07.zip`.
+- Installer preflight: 151 new files, zero collisions; all payload hashes checked before installation.
+- Original workspace: 150 candidate files identical to ZIP, workflow missing; extra delivery attachments left untouched.
+- Standalone `Denys/HydraPulse-Field` main: `184db5473aea258f660604d791d305f7061671fc`, inspected through the authenticated GitHub connector. It contains newer R0 integration, a separate FieldTruth implementation, tests and research. Nothing there was changed or copied over. `references/r0` means the original supplied 29-file archive, not a mirror of current standalone main.
+- Root libDaisy gitlink retained: `85172e2b5c9abea1bf56dcd5d2c7b3f5717eb66b`; LICENSE blob `1c3a251694f1d1842c3f114508d36ca63cddf275`.
+- Nested googletest: `f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4`.
+- No root checkpoint or HydraPulse entry in LATEST_PROJECTS was available. Nearest project documents and live source were used.
+
+## Candidate deviations
+
+1. Reuse the existing root libDaisy pin instead of adding candidate-private `cc146d5065dd8286078a662e2830bf820c37a612`. Root gitlinks and the primary dirty library checkout are unchanged.
+2. Use the pinned BSP's `dsy_gpio_write` Gate Out API; give the adapter stub the same signature and LED overloads.
+3. Make fixed-width integer `std::max` calls explicit, because ARM newlib uses a different underlying `uint32_t` type than host GCC.
+4. Use floating-point zero for LED brightness to avoid the BSP's integer/float overload ambiguity.
+5. Format active C++ with CI's clang-format 10.0.1; replace unsupported SortIncludes enum with boolean. Generated presets have formatter guards. Exclude only the immutable R0 archive from the root formatting workflow.
+6. Add direct BSP includes to the firmware translation units. QAE's per-file analysis does not follow adapter includes.
+
+7. Use `-Os` for application code: the instrument at `-O2` exceeded internal flash by 3632 bytes. Both images now link below 128 KiB without a bootloader/layout change; final forced-build manifest verified.
+8. Override the local SWD `program` recipe to use the correct `build-truth` or `build-beat` ELF; upstream libDaisy remains unchanged.
+9. Force-build helpers now create and mark directory targets old (`-o`) while forcing all objects with `-B`; the pinned upstream `mkdir build` recipe otherwise fails on a second qualification run.
+10. Add `tools/qualify_host.py` to run and log all host gates. Teach logger-width tests to accept formatter-inserted whitespace before the format string.
+11. Repair a Mermaid sequence-note semicolon parse error and replace the clock diagram layout with an equivalent flowchart to avoid overlapping edge labels. Both Markdown fences and `.mmd` sources match.
+12. Change only the OLED fault counter prefix from `E` to `#`, avoiding QAE's regex false positive on an escaped percent followed by E. Formatting remains integer-only.
+
+13. Limit final Git status collection to HydraPulse with unrelated submodules ignored. A complete ARM build had reached artifact creation but the old repository-wide status scan timed out before writing its manifest. The failure transcript is preserved; only a successful manifest qualifies the final images.
+
+14. Add Git attributes preserving archived R0 and raw acquisition log bytes across platform checkouts. Raw logs are binary-diff entries so recorded trailing whitespace remains intact.
+
+15. Match the root workflows' `actions/checkout@v7` and default credential persistence for the parent checkout. Early cleanup with `persist-credentials: false` fails on the repository's pre-existing orphan `.tmp/ai-book-sparse` gitlink, under both v4 and v7, before tests run. The ephemeral CI token has only contents-read permission; independent libDaisy checkout still removes credentials immediately. No unrelated submodule metadata was changed.
+
+## Current software evidence
+
+- `tools/qualify_host.py`: all 15 commands returned zero, with per-command logs and a JSON index.
+- Release (GCC 13.3): 14/14 CTest PASS; UBSan (GCC 13.3): 14/14 PASS; ASan+UBSan (Clang 18.0): 14/14 PASS.
+- Python: 38/38 PASS; repository/provenance, generated presets and 29-file R0 preservation PASS.
+- Host deterministic stereo render comparison PASS. No host render is a Field recording.
+- Upstream libDaisy host tests: 151/151 PASS with GCC 13.3. Clang 18 first rejected an existing GNU variable-length-array extension under upstream -Werror; no upstream source or flags were changed for the GCC run.
+- QAE: zero errors, seven advisory warnings. Two NO-START-ADC reports are per-file false positives: `InitHardware()` starts ADC before `StartAudio()`. Remaining heap/iostream reports concern host tests and the renderer.
+- clang-format 10.0.1: all 37 active C++ files match. Immutable R0 sources were not formatted.
+- Mermaid 11.12.2 / headless Edge: all eight diagrams rendered to SVG/PNG with no page errors; all eight screenshots inspected for clipped or overlapping labels. The corrected clock flowchart is readable. Desktop intrinsic-size renders only; no mobile layout claim.
+- ST-Link identified an STM32H74x/75x Cortex-M7 with a single 128 KiB internal-flash bank. Internal flash backup retained locally before any write: 131072 bytes, SHA-256 `80971a0ee9a0e019ac692308618a8300a1861e5a78102033f3162a31272dc95e`. Recovery image is not published.
+
+## ARM and flash evidence
+
+- Final `tools/build_target.py` completed successfully after rebuilding the library and both images. See `evidence/arm-build-2026-09-08.json` for exact input, compiler, dependency and ELF/BIN/MAP SHA-256 records.
+- Runtime source digest: `24e035a726a12d2ad72f8b69d1491a3ad17267acbbcf1a38135e8937bd8250cc`.
+- FieldTruth (`HydraPulseTruth.bin`): 107280 bytes; SHA-256 `72a21e32621174f5d66cbcade3711b21d5810711813b27fc405c696e48bdab60`.
+- HydraPulse (`HydraPulseBeat.bin`): 122320 bytes; SHA-256 `6b0d5b518b20ad57720755ee6330b684b2e905704ebaa35d906225a9173dc458`.
+- Compiler: GNU Arm Embedded 13.2.1, Ubuntu package; Cortex-M7 hard-float; BOOT_NONE. Application `-Os`, library `-O3`, both finite-math qualification flags and default-width enums.
+- ELF sections (text/data/bss bytes): Truth 104252/2988/86116; Beat 113492/8788/85196. Linker usage: Truth FLASH 107280, SRAM 71160, RAM_D2 17224; Beat FLASH 122320, SRAM 76040, RAM_D2 17224; BACKUP_SRAM 12 each. Neither application allocates SDRAM or QSPI sections. The linked SRAM includes reserved heap/stack; no measured stack high-water claim.
+- Linker emits enum attribute warnings from GNU runtime objects. Actual application and libDaisy translation units use int-width enums and application static assertions enforce the public BSP MIDI enum width. The warning is retained in logs. Newlib nosys stubs also warn about unsupported POSIX I/O; no such I/O is performed in audio callbacks.
+- Diagnostic ELF matched current runtime sources and successful manifest immediately before flashing.
+- Native Windows OpenOCD 0.12.0 with ST-Link serial `0020000A5553500920393256`: **Programming Finished / Verified OK / Resetting Target**, exit 0. The native tool executes the same inspected APP-specific ELF recipe because USB/SWD is attached to Windows, while compilation runs in WSL.
+- Instrument flash: VERIFIED on the replacement Seed after the operator explicitly requested loading HydraPulse despite the unresolved diagnostic audio result. This is an operator-authorized diagnostic continuation, not a passed P0 promotion. Basic musical output is now operator-reported on the replacement Seed; full P0 and all-gesture audio acceptance remain incomplete.
+
+## Initial hardware runtime capture
+
+The flashed diagnostic enumerated as Daisy USB CDC COM5 (`0483:5740`) and emitted
+`build=24e035a726a1`, 48 kHz / 48-sample blocks, CPU 400 MHz and tick clock 200 MHz.
+A 45-second capture reports max load 28 per mille, zero overruns and zero late starts.
+Raw pressed indices captured: 0, 1, 2, 8, 9. This is not all sixteen physical keys.
+CV/Gate output commands were zero; audio input peaks were approximately 0.70 full scale
+in both channels. Input telemetry alone does not prove analog output or passthrough quality.
+The initial serial backlog contains one malformed record and a cumulative drop count of
+857. Preserve this capture; do not classify it as clean complete hardware validation.
+No intentional MIDI stimulus, knob endpoint sweep, all-key mapping or operator listening
+result was recorded during the initial captures. The later operator report and replacement-Seed flash are recorded below.
+
+A second 30-second acquisition discarded the connection backlog before capture.
+It contains 0 malformed records, with callback delta 29560 and drop delta 0.
+Overrun and late-start counters remained zero. The cumulative drop counter was 3058 at capture start and 3058 at its end; disconnected time is not a measured clean interval.
+Raw logs and derived summaries for both acquisitions are retained without byte normalization.
+
+## Operator report and replacement Seed
+
+The operator reported a responsive OLED, key LEDs responding on press, and both switches affecting the display. Knob LEDs did not change brightness; inspection confirms FieldTruth drives only key LEDs, so knob-dependent LED brightness is not implemented. This does not prove all eight analog knob endpoints.
+
+The operator heard either no audio or a whistle with FieldTruth, then replaced the Seed processor. An older synth on the replacement reportedly did not whistle. The cause is unresolved; input passthrough routing, the first processor and the firmware have not been discriminated by measurement. Do not mark diagnostic audio PASS.
+
+The operator explicitly requested loading HydraPulse on the replacement. SWD confirmed a different processor UID (`002e0044 33305110 39383339`, versus the original `001c003b 3330510f 39383339`), still STM32H74x/75x with 128 KiB flash. A separate full internal-flash backup was retained locally before writing: 131072 bytes, SHA-256 `59a8481b89a5f621eb6f8a14318227e1fccdee24824bb562b9ed1e2f7d1735a7`. Neither recovery image is published.
+
+HydraPulseBeat's local qualified ELF was programmed, verified and reset through ST-Link, exit zero. The replacement enumerated on COM7 and emitted `app=beat build=24e035a726a1`, 48 kHz / 48 samples, CPU 400 MHz. A 55-second capture had no malformed records, max reported load 123 per mille, zero faults/overruns/late starts and zero MIDI/snapshot queue drops. These counters do not establish audible behavior or physical gesture coverage. The subsequent operator listening report is recorded below; the capture itself does not verify listening results.
+
+## Subsequent operator smoke result
+
+After the replacement-Seed HydraPulse flash, the operator reported that output now works correctly, the sequencer works, and at least some sounds work. SW1/SW2, keys, key LEDs, knobs and volume were reported working. This is a positive basic instrument smoke result reported by the operator, not a recorded or independently measured audio test.
+
+The operator suspects the previous processor was faulty. That is plausible but unproven: the earlier listening test used FieldTruth passthrough and the successful later test uses HydraPulse synthesis on a different Seed. A controlled comparison or electrical measurement is still needed to identify the cause of the original whistle.
+
+Knob LEDs are not implemented in either firmware image. Both display routines first clear all LED channels, then populate only key LEDs; the instrument additionally sets the two switch LEDs. No knob LED brightness is written. Their dark state is therefore expected firmware behavior, not evidence that the knobs or their LEDs are defective.
+
+Still unverified: all four voices individually; repeated Play/Stop and decay-to-silence; detailed click/DC/clipping checks; each musical gesture (accent, voice select, A/B, Fill, mute and Panic); measured stereo channel/routing behavior; all sixteen raw key mappings and all eight knob endpoints; full CV/Gate/MIDI P0 measurements. Do not infer these from the general positive smoke report.
+
+## Remote software evidence and source identity
+
+At source commit `fdd22f363b04447ceb92c2a2774ae9d96edc54df`, the [branch qualification run](https://github.com/Denys/DaisyExamples/actions/runs/34232758870) passed all three host modes and both ARM builds. [Build All](https://github.com/Denys/DaisyExamples/actions/runs/34232764124) and [Fix Style](https://github.com/Denys/DaisyExamples/actions/runs/34232764041) passed on the PR. Subsequent changes to this evidence report do not change runtime source.
+
+All six downloaded CI ELF/BIN/MAP artifacts matched their manifest sizes and SHA-256 values. See `evidence/ci-arm-build-2026-09-08.json`. Local and CI manifests differ in the raw line endings of `src/app/Presets.generated.h`: CRLF locally and LF in Git. All 22 build inputs are identical after newline normalization; `evidence/commit-source-bridge-2026-09-08.json` records that comparison. This changes the embedded source ID (`24e035a726a1` local, `41e814118b1e` CI). For each application, the two BIN files are byte-identical after replacing only that embedded ID; ELF/MAP also carry build-directory/debug paths. Keep the local manifest for the images actually flashed; do not substitute CI hashes for them.
+
+## Earlier attempts retained for traceability
+
+- Candidate-after-install Python: 38/38 PASS.
+- Candidate-after-install GCC 13.3 Release: 14/14 CTest PASS.
+- GCC 13.3 UBSan: 14/14 CTest PASS.
+- Deterministic host sonic scenarios: PASS for two runs; these are not Field recordings.
+- First ARM qualification: libDaisy compiled with GCC ARM 13.2.1; firmware failed on the target-specific issues listed above. Failure transcript retained under `artifacts/2c2ca122dc96/BOOT_NONE/build.log`.
+- First Clang ASan+UBSan link failed because libclang-rt was absent. Runtime installed; rerun required.
+- QAE first scan: 3 errors, 7 warnings. Two missing-direct-include errors and one regex false positive interpreting the escaped percent before `E` as float printf. No float printf support was added.
+- ST-Link USB serial `0020000A5553500920393256` detected. Daisy USB CDC not yet detected. No flash, runtime, physical control or analog audio result is implied.
+
+Raw run logs and renders are under `artifacts/integration/`. `PACKAGE_MANIFEST.json` is the original ZIP inventory, not a hash assertion about the adapted working tree; run `install_additive.py` only against the untouched extracted candidate. Current source identity and image hashes are recorded by the final ARM manifest. No `dist/` binaries are regenerated by this task: the repository's generic dist helper searches `build/`, while these two deliberately separate images are uploaded by HydraPulse CI.
diff --git a/field/HydraPulse/docs/PRESETS.md b/field/HydraPulse/docs/PRESETS.md
new file mode 100644
index 000000000..de36f6988
--- /dev/null
+++ b/field/HydraPulse/docs/PRESETS.md
@@ -0,0 +1,225 @@
+# Tutorial presets and progressive lessons
+These are original authored patterns and settings from `presets/tutorials.json`. No commercial factory pattern was transcribed. Runtime IDs are 0–7. All banks share voice parameters; Master is retained when loading.
+Load only while stopped: hold SW2, use key 14/15 to preview, then key 16 to confirm. Release SW2 to return Home. Save does not exist yet; loading discards edits.
+Grid legend: `.` empty, `x` active, `X` accented. Positions run left to right from 01 to 16.
+## 0. Blank Canvas
+**Lesson:** Build a rhythm from silence.
+**Build, then remove.** Select Hammer, tap steps 1/5/9/13, then select Crack and tap 5/13. Listen, then remove one kick. The goal is to learn the editing surface, not a finished arrangement.
+**Next exercise:** Hold one existing kick step for 400 ms. Compare that accent to its neighbors.
+Tempo: **110.0 BPM**. Swing share: **50.0%**. Drive: **0%**.
+| Voice | Tune % | Decay % | Character % | Level % | Base Hz, approx. | T60 ms, approx. |
+|---|---:|---:|---:|---:|---:|---:|
+| Hammer | 36 | 32 | 28 | 80 | 49.9 | 164.4 |
+| Crack | 45 | 28 | 50 | 72 | 189.2 | 92.5 |
+| Steel | 45 | 25 | 40 | 55 | 1195.6 | 39.4 |
+| Arc | 40 | 40 | 30 | 65 | 168.2 | 235.2 |
+
+```text
+ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
+A Hammer . . . . . . . . . . . . . . . .
+A Crack . . . . . . . . . . . . . . . .
+A Steel . . . . . . . . . . . . . . . .
+A Arc . . . . . . . . . . . . . . . .
+B Hammer . . . . . . . . . . . . . . . .
+B Crack . . . . . . . . . . . . . . . .
+B Steel . . . . . . . . . . . . . . . .
+B Arc . . . . . . . . . . . . . . . .
+Fill Hammer . . . . . . . . . . . . . . . .
+Fill Crack . . . . . . . . . . . . . . . .
+Fill Steel . . . . . . . . . . . . . . . .
+Fill Arc . . . . . . . . . . . . . . . .
+```
+## 1. First Pulse
+**Lesson:** Hear pitch sweep and decay separately.
+**Separate pitch from tail.** Keep Drive low. Move Tune through its pickup target, then lower it. Return near the stored value and alter Decay. Finally change Character while leaving Tune steady.
+**Next exercise:** Try bank B, then hold Fill for the last few steps of a bar.
+Tempo: **110.0 BPM**. Swing share: **50.0%**. Drive: **5%**.
+| Voice | Tune % | Decay % | Character % | Level % | Base Hz, approx. | T60 ms, approx. |
+|---|---:|---:|---:|---:|---:|---:|
+| Hammer | 32 | 35 | 35 | 85 | 47.5 | 180.7 |
+| Crack | 40 | 28 | 50 | 70 | 179.9 | 92.5 |
+| Steel | 40 | 20 | 40 | 55 | 1126.5 | 34.4 |
+| Arc | 35 | 40 | 25 | 60 | 146.3 | 235.2 |
+
+```text
+ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
+A Hammer X . . . X . . . X . . . X . . .
+A Crack . . . . . . . . . . . . . . . .
+A Steel . . . . . . . . . . . . . . . .
+A Arc . . . . . . . . . . . . . . . .
+B Hammer X . . . X . . x X . . . X . . .
+B Crack . . . . . . . . . . . . . . . .
+B Steel . . . . . . . . . . . . . . . .
+B Arc . . . . . . . . . . . . . . . .
+Fill Hammer X . . . X . . . X . . . X . . .
+Fill Crack . . . . . . . . . . . x . x x x
+Fill Steel . . . . . . . . X x x x X x x x
+Fill Arc . . . . . . . . . . . . . . . .
+```
+## 2. Backbeat
+**Lesson:** Balance kick, snare and offbeat hats.
+**Balance complementary roles.** Mute Steel, set the kick/snare balance with Level, then bring Steel back. Listen to accents before increasing the overall level.
+**Next exercise:** Select Crack and shorten Decay enough to open space between the backbeats.
+Tempo: **120.0 BPM**. Swing share: **50.0%**. Drive: **10%**.
+| Voice | Tune % | Decay % | Character % | Level % | Base Hz, approx. | T60 ms, approx. |
+|---|---:|---:|---:|---:|---:|---:|
+| Hammer | 40 | 28 | 25 | 80 | 52.4 | 144.9 |
+| Crack | 35 | 28 | 55 | 74 | 171.0 | 92.5 |
+| Steel | 48 | 20 | 35 | 55 | 1239.0 | 34.4 |
+| Arc | 40 | 30 | 35 | 65 | 168.2 | 159.7 |
+
+```text
+ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
+A Hammer X . . . . . . . X . . . . . . .
+A Crack . . . . X . . . . . . . X . . .
+A Steel . . x . . . x . . . x . . . x .
+A Arc . . . . . . . . . . . . . . . .
+B Hammer X . . . . . x . X . . . . . x .
+B Crack . . . . X . . . . . . . X . . .
+B Steel . . x . . . x . . . x . . . x .
+B Arc . . . . . . . . . . . . . . . .
+Fill Hammer X . . . . . . . X . . . . . . .
+Fill Crack . . . . X . . . . . . x X x x x
+Fill Steel . . x . . . x . X x x x X x x x
+Fill Arc . . . . . . . . . . . . . . . .
+```
+## 3. Broken Grid
+**Lesson:** Hear syncopation without probability.
+**Change placement, not machinery.** Compare A and B at a fixed tempo. The grid is deterministic: the variation comes from note placement, not probability.
+**Next exercise:** Edit one Arc step while another bank plays. Observe the edit/play bank distinction.
+Tempo: **126.0 BPM**. Swing share: **50.0%**. Drive: **16%**.
+| Voice | Tune % | Decay % | Character % | Level % | Base Hz, approx. | T60 ms, approx. |
+|---|---:|---:|---:|---:|---:|---:|
+| Hammer | 45 | 24 | 48 | 82 | 55.8 | 127.8 |
+| Crack | 40 | 24 | 65 | 75 | 179.9 | 82.1 |
+| Steel | 58 | 18 | 60 | 50 | 1395.5 | 32.6 |
+| Arc | 45 | 25 | 35 | 62 | 193.5 | 131.6 |
+
+```text
+ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
+A Hammer X . . . . . x . X . . x . . . .
+A Crack . . . . X . . . . . . . X . . .
+A Steel X . x . . x x . X . x . . x x .
+A Arc . . . x . . . . . . . x . . . .
+B Hammer X . . . . . . . X . . x . . x .
+B Crack . . . . X . . . . . . . X . . .
+B Steel X . x . . x x . X . x . . x x .
+B Arc . . . x . . . x . . . x . . . x
+Fill Hammer X . . . . . x . X . . x . . . .
+Fill Crack . . . . X . . . . . . x X x x x
+Fill Steel X . x . . x x . X x x x X x x x
+Fill Arc . . . x . . . . . . . x . . . .
+```
+## 4. Alloy Swing
+**Lesson:** Compare straight and delayed offbeats.
+**Hear swing in pairs.** Compare knob 6 at straight 50% long-step share with the stored swing setting. Leave the note pattern unchanged during the comparison.
+**Next exercise:** Accent an offbeat hat. Avoid maximizing every hat level; contrast makes the swing audible.
+Tempo: **124.0 BPM**. Swing share: **61.0%**. Drive: **18%**.
+| Voice | Tune % | Decay % | Character % | Level % | Base Hz, approx. | T60 ms, approx. |
+|---|---:|---:|---:|---:|---:|---:|
+| Hammer | 30 | 35 | 38 | 82 | 46.3 | 180.7 |
+| Crack | 46 | 34 | 70 | 72 | 191.1 | 110.8 |
+| Steel | 55 | 42 | 55 | 56 | 1346.6 | 62.4 |
+| Arc | 55 | 35 | 45 | 60 | 255.9 | 193.8 |
+
+```text
+ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
+A Hammer X . . . . . . x X . . . . . . .
+A Crack . . . . X . . . . . . . X . . .
+A Steel X . x . X . x . X . x . X . x .
+A Arc . . . x . . . . . x . . . . . x
+B Hammer X . . . . . x x X . . . . . x .
+B Crack . . . . X . . . . . . . X . . .
+B Steel X . x . X . x . X . x . X . x .
+B Arc . . . x . . . x . x . . . . . x
+Fill Hammer X . . . . . . x X . . . . . . .
+Fill Crack . . . . X . . . . . . x X x x x
+Fill Steel X . x . X . x . X x x x X x x x
+Fill Arc . . . x . . . . . x . . . . . x
+```
+## 5. Arc Study
+**Lesson:** Explore phase modulation, tune and decay.
+**Learn the fourth voice.** Mute the other voices to hear Arc. Move Tune first, then Character. Try a short decay for a dry strike and a longer decay for the modulation tail.
+**Next exercise:** Return the other voices one at a time. High Character and high Tune are also the place to listen for aliasing.
+Tempo: **105.0 BPM**. Swing share: **50.0%**. Drive: **12%**.
+| Voice | Tune % | Decay % | Character % | Level % | Base Hz, approx. | T60 ms, approx. |
+|---|---:|---:|---:|---:|---:|---:|
+| Hammer | 40 | 28 | 35 | 80 | 52.4 | 144.9 |
+| Crack | 50 | 30 | 45 | 70 | 199.0 | 98.3 |
+| Steel | 40 | 20 | 40 | 50 | 1126.5 | 34.4 |
+| Arc | 62 | 55 | 60 | 62 | 311.1 | 420.4 |
+
+```text
+ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
+A Hammer X . . . . . . . X . . . . . . .
+A Crack . . . . . . . . . . . . X . . .
+A Steel . . x . . . x . . . x . . . x .
+A Arc X . . x . . x . . x . x . . x .
+B Hammer X . . . . . x . X . . . . . x .
+B Crack . . . . . . . . . . . . X . . .
+B Steel . . x . . . x . . . x . . . x .
+B Arc X . . x . . x x . x . x . . x x
+Fill Hammer X . . . . . . . X . . . . . . .
+Fill Crack . . . . . . . . . . . x X x x x
+Fill Steel . . x . . . x . X x x x X x x x
+Fill Arc X . . x . . x . . x . x . x x x
+```
+## 6. Night Shift
+**Lesson:** Perform A/B, mutes and momentary Fill.
+**Perform an arrangement.** Let A repeat, request B, then borrow Fill. Drop one voice with Shift + mute, bring it back, and use Drive as a temporary intensifier.
+**Next exercise:** Use Panic once at low external listening level, release both switches, then restart deliberately.
+Tempo: **132.0 BPM**. Swing share: **54.0%**. Drive: **38%**.
+| Voice | Tune % | Decay % | Character % | Level % | Base Hz, approx. | T60 ms, approx. |
+|---|---:|---:|---:|---:|---:|---:|
+| Hammer | 25 | 50 | 70 | 86 | 43.6 | 289.8 |
+| Crack | 30 | 40 | 80 | 74 | 162.5 | 132.6 |
+| Steel | 50 | 50 | 60 | 52 | 1268.9 | 77.5 |
+| Arc | 30 | 60 | 68 | 60 | 127.2 | 510.2 |
+
+```text
+ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
+A Hammer X . . . X . . x X . . . X . . .
+A Crack . . . . X . . . . . . x X . . .
+A Steel X . x . X . x . X . x . X . x .
+A Arc . . . x . . . x . . x . . . . x
+B Hammer X . . . X . x x X . . . X . x .
+B Crack . . . . X . . . . . . x X . . .
+B Steel X . x . X . x . X . x . X . x .
+B Arc . . . x . . . x . . x . . . . x
+Fill Hammer X . . . X . . x X . . . X . . .
+Fill Crack . . . . X . . . . . . x X x x x
+Fill Steel X . x . X . x . X x x x X x x x
+Fill Arc . . . x . . . x . x x x . x . x
+```
+## 7. Fracture Lab
+**Lesson:** Dense stress lesson: arrange with subtraction.
+**Make density useful.** Begin by muting two voices rather than making the pattern denser. Add them back only where they contribute. Compare short Fill gestures with a full held bar.
+**Next exercise:** Rapidly retrigger the selected voice while stopped, then capture the result for the transition/listening QA exercise.
+Tempo: **144.0 BPM**. Swing share: **57.0%**. Drive: **52%**.
+| Voice | Tune % | Decay % | Character % | Level % | Base Hz, approx. | T60 ms, approx. |
+|---|---:|---:|---:|---:|---:|---:|
+| Hammer | 45 | 40 | 90 | 84 | 55.8 | 211.5 |
+| Crack | 65 | 38 | 90 | 68 | 231.6 | 124.9 |
+| Steel | 68 | 50 | 70 | 46 | 1571.8 | 77.5 |
+| Arc | 72 | 55 | 85 | 56 | 411.5 | 420.4 |
+
+```text
+ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
+A Hammer X . . x . . x . X . . x . . x .
+A Crack . . . . X . . x . . . . X . . x
+A Steel X x x x X x x x X x x x X x x x
+A Arc . x . . . x . . . x . x . x . .
+B Hammer X . . x . . . . X . . x . . . .
+B Crack . . . . X . . x . . . . X . . x
+B Steel X x x x X x x x X x x x X x x x
+B Arc . x . . . x . x . x . x . x . x
+Fill Hammer X . . x . . x . X . . x . . x .
+Fill Crack . . . . X . . x . . . x X x x x
+Fill Steel X x x x X x x x X x x x X x x x
+Fill Arc . x . . . x . . . x . x . x . x
+```
+## Host WAV scenario
+Each ten-second host render starts the tutorial, queues B at 3 s, requests Fill at 5 s, releases Fill at 6 s, and panics at 7 s. The final seconds test silence. Master is 0.70 for the renderer, not a value loaded by the tutorial. Blank Canvas is intentionally silent.
+The supplied WAVs are native C++ renders, not recordings from Daisy Field. Matching hashes show repeatability in the tested host build, not bit equality across processors.
+## Blank notes sheet
+Record the voice values above, then three sixteen-step grids per voice. Include tempo, swing, drive, master, mutes, the firmware hash and anything learned during the lesson. A/B changes occur on the next bar; Fill continues the current phase.
diff --git a/field/HydraPulse/docs/QA_PLAN.md b/field/HydraPulse/docs/QA_PLAN.md
new file mode 100644
index 000000000..fc11996ea
--- /dev/null
+++ b/field/HydraPulse/docs/QA_PLAN.md
@@ -0,0 +1,264 @@
+# Qualification plan: software evidence, then hardware truth
+
+## Acceptance model
+
+A result has an evidence class, a tested object and a scope. Use
+`VERIFIED | DERIVED | PROPOSED | ASSUMED | HOLD | NOT_RUN`.
+`VERIFIED` does not mean universally correct: name the exact source/build,
+procedure, stimulus, duration, fixture and observed result.
+
+The stages are independent:
+
+| Gate | Required evidence | What it does not prove |
+|---|---|---|
+| R0/core | Host tests, negative tests, exact scenario replay | ARM timing or peripheral operation |
+| Adapter contract | Actual firmware translation units compiled against declared fake BSP; application routing tests | Actual libDaisy ABI, UART, codec, DMA, linker |
+| ARM | Exact pinned dependencies, full rebuild, ELF/BIN/MAP and hashes | Power-up, analog performance or real-time margin |
+| P0 | Measured Field I/O, mapping and callback evidence with raw artifacts | Musical usability of four voices |
+| Beat qualification | Target stress, audition, interface tests and timed loopback | Full seven-voice product or Hydrasynth integration |
+
+The delivered Beat code is an implemented candidate, not a declaration that
+P0 has passed. Review `VALIDATION.md` for checks actually executed.
+
+## A. Native automation
+
+Run the commands in `CODEX_EXECUTION.md`. The current CTest registration is the
+source of truth for executable names.
+
+### Pattern and transport
+
+Exercise all valid and invalid track/step bounds, toggle twice identity, clear
+isolation, deterministic property stress, stable ordering, 16-step wrap, stopped
+silence, start at step zero, repeated Start/Stop, A/B bar-boundary switching and
+momentary Fill return. Check A/B editing versus the playing bank separately.
+A mute should neither delete steps nor reappear as a latent stuck trigger.
+
+Test simultaneous bank requests, Fill edge near step zero, preset confirmation
+while playing, and stopping immediately before a boundary. Presets must refuse
+to load while running. Start/stop/cancel must remain meaningful while a preset
+transaction is fading.
+
+### Clock
+
+Check 44.1/48/96 kHz and integer/fractional BPM; compare rational ideal event
+positions, not wall-clock execution speed. Error against the sample-quantized
+schedule is at most one sample in the tested fixed-tempo scenarios. Test
+repeated identical tempo updates, changed tempo mid-interval, extreme legal
+tempo, phase preservation and swing pair length. Swing must commit at a pair
+boundary without shifting the pair's total duration. Check invalid rates,
+NaN/Infinity, out-of-range tempo and transactional rejection.
+
+Extend the long-run test to a representative hour or an equivalent event-domain
+reference before any external clock implementation. No MIDI-clock drift claim
+exists for a core that deliberately ignores external clock.
+
+### Control gestures
+
+Use deterministic 1 kHz stable input frames. Cover tap release, hold threshold
+minus/at/plus one tick, release ordering, keys already held when Shift is
+pressed, Shift released before the key, both switches released in either order,
+repeated panic, switching voices with knobs elsewhere, and preset-load pickup.
+Confirm no modifier gesture leaks a normal step toggle on release.
+
+Check the exact beginner path without a menu browser:
+select voice, enter steps, edit four voice controls, Play.
+Confirm a step hold toggles accent once, not at every scan. Clear requires a
+stopped transport and a one-second hold. Preset confirmation requires stopped
+transport; preview expires after five seconds.
+
+### Concurrency and bounded work
+
+The audio callback owns controls, Engine and DSP. Foreground is the sole
+producer of MIDI commands and the sole consumer of UI snapshots. Tests include
+queue capacity, wrap and 200,000 ordered producer/consumer transfers. Capacity
+is N-1, not N. Overflow must be visible and must never corrupt adjacent state.
+
+The callback drains at most eight ordinary MIDI commands per block. Urgent
+stop/panic bypasses the ordinary queue and flushes at most its fixed capacity.
+The no-allocation test watches callback-equivalent engine/controller processing.
+Review the real firmware callback for accidental logging, display, storage,
+formatting, heap allocation or blocking calls as well. A passing heap hook
+does not detect every possible third-party allocation or interrupt stall.
+
+### DSP and sonic scenarios
+
+Render each voice across the parameter corners and long tails. Test finite
+output, normalized bounds, zero-input silence, tail convergence, frequency
+range limits, parameter ramp endpoints, repeated release, rapid retrigger and
+preset transitions. Each voice has an independent deterministic noise seed.
+
+Use matched state trajectories to isolate discontinuities:
+run two identical voices; apply a retrigger or parameter jump to only one; compare
+the first affected sample against the uninterrupted trajectory. A large raw
+adjacent-sample difference in a noisy snare is not automatically a click.
+The current differential regression threshold is 0.08 full scale for the
+selected retrigger cases, with explicit test fixtures and recorded maxima.
+It is not a universal psychoacoustic audibility threshold.
+
+The first candidate failed this test because Arc reset modulation depth
+instantly. The repair ramps its transient envelope from current state and
+smooths the noise-filter coefficient. Keep the before/after regression evidence.
+
+`render_scenarios` renders eight deterministic ten-second stereo scenarios:
+A starts, B is requested at 3 s, Fill is held from 5 to 6 s, and panic occurs
+at 7 s. Check the last second is silent. Two runs on the same compiled object
+must produce identical PCM and metrics. Check all samples/metrics are finite,
+all eight files have the documented format, blank preset is silent, nonblank
+presets have energy, final faults are zero and peak is below 0.737 normalized.
+No bit identity across different architectures/compilers is promised.
+
+Retain peak, RMS, DC mean, maximum adjacent delta, tail peak and fault counts,
+but do not mistake these six metrics for a complete sound-quality test.
+Before musical release add: oversampled reference comparison, alias-energy
+sweeps, spectral-centroid/band-energy scenarios, pitch/decay tolerance checks,
+channel balance, and target/host numerical comparison under documented
+floating-point tolerance. The present Arc and saturation path is not oversampled.
+
+### Sanitizers and build variants
+
+Run Release, Debug+UBSan, Debug+ASan+UBSan, and a second compiler. Build both
+firmware translation units under the host contract check. Verify the intentional
+NaN-negative test exits for its assertion, not a sanitizer startup failure.
+Host memory/threading tests do not prove interrupt or DMA/cache coherence.
+
+## B. Repository, documentation and evidence tests
+
+Validate generated preset freshness, original R0 hashes, full dependency-pin
+format, source/provenance entries, isolation of the host core from BSP includes,
+all required files and all eight Mermaid source/Markdown pairs.
+
+Python negative tests must reject malformed/nonfinite presets, inconsistent
+accent masks, duplicate/malformed telemetry, unqualified hardware evidence,
+missing/hash-mismatched raw artifacts, unsafe paths, conflicting destination
+files and symlink traversal. Installer conflict detection is a complete
+preflight; unrelated destination content must remain byte-identical.
+
+After integration verify actual recursive dependency gitlinks, cleanliness,
+license blob and real required API files. This is deliberately separate from
+offline JSON validation. Render all Mermaid diagrams with the real CLI and
+review them. The manual, control map and factory data must agree with code.
+Check all documented gestures by test, and all rendered display strings for
+the 128x64 layout.
+
+The inspected logger uses a 128-byte message buffer. Test conservative maximum
+numeric widths, string precision and room for CRLF/NUL; split telemetry rather
+than relying on silent truncation.
+
+## C. Actual ARM qualification
+
+Build both Truth and Beat against the pinned dependency. Save compiler version,
+tool paths, full source fingerprint, parent Git SHA and dirty state, recursive
+gitlink records, flags, full log, link map, section sizes and image hashes.
+
+Verify no unintended host stub include path enters the ARM command. Confirm
+default-width enums on both sides of public MIDI API boundaries. Inspect
+float ABI/CPU flags, linker memory layout, static allocation and stack reserve.
+Do not accept source pinning alone as bit-reproducible toolchain evidence.
+
+For the selected actual Seed/bootloader, verify that the compiled APP_TYPE
+matches the programming method. Check internal-flash bounds for BOOT_NONE;
+otherwise make an explicit bootloader/layout decision. Do not increase limits
+on the assumption that an undocumented memory region happens to work.
+
+## D. P0 Field Truth bench sequence
+
+Start with Truth, no connected CV/Gate destination, and muted/low monitoring.
+Use the actual manufacturer's board documentation for electrical limits.
+Hydrasynth and DFAM manual voltages are not Field specifications.
+
+Record Field/Seed revisions and identifying photos, power configuration,
+firmware SHA-256/build ID/APP_TYPE, operator, date/time, temperature when useful,
+measurement instruments and settings, leads/probes/loading, audio interface
+gain/sample rate, fixture wiring and calibration checks. Keep raw captures;
+derived plots or a filled checklist alone are insufficient.
+
+| Test | Stimulus and raw artifact | Completion criterion |
+|---|---|---|
+| BOOT | True power interruption and rail/power video or trace; serial from earliest available connection; repeat with USB absent/present | Explicit distinction between power-on, reset and late USB enumeration; deterministic stopped state and output behavior |
+| KEYS | Every physical key pressed/released individually and in chords; video plus raw key log | All 16 unique raw indices and actual LED relationships mapped; no missing/duplicate stable events in the test |
+| KNOBS | Slow full-range sweeps and stationary endpoints for all eight controls; serial CSV | Monotonic practical behavior, endpoints and idle spread recorded; pickup reachable despite real tolerances |
+| CVIN | Four individually driven inputs with calibrated bounded source and DMM | Transfer/range/polarity/saturation derived from actual voltage, not normalized ADC numbers alone |
+| GATEIN | Pulse generator and scope at defined levels, frequencies and widths | Measured levels and tested detection envelope; qualification includes the 1 kHz polling limitation |
+| CVOUT | SW1 release-to-arm; DAC codes 0,1024,2048,3072,4095 on both channels; DMM with known loading | Measured voltage/code table, fitted gain/offset/residual, inactive and release behavior |
+| GATEOUT | Armed/unarmed and switch-release combinations with scope | Measured low/high levels and release latency; no claim of hardware fail-safe |
+| MIDI | Known messages from independent source, raw transmit log and receive log | Byte/event/channel agreement, isolated overflow test, disconnect/reconnect behavior |
+| DISPLAY | Individual controls and SW2 Vegas mode, photos/video | All required LEDs and OLED pixels/status fields exercised; map recorded |
+| AUDIO | Calibrated interface loopback then Field loopback, stimuli and raw stereo WAVs | Gain, noise, headroom/clipping, crosstalk and routing characterized relative to baseline |
+| TIMING | Scope/logic analyzer on qualified timing testpoint, telemetry, stressed UI/MIDI | Callback interval, maxima and observed deadline margin measured with fixture details |
+
+The firmware's Seed testpoint use is a **proposed probe route**, not proof that
+it is conveniently accessible on an assembled Field. Inspect the actual board
+and continuity first. Do not attach a probe to a presumed unused signal based
+on a package name. If unavailable, choose an electrically safe verified
+alternative and record the code/fixture change.
+
+`SetCvOut*(0)` is a commanded minimum DAC code, not a measured zero-volt
+guarantee. The momentary output interlock requires an initial release of SW1,
+then SW1 held alone; SW2 inhibits output testing. It is software logic and
+cannot remove voltage after a CPU crash. Use an appropriate external test
+fixture and disconnected load.
+
+The gate input is polled once per 1 ms callback. Pulses narrower than that can
+be missed; characterize it rather than describing the diagnostic as a
+sample-accurate external clock receiver. Future clock input needs a separately
+qualified capture/timestamp design.
+
+### Proposed engineering thresholds, to ratify against the fixture
+
+These are project acceptance targets, **not measured results or manufacturer
+specifications**:
+
+- Zero unexpected resets, fault counts, telemetry/key losses, or unexplained
+ I/O changes during the normal diagnostic session.
+- Maximum instrumented callback body below 0.5 ms at 48 samples/48 kHz under
+ worst exercised UI and MIDI activity; zero observed >1 ms body intervals and
+ zero reported >1.5 ms start gaps. Measure full IRQ/DMA overhead separately:
+ the callback probe does not include work outside the application callback.
+- Knob endpoint residuals/pickup tolerance ratified from measured spread; do not
+ hardcode an arbitrary claim of exact 0.000/1.000 physical endpoints.
+- CV output monotonicity and residual within 1% of the **measured** span after
+ affine fitting, unless the hardware requirement calls for tighter calibration.
+ This target is not sufficient for claiming precision 1 V/oct tuning.
+- Audio round-trip gain repeatability within 0.2 dB under an unchanged fixture,
+ no unexplained channel reversal, and clipping/noise/crosstalk boundaries
+ explicitly measured. Choose absolute performance limits only after measuring
+ the direct-interface baseline and establishing required source/load levels.
+
+Do not convert an untested item into PASS to reach an aggregate milestone.
+Use `hardware/p0_record.template.json`, add real metadata and hashed raw files,
+then run `tools/validate_evidence.py`. That validates records, not their truth.
+
+## E. Beat Core on-hardware qualification
+
+After credible P0: flash the separately hashed Beat build; verify all controls
+from the manual, voice identification, master pickup and stopped boot. Check
+initial master .25 does not become a high physical-knob value without pickup.
+
+Run every preset and full A/B/Fill scenario. Compare recorded target output
+with deterministic host renders after removing measured latency and fixture
+gain; define numerical/spectral tolerances before judging. Listen on conservative
+monitoring levels for zipper noise, retrigger discontinuities, truncation,
+stereo imbalance, persistent tails, aliasing and unintended ringing. Intended
+percussive attacks and saturation must not be mislabeled as arbitrary faults.
+
+Exercise rapid accents, long decays, all voices, high drive, parameter sweeps,
+mute/unmute, multiple gestures, preset loads, repeated Panic, MIDI bursts,
+USB disconnected/connected and prolonged idle. Suggested initial soak is
+60 minutes with full raw error/callback summaries and representative scope/audio
+captures; longer reliability work remains a product gate.
+
+For every audible defect preserve: build ID, preset, knob values, gesture/MIDI
+sequence, time range, raw audio and shortest deterministic host reproducer.
+Do not tune a limiter to hide the fault without fixing its cause.
+
+## F. Deferred qualification, not hidden promises
+
+No current claims for persistence/power-loss recovery of saved patterns,
+seven voices, sampling, probability/ratchets/microtiming/locks, MIDI clock sync,
+CV musical modulation, audio-through, Hydra-specific integration, or a valid
+DVPE graph. Add each as a separate testable requirement after the independent
+instrument proves useful.
+
+This plan and its accompanying test code are not an independent audit. Formal
+musical release still needs actual hardware evidence and a separate review
+appropriate to the intended distribution.
diff --git a/field/HydraPulse/docs/QUICK_REFERENCE.md b/field/HydraPulse/docs/QUICK_REFERENCE.md
new file mode 100644
index 000000000..172ac99bb
--- /dev/null
+++ b/field/HydraPulse/docs/QUICK_REFERENCE.md
@@ -0,0 +1,26 @@
+# HydraPulse Beat | quick reference
+
+**First beat:** Shift + 1 selects Hammer → tap steps → K1–K4 shape it → tap/release SW1.
+Key numbers are provisional logical positions; qualify the real Field mapping first.
+
+| Control | Meaning |
+|---|---|
+| K1 / K2 / K3 / K4 | Tune / Decay / Character / Level of selected voice |
+| K5 / K6 / K7 / K8 | Tempo / Swing / Drive / Master |
+| SW1 tap-release | Play / Stop |
+| SW2 held | Shift palette; release always returns Home |
+| Both switches 500 ms | Panic |
+| Step tap-release | Toggle |
+| Step hold 400 ms | Toggle accent; never delete on release |
+
+**Shift:** 1–4 select voices; 5=A; 6=B; 7=edit Fill; 8=hold Fill;
+9–12=mutes; 13=hold clear one second while stopped;
+14/15=previous/next tutorial preview; 16=confirm load while stopped.
+
+A/B changes play at the next step 1, editing immediately. Fill borrows the third bank
+at the current step position. All banks share voice settings and globals.
+Knobs use pickup; cross the stored target. Tutorial load preserves Master.
+
+**No Save, Undo, external clock following, audio-through or live CV/Gate integration.**
+MIDI channel 10 notes 36/38/42/39 trigger Hammer/Crack/Steel/Arc.
+Hardware qualification is pending. These instructions are for the implemented candidate.
diff --git a/field/HydraPulse/docs/USER_MANUAL.md b/field/HydraPulse/docs/USER_MANUAL.md
new file mode 100644
index 000000000..ead14d1d1
--- /dev/null
+++ b/field/HydraPulse/docs/USER_MANUAL.md
@@ -0,0 +1,410 @@
+# HydraPulse Field
+## Player's manual | 0.2.0 candidate | 7 September 2026
+
+### Read this first
+
+HydraPulse is a standalone four-voice drum instrument for Daisy Field. Its normal job is
+simple: choose a drum, enter a one-bar rhythm on sixteen keys, shape its sound, and play.
+The first screen is the playing surface. There is no project browser, machine browser,
+patch hierarchy or required desktop editor.
+
+This manual describes the implemented candidate, not a measured production instrument.
+Two separate programs are supplied as source: **Field Truth** for bench checks and
+**Beat Core** for music. Run and record Field Truth before promoting Beat to normal use.
+No physical control mapping, electrical range, audio measurement or target callback
+deadline has been verified in this delivery. Read the build report for the particular
+binary you are about to use.
+
+Do not connect CV/Gate equipment during the first boot. Lower the external mixer,
+speaker or headphone amplifier before connecting audio or replacing firmware. A software
+limiter is not hearing protection and cannot prevent power-on codec transients.
+Use the Field board's own documented supply and connection requirements. The supply,
+signal ranges and tolerance statements in the Hydrasynth and DFAM manuals do **not**
+describe Daisy Field.
+
+Firmware replacement can overwrite another application or a resident bootloader.
+Follow the boot-layout checks in the Codex guide; do not select a flash address by guesswork.
+
+## 1. First sound and first beat
+
+### Starting condition
+
+Beat starts stopped on **Blank Canvas**, bank A, with Hammer selected. All pattern banks
+are empty. No notes are generated until you audition a voice, receive a supported MIDI
+trigger or start a pattern containing steps. Parameters have stored initial values.
+Master starts at 25% of its software range, regardless of the physical knob position.
+
+The eight knobs use **pickup**. A knob must pass through its stored value before it
+takes control. This prevents selecting a new drum or loading a tutorial from yanking
+that sound to the old knob position. Move slowly through the stored percentage shown
+on the screen, then continue editing. A pickup can occur within about 1.5% of the target;
+it is not an encoder.
+
+### Make a kick pattern
+
+1. Hold SW2, tap key 1, then release SW2. This selects Hammer; while stopped, selection
+ also auditions it.
+2. Tap keys **1, 5, 9 and 13**. Release each key promptly. Their LEDs show four active steps.
+3. Move knob 1 through the displayed Tune value, then lower it for a deeper body.
+4. Move knob 2 through Decay, then shorten the tail until the hits leave useful space.
+5. Tap and release SW1 to start. Step 1 occurs at the next audio sample after the
+ transport action is accepted. The step LEDs show the moving play position.
+6. Adjust the external listening level conservatively. Use knob 8 for the instrument's
+ software Master once its pickup target has been crossed.
+
+### Add snare and hat
+
+Hold SW2, tap key 2, release SW2. You are editing Crack without changing the kick pattern.
+Tap keys 5 and 13 for a backbeat. Then select Steel with SW2 + key 3 and enter a few
+off-beat or alternating steps. Press SW1 to stop when you need time to listen to a single
+voice; selecting a voice while stopped auditions it without requiring a sequencer step.
+
+A normal step action happens **on key release**, not on initial press. Holding the key
+for 400 ms has a different purpose: accent. This distinction lets the same sixteen keys
+edit a complete one-bar rhythm without sacrificing step visibility.
+
+## 2. The panel
+
+Key numbers in this manual are **logical steps 1–16**. The provisional adapter uses
+libDaisy keyboard indices 0–15 in order, with LEDs A1–A8 then B1–B8. Physical row/position
+assignment must be checked on the actual Field. Field Truth logs raw indices so that
+this mapping can be corrected in one place: `firmware/FieldAdapter.h`.
+
+### Eight knobs, one stable meaning
+
+| Control | Meaning | Behavior |
+|---|---|---|
+| Knob 1 | Tune | Selected drum's base frequency; logarithmic mapping |
+| Knob 2 | Decay | Selected drum's amplitude-decay time; logarithmic mapping |
+| Knob 3 | Character | Selected drum's tonal/noise/modulation balance |
+| Knob 4 | Level | Selected drum's linear level |
+| Knob 5 | Tempo | Internal tempo, 40.0–240.0 BPM in 0.1-BPM increments |
+| Knob 6 | Swing | Long/short sixteenth pairs, 50–70% long-step share |
+| Knob 7 | Drive | Global pre-saturation gain |
+| Knob 8 | Master | Global software output level |
+
+The display shows voice parameters as normalized percentages. It does not claim that
+50% Tune means 50 Hz, or that 50% Decay means half the maximum time. The musical ranges
+are in the voice chapter. Parameter targets normally ramp over five milliseconds;
+musically deliberate oscillator/noise content is not removed by that smoothing.
+
+### Two switches
+
+**SW1:** tap and release for Play/Stop. Holding SW1 alone does not create another menu.
+
+**SW2:** hold for Shift; release to return to Home. Shift is momentary, not a latched mode.
+The palette labels the available actions. Knobs retain their usual meanings.
+
+**Both switches:** hold together for 500 ms for Panic. The transport stops, Fill is
+released and voices enter a short release. Keep both released before using Play again.
+The chord cannot accidentally start transport when its switches are released in either order.
+
+### Shift palette
+
+| While holding SW2 | Action |
+|---|---|
+| Key 1 / 2 / 3 / 4 | Select Hammer / Crack / Steel / Arc; audition only while stopped |
+| Key 5 / 6 | Edit A / B and request it for playback |
+| Key 7 | Edit the Fill bank without forcing it into playback |
+| Hold key 8 | Use Fill for steps that occur while held |
+| Key 9 / 10 / 11 / 12 | Toggle the four voice mutes |
+| Hold key 13 for one second | Clear the selected track in the edited bank, stopped only |
+| Key 14 / 15 | Preview previous / next tutorial preset, stopped only |
+| Key 16 | Confirm the displayed tutorial load, stopped only |
+
+Release a Shift key and Shift in either order: it will not leak an ordinary step edit.
+For a Fill performance gesture, keep both SW2 and key 8 held. Releasing either ends
+the request. Clear and preset loading do not operate while transport is running.
+
+## 3. Reading Home
+
+The top line shows **RUN/STOP**, the edited bank and the last bank actually played.
+`A>B`, for example, means you are editing A while B supplied the last emitted step;
+the arrow is an edit/play relationship, not a copy command. The implementation's
+snapshot also exposes the queued A/B bank for development tools.
+
+The next line shows selected voice, current step number and a Fill-request indicator.
+Tune/Decay and Character/Level follow. A global line shows swing share, drive and master.
+The tutorial name or pending-load prompt is below them.
+
+`PICKUP` is a hexadecimal diagnostic mask: bit 0 is knob 1, bit 7 is knob 8.
+A set bit means that knob has not picked up its target. This compact display is
+functional but not a polished directional pickup guide. Move the relevant knob
+through its stored value; do not interpret the mask as an error counter.
+
+`MUTE` uses bits 0–3 for Hammer, Crack, Steel and Arc. `CPUmax` is the largest callback
+load measured by the firmware since initialization. `E` counts detected DSP faults.
+A zero error counter is not a substitute for a scope trace, nor is the displayed
+callback load a whole-system worst-case execution proof.
+
+Ordinary active steps are dimmer than accents. The current playing step is bright.
+In Shift, the first four LEDs indicate voice selection. The screen palette takes
+priority over the Home information; a preset preview remains visible in its bottom lines.
+
+## 4. Rhythm, accents and variations
+
+### Editing a step
+
+Tap to toggle a step in the **edited bank and selected voice only**. Deleting a step
+also deletes its accent. Hold for 400 ms to toggle accent once; this also creates the
+step if it was empty. Releasing after a hold does not undo the result.
+
+Unaccented sequencer hits use velocity 0.72. Accents use 1.0. This is a two-level
+dynamic system, not a full per-step velocity editor. MIDI hits can use all 127 nonzero
+velocity values. Velocity zero does not trigger a voice.
+
+### A and B
+
+A and B are separate sixteen-step banks containing all four tracks. They share the
+four current voice settings, tempo, swing, drive and mutes. Changing Tune on A also
+changes that voice when B plays. They are pattern variations, not independent kits.
+
+Selecting A or B immediately changes the editing bank. While running, the playback
+change occurs at the next bar's step 1. While stopped, bank selection takes effect
+immediately. Stop and Start restart the sequence at step 1 rather than continuing
+from the old position.
+
+There is no automatic A-to-B copy command in this candidate. Build a different bank
+manually or start from a tutorial containing both variations.
+
+### Fill
+
+Fill is a third bank. Key 7 in Shift selects it for editing, without switching the
+underlying A/B arrangement. Hold Shift + key 8 to borrow the Fill bank for each
+new step. The underlying sixteen-step phase continues. On release, the next step
+uses the active A/B bank at that same phase.
+
+This is **not** an automatic one-bar fill, a retrigger roll, or a parameter-lock system.
+Pressing Fill between steps does not immediately strike a drum. A short hold that
+contains no step boundary may produce no Fill hit. An A/B request still commits at
+the next bar boundary even while Fill is held.
+
+### Swing and tempo
+
+Swing delays every second sixteenth within a two-step pair while preserving the pair's
+nominal length. At 50% the steps are straight. At 70%, the first interval occupies
+70% of the pair and the second 30%. Swing changes commit at a pair boundary;
+tempo changes preserve the accumulated musical phase instead of restarting the clock.
+
+Sequencer timing is sample-domain timing relative to the device audio clock.
+It is not evidence that the physical oscillator has zero ppm error or that two
+unconnected instruments stay synchronized forever.
+
+## 5. The voices
+
+All four voices are synthesized. There are no samples to manage or load.
+
+| Voice | Tune range | Amplitude T60 range | Character |
+|---|---|---|---|
+| Hammer | 32–110 Hz | 60 ms–1.4 s | Strength of pitch punch and second harmonic |
+| Crack | 120–330 Hz | 40–800 ms | Body/noise balance and noise brightness |
+| Steel | 700–2300 Hz | 20–300 ms | Inharmonic metal/noise balance and brightness |
+| Arc | 55–900 Hz | 50 ms–2.4 s | Modulator ratio and phase-modulation depth |
+
+T60 means the exponential decay takes that duration to reach approximately 0.001 of
+its starting amplitude, or −60 dB in amplitude ratio. It does not mean the entire
+sound is cut off at that instant. The envelope reaches a very small tail threshold
+before it is set to exact zero.
+
+### Hammer: body and impact
+
+Hammer combines a sine body with a small second harmonic. A short pitch envelope
+gives the beginning of the hit more impact. Tune moves the body pitch; Decay controls
+the amplitude tail; Character increases the transient pitch excursion and harmonic bite.
+
+Start with low Drive. Raise Character while holding Tune steady, then shorten Decay.
+The impact should become more pronounced without turning the basic kick into a
+permanent oscillator. High Character is an exaggerated synthesized kick, not a
+model of a particular commercial drum circuit.
+
+### Crack: shell and snare noise
+
+Two inharmonic sine components form the body. High-pass-shaped deterministic noise
+provides the snare-like component. Low Character favors the pitched body; higher
+Character adds a brighter, noisier crack. There is no high-Q resonator that can enter
+self-oscillation.
+
+Audition Crack while stopped. Change Tune first to hear the body. Then change Character
+to find the point where the noise supplies enough edge. Avoid compensating a quiet
+Level by immediately maximizing global Drive; learn the voice's own balance first.
+
+### Steel: metallic rhythm
+
+Six bounded sine oscillators are combined in three pairs to create an inharmonic
+metallic spectrum, mixed with shaped noise. Short Decay gives a closed-hat role;
+longer Decay makes the metallic body easier to hear. This is one voice, with no
+separate open-hat lane or choke group.
+
+Try alternating steps, then accent every fourth hit. Increase swing before adding
+more notes. The tutorial exercises intentionally show that timing and dynamics can
+change the feel without requiring a denser pattern.
+
+### Arc: FM-style percussion
+
+Arc uses feed-forward sine phase modulation, a close relative of sinusoidal FM
+synthesis, with a moving modulation envelope. Character changes the modulator ratio
+and depth. Low values can be rounded or bell-like; larger values are more metallic.
+
+The carrier phase does not reset on a retrigger, and the modulation-envelope level
+ramps from its current state rather than jumping. That reduces one specific
+retrigger-click mechanism. It does not mean every extreme FM setting is spectrally
+clean. High-pitch/high-Character settings can alias; the candidate does not claim
+oversampled or alias-free synthesis.
+
+## 6. Drive, level and stopping
+
+Voice Level, mute gain, Drive and Master have different jobs. Level balances a drum;
+mute temporarily removes it; Drive increases gain before the bounded saturator;
+Master controls the final mix.
+
+The path includes a 10-Hz DC blocker and a bounded rational saturator. With valid
+internal state and Master no higher than 1, the final normalized samples are bounded
+below approximately ±0.736. That arithmetic is about digital samples, not volts at
+a jack, acoustic loudness or calibrated headroom.
+
+Mute, Stop and Panic use short gain/envelope transitions. The oscillator and envelope
+state is not abruptly zeroed while audible. A residual DC-filter tail may remain
+briefly after the voice release; tests check return to silence. Normal percussion,
+especially noise and high-frequency metallic voices, naturally has large adjacent
+sample differences. The transition test compares matched signals with and without
+a control event rather than treating every steep drum transient as a defect.
+
+There is no feedback delay, reverb freeze or resonant filter in this candidate.
+Unintended endless oscillation is not a musical feature. If a sound continues when
+it should stop, lower the external listening level, use Panic and save the telemetry
+and reproduction steps for investigation.
+
+## 7. Tutorial presets
+
+Tutorials are compiled original data, not extracted factory patterns. They load a
+complete set of three banks, four voice settings, tempo, swing and drive. Loading
+resets mutes and stops transport. **Master is retained** so a tutorial cannot silently
+replace your chosen listening level.
+
+To load: stop with SW1, hold SW2, tap key 14 or 15 to choose a tutorial, read its name,
+then tap key 16. Release SW2. The audio path fades down for about 5 ms, applies the
+new data at zero gain and ramps up again. Controls re-arm pickup. Selection is a
+preview until confirmed; the preview expires after five seconds.
+
+Loading a tutorial or clearing a track discards the affected unsaved edits.
+There is **no user-save function, undo or persistence across power cycles** yet.
+Write important edits in the patch notes sheet before loading something else.
+
+| Tutorial | Main lesson |
+|---|---|
+| Blank Canvas | Build the first sixteen-step pattern without hidden content |
+| First Pulse | Kick Tune, Decay and Character |
+| Backbeat | Balancing three complementary drum roles |
+| Broken Grid | Syncopation and A/B variation |
+| Alloy Swing | Metallic hats, accents and swing |
+| Arc Study | Tuned modulation-based percussion |
+| Night Shift | Four-voice balance and controlled drive |
+| Fracture Lab | Fill, mutes and more aggressive performance |
+
+[Preset lessons](PRESETS.md) contain the exact values, banks and step grids.
+The companion host WAVs demonstrate the programmed scenario, not recorded Field audio.
+
+## 8. MIDI and external connections
+
+Beat receives serial MIDI through the Field's initialized `MidiUartHandler`.
+It does not configure USB MIDI. Connect a known-good source through the board's
+documented MIDI input connection and verify it in Field Truth first.
+
+| Message | Response |
+|---|---|
+| Channel 10 Note On 36 | Hammer |
+| Channel 10 Note On 38 | Crack |
+| Channel 10 Note On 42 | Steel |
+| Channel 10 Note On 39 | Arc |
+| Nonzero note velocity | Trigger level |
+| Note On velocity 0 / Note Off | No new trigger; voices are one-shots |
+| MIDI Start | Start from step 1 when stopped; ignored when already running |
+| MIDI Stop / System Reset | Priority stop/panic and discard queued old commands |
+| Channel 10 CC 120 / 123 | Priority panic |
+| MIDI Clock / Continue | Ignored; not an external-clock follower |
+
+Do not expect a DAW or Hydrasynth clock to synchronize Beat just because MIDI Start
+works. There is no MIDI clock PLL, song-position handling or clock arbitration in this
+candidate. The fixed queue rejects excess ordinary commands and reports drops.
+Priority stop is separate from that queue.
+
+Beat ignores both audio inputs and leaves CV/Gate outputs commanded off. Its CV
+inputs and Gate In do not modulate or clock the instrument. Field Truth exercises
+these interfaces, but future musical integration requires measured mappings and
+an explicit control/clock specification.
+
+## 9. Field Truth bench program
+
+Field Truth is not a sound preset inside Beat. It is a separate diagnostic firmware.
+Its stereo callback passes input to output at unity digital gain. Start with a
+low-level calibrated source and disconnected CV/Gate cables. It is intentionally
+not limited, so that gain/clipping tests measure the actual path.
+
+Raw key press/release records use indices 0–15. Hold and release every physical key,
+record its label and LED correspondence, and compare the complete set. Knob and CV
+telemetry are normalized readings multiplied by 10,000. They are **not millivolts**.
+
+The output test is off until SW1 has first been observed released. After that,
+holding SW1 alone enables the test. Knobs 1 and 2 independently select DAC codes
+0, 1024, 2048, 3072 or 4095 in five zones; Gate Out is commanded active.
+Release SW1 to command zero/off. SW2 inhibits this output test.
+
+This is a software interlock, not a fail-safe circuit. A crashed program can leave
+an output asserted. Use an isolated bench fixture appropriate to the board and
+do not connect this output test to equipment with unverified input limits.
+Debounced switch handling also means release is not electrically instantaneous.
+
+Holding SW2 alone runs the BSP LED/OLED light test. MIDI reception is counted and
+the latest message metadata is reported. Gate In is sampled at the callback rate;
+narrow pulses can be missed. Timing is exposed through the Seed testpoint and
+callback counters. Check probe access and electrical loading before attaching
+a scope or logic analyzer to an assembled Field.
+
+The periodic `boot` record replays build/rate/board/memory/loader information.
+`reset=unknown` is deliberate. Receiving this line after USB enumeration does not
+prove a true cold boot, the reset source or absence of an earlier fault.
+
+## 10. Troubleshooting
+
+**No sound:** confirm which image is running. Truth needs an audio input. Beat needs
+an audition, supported MIDI hit or active steps plus Play. Check Master pickup,
+voice Level, mutes, edited versus playing bank, mixer connection and actual key mapping.
+
+**Knob appears stuck:** it probably has not picked up the stored target. Read the
+parameter percentage and sweep slowly through it. Preset loading and voice selection
+intentionally re-arm pickup.
+
+**Step changed differently from expected:** short release toggles the step; a
+400-ms hold changes accent. A Shift gesture is consumed even if Shift is released first.
+
+**Changing bank did not sound immediate:** A/B playback changes at the next bar.
+Editing changes immediately. Fill is borrowed per step, not a restart.
+
+**Preset controls do nothing:** Stop first. Key 14/15 preview; key 16 confirms.
+The program deliberately refuses a running load.
+
+**MIDI runs but drifts against another box:** external clock following is absent.
+Start/Stop handling does not establish tempo synchronization.
+
+**A click, dropout or runaway sound:** lower external volume, Panic, capture the build
+manifest, raw log, control sequence and audio recording. Inspect `over`, `late`, drops,
+faults and callback waveform. Do not diagnose solely from a successful host test.
+
+## 11. Patch notes
+
+Before discarding edits, record: tutorial, tempo, swing, drive, master, four normalized
+voice parameter sets, A/B/Fill note and accent grids, and mute state. Also record the
+binary hash and verified physical key mapping. A printed grid is a temporary backup
+method, not a claim that the firmware implements storage.
+
+## 12. Instructional sources
+
+The supplied Hydrasynth Explorer manual's Home/shortcut/macro explanations
+(pp. 14–18, 24, 66–67) informed direct access and clear distinctions between selection
+and action. Its cover says Version 2.0 despite the attachment's 2.2.0 filename.
+The supplied DFAM manual's signal-flow overview and progressive listening tour
+(pp. 7–12), envelope explanation (pp. 20–21) and trigger/advance distinction
+(pp. 22–23) informed the teaching order.
+
+HydraPulse's controls, algorithms, presets, words and diagrams are its own candidate
+design. It is not a Hydrasynth or DFAM emulation, and neither source manual is bundled.
diff --git a/field/HydraPulse/docs/VALIDATION.md b/field/HydraPulse/docs/VALIDATION.md
new file mode 100644
index 000000000..0c0a61a47
--- /dev/null
+++ b/field/HydraPulse/docs/VALIDATION.md
@@ -0,0 +1,131 @@
+# Historical supplied candidate validation
+
+This is the original 2026-09-07 authoring snapshot. Current integration/build/hardware
+results are in [INTEGRATION_2026-09-08.md](INTEGRATION_2026-09-08.md).
+
+Date: **2026-09-07**. Candidate: **HydraPulse Field 0.2.0**.
+Intended destination: `Denys/DaisyExamples/field/HydraPulse`.
+
+**Result:** implemented and host-tested source candidate. It is not yet an
+ARM-linked, ready-to-flash deliverable, a measured Field instrument, or a
+GitHub-published update.
+
+Runtime-source SHA-256:
+
+```text
+7aa39d796ad68faea522189185c2f451230aa92a77b5996aa73af942c78d41f8
+```
+
+This is a source-content fingerprint, **not a remote commit SHA**. Its exact
+file basis is preserved in `evidence/local/results.json`. The final package
+manifest additionally binds documentation, tests and all other payload files.
+
+## Executed checks
+
+| Check | Evidence class / result | Scope |
+|---|---|---|
+| GCC 14.2 Release configure/build/CTest | VERIFIED / 14 of 14 PASS | Current core, engine, controller, DSP and explicit adapter-contract targets |
+| GCC 14.2 Debug + UBSan | VERIFIED / 14 of 14 PASS | Current native executable tests |
+| Clang 17 Release | VERIFIED / 14 of 14 PASS | Second-compiler build and native tests |
+| Clang 17 Debug + ASan + UBSan | VERIFIED / 14 of 14 PASS | Fresh sanitizer build and execution, without disabling sandbox protections |
+| Python 3.13 standard-library tests | VERIFIED / 38 of 38 PASS | Positive and negative preset, metadata, telemetry, dependency, source-preservation and installer fixtures |
+| NaN helper negative test | VERIFIED / expected exit 1 | Actual CHECK_NEAR assertion observed, not a sanitizer startup failure |
+| Firmware adapter C++ checks | VERIFIED / PASS | Both real firmware translation units compiled with an explicitly fake BSP; NOT real libDaisy/ARM |
+| Application MIDI/callback policy | VERIFIED / PASS | Actual router/callback with fake transport: channel, velocity-zero, queue overflow, priority stop, Start, ignored Clock and CC123 |
+| Original R0 preservation | VERIFIED / 29 of 29 byte matches | Directly compared supplied ZIP bytes with references/r0 |
+| Factory data generation | VERIFIED / PASS | Checked-in header matches validated original JSON |
+| Host sonic scenarios | VERIFIED / 8 of 8 PASS | Ten-second 48 kHz stereo PCM renders; two runs identical; metrics and actual PCM gates |
+| GitHub workflow structure | VERIFIED / PASS | YAML parsed; exact dependency-ref, jobs, permissions and no-flash assertions only |
+| Documentation links | VERIFIED / PASS | Active local Markdown targets exist |
+| Manual PDF | VERIFIED / PASS | 18 pages; rendered layout and final panel/tutorial visual checks; page-text bounds |
+| Additive package/patch transfer | See delivery receipt | Final manifest, no-conflict application and unrelated-file preservation checked at packaging |
+
+These are fourteen distinct CTest executables, repeated across four build
+configurations; do not describe them as fifty-six distinct behavioral tests.
+The source history also includes ordinary four-test R0 results. Those historical
+results are not being substituted for this candidate's validation.
+
+## Repaired audible-transition failure
+
+The first matched-trajectory test failed on an active Arc retrigger:
+**1.42942 full-scale first-sample deviation** versus its untouched reference.
+After replacing the modulation-depth reset with a 1 ms rise from current state,
+the worst tested retrigger deviation is **0.0320582**.
+
+Smoothing the noise-filter coefficient reduced the tested parameter-event
+deviation from **0.210695** to **0.0263815**. `test_transitions` also verifies
+that repeated Stop/Release does not postpone silence.
+
+The before/after logs are retained. These are selected software regression
+measurements, not an assertion that every audible click or aliasing mechanism
+has been eliminated.
+
+## Host audio measurements
+
+Eight original tutorials were rendered twice using the actual C++ Engine.
+All WAVs are 48 kHz, stereo, 16-bit PCM, ten seconds long. Bank B is requested
+at 3 s, Fill held from 5 to 6 s, Panic at 7 s.
+
+Observed maximum peak over the scenarios: **0.300022 normalized**.
+All scenario fault counters are zero. All final-second PCM tails are exactly
+silent. Blank Canvas is intentionally silent. The metrics file records peak,
+RMS, DC, maximum adjacent-sample delta and tail level.
+
+The renderer is not Field audio capture. A high noisy-drum adjacent-sample
+delta is not itself evidence of a control click. No listening assessment,
+alias-free claim, physical SNR result, analog-voltage result or host/target
+numerical equivalence is inferred.
+
+## Local holds and unexecuted checks
+
+**Earlier GCC ASan route: HOLD.** Its binaries built, but the runtime rejected
+startup because the ASan library was not first in the library list. That run
+is not counted as PASS. The separately compiled Clang ASan+UBSan suite
+subsequently passed all fourteen tests in the unchanged host environment.
+The failure log remains visible rather than being erased.
+
+**Real ARM build: NOT_RUN.** Running `tools/build_target.py` exited nonzero at
+preflight because the `arm-none-eabi-*` tools are absent. The real dependency
+verifier separately rejected the missing local libDaisy checkout. No real
+target compiler or linker was executed. No firmware BIN/ELF/MAP is supplied.
+
+**Pinned libDaisy checkout: NOT_VERIFIED in this runtime.** Full commit and
+license-blob identities came from earlier repository inspection in this
+conversation. Current public API/build sources were inspected, but this does
+not replace compiling the exact pin. The private-checkout verifier and build
+script make that a hard Codex gate.
+
+**Mermaid parser/rendering: NOT_RUN.** Eight `.mmd`/Markdown pairs are checked
+for exact source consistency and were reviewed against the implementation.
+The actual CLI is absent. Do not label pair consistency as grammar/render PASS.
+
+**Formatter: NOT_RUN.** No clang-format executable was available. C++ warnings
+are treated as errors by the host build; that is not a formatting check.
+
+**GitHub publication and CI: NOT_RUN.** No remote changes were made. The live
+target fork, its current master HEAD and any extra private tools were not
+qualified through this run's public retrieval route. The old dedicated
+repository's historical CI result does not apply to this candidate.
+
+**Physical P0/P1: NOT_RUN.** No Field was attached. Key/LED physical mapping,
+boot/reset provenance, audio, CV/Gate levels, MIDI electrical reception and
+callback margin remain bench measurements. The candidate's output-arm logic
+is not hardware fail-safe protection.
+
+**DVPE: intentionally omitted.** No valid graph/schema/export round trip was
+executed. No guessed `.dvpe` or new DVPE registry block is delivered.
+
+## Evidence files and next gate
+
+Raw commands/output and tool versions are in `evidence/local`. Machine-readable
+results bind the runtime source list; the package manifest binds delivery bytes.
+The printable manual is a separate artifact generated from the active Markdown,
+not a copy of either supplied commercial manual.
+
+Use `CODEX_EXECUTION.md` for actual repository comparison, additive integration,
+exact dependency checkout, target compilation, Mermaid rendering, CI and
+non-destructive publication. Use `QA_PLAN.md` and the eleven NOT_RUN hardware
+record templates for the subsequent bench work.
+
+All review in this authoring context is non-independent. No formal release
+or independent assurance is claimed.
diff --git a/field/HydraPulse/docs/system/01_context.md b/field/HydraPulse/docs/system/01_context.md
new file mode 100644
index 000000000..ec4f8887a
--- /dev/null
+++ b/field/HydraPulse/docs/system/01_context.md
@@ -0,0 +1,32 @@
+# Context
+
+The instrument and diagnostic are different firmware images. Beat ignores audio inputs and keeps
+both DAC codes and Gate Out at zero/false. Truth provides the measurement path.
+No MIDI parser, display writer or logger has a reference to Engine in a concurrent context:
+the foreground sends value-only commands, and receives value-only snapshots.
+The single audio owner is the primary race-avoidance mechanism. The two SPSC queues have
+one producer and one consumer each, reject overflow and never overwrite unread slots.
+Source: firmware/HydraPulse.cpp, firmware/FieldTruth.cpp, src/app/Engine.*.
+
+```mermaid
+flowchart LR
+ Keys["Field: 16 keys + SW1/SW2"] --> Scan["ReadControls / 1 kHz"]
+ Knobs["Field: 8 knobs"] --> Scan
+ Scan --> Controller["Controller::Tick"]
+ Controller --> Engine["Engine / single audio owner"]
+ Midi["Field MIDI UART receiver"] --> Foreground["Foreground MIDI policy"]
+ Foreground --> Queue["SpscQueue: 31 commands"]
+ Queue --> Engine
+ Foreground --> Urgent["Atomic urgent stop flag"]
+ Urgent --> Engine
+ Engine --> Voices["Hammer / Crack / Steel / Arc"]
+ Voices --> Out["DC block / saturation / master ramp"]
+ Out --> Codec["Field stereo output"]
+ Engine --> Snap["SpscQueue: 7 snapshots"]
+ Snap --> Display["Foreground OLED + LEDs + serial"]
+ CV["CV and Gate ports"] -. "diagnostic target only" .-> Truth["FieldTruth"]
+ AudioIn["Stereo audio input"] --> Truth
+ Truth --> Passthrough["Unprocessed stereo passthrough"]
+ Passthrough --> Codec
+ Hydra["Hydrasynth-specific integration"] -. "deferred; no path in Beat" .-> Later["Later milestone"]
+```
diff --git a/field/HydraPulse/docs/system/01_context.mmd b/field/HydraPulse/docs/system/01_context.mmd
new file mode 100644
index 000000000..d95429e37
--- /dev/null
+++ b/field/HydraPulse/docs/system/01_context.mmd
@@ -0,0 +1,20 @@
+flowchart LR
+ Keys["Field: 16 keys + SW1/SW2"] --> Scan["ReadControls / 1 kHz"]
+ Knobs["Field: 8 knobs"] --> Scan
+ Scan --> Controller["Controller::Tick"]
+ Controller --> Engine["Engine / single audio owner"]
+ Midi["Field MIDI UART receiver"] --> Foreground["Foreground MIDI policy"]
+ Foreground --> Queue["SpscQueue: 31 commands"]
+ Queue --> Engine
+ Foreground --> Urgent["Atomic urgent stop flag"]
+ Urgent --> Engine
+ Engine --> Voices["Hammer / Crack / Steel / Arc"]
+ Voices --> Out["DC block / saturation / master ramp"]
+ Out --> Codec["Field stereo output"]
+ Engine --> Snap["SpscQueue: 7 snapshots"]
+ Snap --> Display["Foreground OLED + LEDs + serial"]
+ CV["CV and Gate ports"] -. "diagnostic target only" .-> Truth["FieldTruth"]
+ AudioIn["Stereo audio input"] --> Truth
+ Truth --> Passthrough["Unprocessed stereo passthrough"]
+ Passthrough --> Codec
+ Hydra["Hydrasynth-specific integration"] -. "deferred; no path in Beat" .-> Later["Later milestone"]
diff --git a/field/HydraPulse/docs/system/02_firmware_ownership.md b/field/HydraPulse/docs/system/02_firmware_ownership.md
new file mode 100644
index 000000000..4b0654053
--- /dev/null
+++ b/field/HydraPulse/docs/system/02_firmware_ownership.md
@@ -0,0 +1,37 @@
+# Firmware Ownership
+
+Expected firmware sample rate is 48,000 Hz, block length is 48 frames and scan rate is 1,000 Hz.
+These are configuration values, not measured physical clock accuracy. A snapshot is offered
+every 40 callbacks (25 Hz). The foreground consumes at most eight per iteration and displays
+only the last. Normal MIDI command consumption is at most eight per callback; urgent stop
+does not wait behind note traffic.
+CpuLoadMeter brackets callback work and uses System ticks, not Cortex cycle counts.
+The testpoint pulse includes the user callback body but excludes library interrupt entry,
+sample conversion outside the callback and exit overhead. Late-start and overrun counters
+are indicators, not a complete DMA underrun detector. Measure the instrumented interval
+and interrupt/codec behavior on the actual hardware before interpreting headroom.
+
+```mermaid
+sequenceDiagram
+ participant FG as Foreground
+ participant MIDI as MIDI driver
+ participant Q as Command queue
+ participant IRQ as Audio callback
+ participant UI as Controller
+ participant E as Engine
+ participant S as Snapshot queue
+ participant OLED as OLED and LED drivers
+ FG->>MIDI: Listen / pop at most 32 events per iteration
+ FG->>Q: Push supported command or count drop
+ IRQ->>IRQ: Testpoint high / meter begin
+ IRQ->>UI: Scan controls and Tick once
+ IRQ->>Q: Drain at most 8 normal commands
+ Note over IRQ,E: Urgent stop instead flushes at most 32 stale commands
+ IRQ->>E: Process each of 48 samples
+ E-->>IRQ: StereoFrame
+ IRQ->>S: Every 40 blocks: copy UiSnapshot
+ IRQ->>IRQ: Meter end / testpoint low
+ FG->>S: Drain at most 8 snapshots
+ FG->>OLED: Draw most recent snapshot
+ FG->>FG: Rate-limited serial telemetry
+```
diff --git a/field/HydraPulse/docs/system/02_firmware_ownership.mmd b/field/HydraPulse/docs/system/02_firmware_ownership.mmd
new file mode 100644
index 000000000..c12ea103a
--- /dev/null
+++ b/field/HydraPulse/docs/system/02_firmware_ownership.mmd
@@ -0,0 +1,22 @@
+sequenceDiagram
+ participant FG as Foreground
+ participant MIDI as MIDI driver
+ participant Q as Command queue
+ participant IRQ as Audio callback
+ participant UI as Controller
+ participant E as Engine
+ participant S as Snapshot queue
+ participant OLED as OLED and LED drivers
+ FG->>MIDI: Listen / pop at most 32 events per iteration
+ FG->>Q: Push supported command or count drop
+ IRQ->>IRQ: Testpoint high / meter begin
+ IRQ->>UI: Scan controls and Tick once
+ IRQ->>Q: Drain at most 8 normal commands
+ Note over IRQ,E: Urgent stop instead flushes at most 32 stale commands
+ IRQ->>E: Process each of 48 samples
+ E-->>IRQ: StereoFrame
+ IRQ->>S: Every 40 blocks: copy UiSnapshot
+ IRQ->>IRQ: Meter end / testpoint low
+ FG->>S: Drain at most 8 snapshots
+ FG->>OLED: Draw most recent snapshot
+ FG->>FG: Rate-limited serial telemetry
diff --git a/field/HydraPulse/docs/system/03_synthesis.md b/field/HydraPulse/docs/system/03_synthesis.md
new file mode 100644
index 000000000..fa8ac233f
--- /dev/null
+++ b/field/HydraPulse/docs/system/03_synthesis.md
@@ -0,0 +1,57 @@
+# Synthesis
+
+All four algorithms are original in this package, not ports of the manuals or unverified community DSP.
+The shared 1,025-entry sine table is initialized once before audio starts. Noise is xorshift32
+with per-voice deterministic seeds. No audio processing requests random bytes from a peripheral.
+Hammer uses a unipolar exponentially decaying pitch transient; Character scales its amount.
+Crack blends two sine modes with high-passed noise. Steel sums three products of six sine
+partials and adds shaped noise. Arc uses feed-forward phase modulation; its Character changes
+modulator ratio and index, not a feedback gain.
+
+The attack is 1 ms for Hammer/Crack/Arc and 0.5 ms for Steel. Decay is a T60-like parameter:
+coefficient = exp(-ln(1000)/(Fs*T60)). Release is a 5 ms linear ramp from the current envelope
+value to exact zero. Retrigger starts from the current amplitude and retains oscillator phase.
+The pitch/modulation transient has its own 1 ms attack from current state, avoiding an
+instantaneous phase-modulation jump on Arc retrigger. Frequency, Character, Level and the
+noise-filter coefficient use 5 ms ramps; changing a decay coefficient changes slope,
+not the current envelope value. The noise-filter coefficient is bounded between zero and one.
+The master is capped by the mathematical output path; this is not a true-peak/lookahead limiter.
+Harmonics from phase modulation, ring products and saturation still require aliasing/listening QA.
+No resonant filter or delay feedback loop is present in this candidate.
+
+```mermaid
+flowchart TB
+ Trig["Step or MIDI trigger / velocity"] --> Env["Finite attack-decay envelope"]
+ Params["Tune / Decay / Character / Level"] --> Smooth["Validated targets / 5 ms ramps"]
+ Smooth --> H
+ Smooth --> C
+ Smooth --> S
+ Smooth --> A
+ subgraph H["Hammer"]
+ HP["Exponential pitch transient"] --> HS["Sine + small second harmonic"]
+ end
+ subgraph C["Crack"]
+ CT["Two inharmonic sine modes"] --> CM["Body-noise mix"]
+ CN["Seeded noise / one-pole highpass"] --> CM
+ end
+ subgraph S["Steel"]
+ SO["Six sine oscillators"] --> SR["Three pairwise products"]
+ SN["Seeded highpass noise"] --> SM["Metal-noise mix"]
+ SR --> SM
+ end
+ subgraph A["Arc"]
+ AM["Sine modulator / bounded ratio"] --> AP["Decaying phase-modulation index"]
+ AP --> AC["Sine carrier"]
+ end
+ H --> Amp["Envelope x level x mute ramp"]
+ C --> Amp
+ S --> Amp
+ A --> Amp
+ Env --> Amp
+ Amp --> Pan["Fixed linear pan / stereo sum"]
+ Pan --> Gain["Drive gain"]
+ Gain --> DC["10 Hz DC blocker"]
+ DC --> Sat["Bounded feed-forward saturator"]
+ Sat --> Master["Master + preset fade"]
+ Master --> LR["StereoFrame"]
+```
diff --git a/field/HydraPulse/docs/system/03_synthesis.mmd b/field/HydraPulse/docs/system/03_synthesis.mmd
new file mode 100644
index 000000000..d4c379ce0
--- /dev/null
+++ b/field/HydraPulse/docs/system/03_synthesis.mmd
@@ -0,0 +1,34 @@
+flowchart TB
+ Trig["Step or MIDI trigger / velocity"] --> Env["Finite attack-decay envelope"]
+ Params["Tune / Decay / Character / Level"] --> Smooth["Validated targets / 5 ms ramps"]
+ Smooth --> H
+ Smooth --> C
+ Smooth --> S
+ Smooth --> A
+ subgraph H["Hammer"]
+ HP["Exponential pitch transient"] --> HS["Sine + small second harmonic"]
+ end
+ subgraph C["Crack"]
+ CT["Two inharmonic sine modes"] --> CM["Body-noise mix"]
+ CN["Seeded noise / one-pole highpass"] --> CM
+ end
+ subgraph S["Steel"]
+ SO["Six sine oscillators"] --> SR["Three pairwise products"]
+ SN["Seeded highpass noise"] --> SM["Metal-noise mix"]
+ SR --> SM
+ end
+ subgraph A["Arc"]
+ AM["Sine modulator / bounded ratio"] --> AP["Decaying phase-modulation index"]
+ AP --> AC["Sine carrier"]
+ end
+ H --> Amp["Envelope x level x mute ramp"]
+ C --> Amp
+ S --> Amp
+ A --> Amp
+ Env --> Amp
+ Amp --> Pan["Fixed linear pan / stereo sum"]
+ Pan --> Gain["Drive gain"]
+ Gain --> DC["10 Hz DC blocker"]
+ DC --> Sat["Bounded feed-forward saturator"]
+ Sat --> Master["Master + preset fade"]
+ Master --> LR["StereoFrame"]
diff --git a/field/HydraPulse/docs/system/04_clock_transport.md b/field/HydraPulse/docs/system/04_clock_transport.md
new file mode 100644
index 000000000..adf3fbc0a
--- /dev/null
+++ b/field/HydraPulse/docs/system/04_clock_transport.md
@@ -0,0 +1,31 @@
+# Clock Transport
+
+The step grid is always 16 steps. Internally indices are 0..15; the manual labels them 1..16.
+At Start, step zero emits on the next processed sample, then GrooveClock counts subsequent
+samples. At 120 BPM without swing, a boundary occurs every 6,000 samples at 48 kHz.
+For normalized swing w in [0,1], s = round(400*w). Phase increment is BPM_micro * 4 * 1000.
+Long threshold is Fs*60*1,000,000*(1000+s); short threshold substitutes (1000-s).
+Thus each constant-tempo swing pair retains exactly two nominal step durations before
+sample quantization. Remainder is retained, so error against the quantized-BPM rational
+reference stays below one sample. The display reports 50..70 percent swing.
+This is a sample-domain property, not a claim about crystal tolerance or MIDI jitter.
+
+An A/B request changes the edit bank immediately, and changes the playback bank at the
+next step-zero boundary. A request while stopped sets the bank immediately.
+Fill substitutes its pattern at the next step boundary while held and leaves transport
+phase/step number unchanged. Release returns to the active A/B bank at the next boundary.
+It is not an automatic one-bar Fill latch. Stop releases sounds; Start restarts the grid.
+Incoming MIDI TimingClock and Continue are intentionally ignored, not partly implemented.
+
+```mermaid
+flowchart LR
+ S["Stopped"] -->|"Start, no preset transaction"| P["Start pending"]
+ P -->|"Next sample: emit step zero"| L["Long step"]
+ L -->|"Long threshold crossed"| H["Short step"]
+ H -->|"Short threshold crossed, latch swing"| L
+ P -->|"Stop before first sample"| S
+ L -->|"Stop / Panic"| S
+ H -->|"Stop / Panic"| S
+ S -->|"Audition voice"| S
+ N["Tempo preserves phase.
A/B commits at step zero.
Fill selects the next emitted step bank."] -.-> L
+```
diff --git a/field/HydraPulse/docs/system/04_clock_transport.mmd b/field/HydraPulse/docs/system/04_clock_transport.mmd
new file mode 100644
index 000000000..a8067c6f1
--- /dev/null
+++ b/field/HydraPulse/docs/system/04_clock_transport.mmd
@@ -0,0 +1,10 @@
+flowchart LR
+ S["Stopped"] -->|"Start, no preset transaction"| P["Start pending"]
+ P -->|"Next sample: emit step zero"| L["Long step"]
+ L -->|"Long threshold crossed"| H["Short step"]
+ H -->|"Short threshold crossed, latch swing"| L
+ P -->|"Stop before first sample"| S
+ L -->|"Stop / Panic"| S
+ H -->|"Stop / Panic"| S
+ S -->|"Audition voice"| S
+ N["Tempo preserves phase.
A/B commits at step zero.
Fill selects the next emitted step bank."] -.-> L
diff --git a/field/HydraPulse/docs/system/05_controls_menu.md b/field/HydraPulse/docs/system/05_controls_menu.md
new file mode 100644
index 000000000..c28cfa316
--- /dev/null
+++ b/field/HydraPulse/docs/system/05_controls_menu.md
@@ -0,0 +1,45 @@
+# Controls Menu
+
+There is no menu stack, machine browser, patch hierarchy or desktop editor requirement.
+The Shift palette is a temporary action legend. Preset preview is a five-second pending
+selection shown on Home, not a browsing page; it never changes sound until confirmed.
+SW1 is a tap-release transport action. Holding both switches cancels any pending transport
+tap, so releasing the panic chord cannot accidentally restart playback.
+
+Knob mapping: 1 Tune, 2 Decay, 3 Character, 4 Level, 5 Tempo (40..240 BPM),
+6 Swing (50..70 percent), 7 Drive, 8 Master. All use pickup; sound pickups re-arm on
+voice selection, all pickups re-arm after preset load. A pickup bit of one means not captured.
+Knob values and target values shown in the manual are normalized percentages, not voltages.
+
+Shift+13 must be held for one second while stopped to clear the selected track in the edit bank.
+Preset load and clear are unavailable during playback. Ordinary taps toggle, rather than
+cycling through velocity states. Long step hold toggles its accent and ensures the step exists.
+Keys consumed by Shift cannot become ordinary step edits when the modifier is released first.
+Physical labels remain provisional until P0 validates FieldAdapter.h mapping on the actual unit.
+
+```mermaid
+flowchart TD
+ Input["1 kHz stable controls"] --> Panic{"Both switches held 500 ms?"}
+ Panic -->|"yes"| Latch["Panic once / suppress until both released"]
+ Panic -->|"no"| Normal["Normal gesture processing"]
+ Normal --> Display{"SW2 held?"}
+ Display -->|"yes"| Shift["Show momentary Shift palette"]
+ Display -->|"no"| Home["Show Home / release actions"]
+ Shift --> Direct["1-4 voice / 5-7 banks / 8 held Fill / 9-12 mute"]
+ Shift --> Clear["13 held 1 s / clear only while stopped"]
+ Shift --> Select["14-15 / stopped-only tutorial preview"]
+ Select --> Preview["Pending preset flag / five-second timeout"]
+ Preview --> Confirm{"16 with Shift / stopped?"}
+ Confirm -->|"yes"| Loading["Engine fades / applies preset at zero gain"]
+ Confirm -->|"no or timeout"| Retain["No preset change"]
+ Loading --> Revision["Next control tick sees revision / re-arm pickups"]
+ Home --> Step["Tap-release toggles / 400 ms hold accents"]
+ Normal --> Knobs["Eight fixed-meaning pickup knobs"]
+ Preview -. "name visible on either display" .-> Shift
+ Preview -. "name visible on either display" .-> Home
+ Revision -. "does not latch a menu" .-> Display
+```
+
+Preset preview/loading are independent flags, not persistent menu pages. Releasing SW2
+returns the display to Home even if a preview is still pending; loading while SW2 is held
+does not force the palette closed. The next control tick re-arms pickup after the engine revision changes.
diff --git a/field/HydraPulse/docs/system/05_controls_menu.mmd b/field/HydraPulse/docs/system/05_controls_menu.mmd
new file mode 100644
index 000000000..cdc6f1196
--- /dev/null
+++ b/field/HydraPulse/docs/system/05_controls_menu.mmd
@@ -0,0 +1,20 @@
+flowchart TD
+ Input["1 kHz stable controls"] --> Panic{"Both switches held 500 ms?"}
+ Panic -->|"yes"| Latch["Panic once / suppress until both released"]
+ Panic -->|"no"| Normal["Normal gesture processing"]
+ Normal --> Display{"SW2 held?"}
+ Display -->|"yes"| Shift["Show momentary Shift palette"]
+ Display -->|"no"| Home["Show Home / release actions"]
+ Shift --> Direct["1-4 voice / 5-7 banks / 8 held Fill / 9-12 mute"]
+ Shift --> Clear["13 held 1 s / clear only while stopped"]
+ Shift --> Select["14-15 / stopped-only tutorial preview"]
+ Select --> Preview["Pending preset flag / five-second timeout"]
+ Preview --> Confirm{"16 with Shift / stopped?"}
+ Confirm -->|"yes"| Loading["Engine fades / applies preset at zero gain"]
+ Confirm -->|"no or timeout"| Retain["No preset change"]
+ Loading --> Revision["Next control tick sees revision / re-arm pickups"]
+ Home --> Step["Tap-release toggles / 400 ms hold accents"]
+ Normal --> Knobs["Eight fixed-meaning pickup knobs"]
+ Preview -. "name visible on either display" .-> Shift
+ Preview -. "name visible on either display" .-> Home
+ Revision -. "does not latch a menu" .-> Display
diff --git a/field/HydraPulse/docs/system/06_field_hardware.md b/field/HydraPulse/docs/system/06_field_hardware.md
new file mode 100644
index 000000000..01f4ed496
--- /dev/null
+++ b/field/HydraPulse/docs/system/06_field_hardware.md
@@ -0,0 +1,37 @@
+# Field Hardware
+
+The inspected upstream daisy_field.cpp uses the Field BSP, not an invented per-pin HAL.
+Its Rev2 constants associate Gate In with Seed D0, Gate Out with D15, pot mux ADC with D16,
+CV inputs with D17/D18/D25/D24, DAC outputs with D23/D22, the two switches with D30/D29,
+and the keyboard shift-register signals with D26/D27/D28. These are source observations,
+not a substitute for the schematic and board-revision check. Do not wire directly to those
+pins from this document. Use the stock Field connectors and qualified fixture.
+
+The inspected Seed implementation maps the testpoint to PG14 and the Seed LED to PC7.
+Testpoint accessibility and probe grounding on the assembled Field are not verified.
+CV inputs are reported in BSP normalized bipolar units; do not interpret 10,000 telemetry
+units as a fixed number of volts without calibration. DAC codes are 0,1024,2048,3072,4095.
+Voltage, polarity, loading, limits and ground reference remain bench-measured fields.
+Gate sampling at 1 kHz cannot prove detection of shorter-than-scan pulses.
+The musical image holds CV/Gate outputs off and has no external audio-through lane.
+Sources: source ledger entries DAISY-FIELD, DAISY-SEED and DAISY-SYSTEM.
+
+```mermaid
+flowchart LR
+ CodecIn["Stereo codec input"] --> Truth["Truth passthrough"]
+ Beat["Beat stereo renderer"] --> CodecOut["Stereo codec output"]
+ Truth --> CodecOut
+ Pots["8 pots / mux"] --> ADC["DaisyField analog controls"]
+ CV["CV1..4"] --> ADC
+ ADC --> Snap["Diagnostic snapshot"]
+ SR["16-key shift register"] --> HID["DaisyField keyboard scan"]
+ Switches["SW1 / SW2"] --> HID
+ HID --> UI["Pure Controller"]
+ GateIn["Gate In / BSP State"] --> GC["1 kHz sampled edge counter"]
+ SW1["Release-to-arm SW1 hold"] --> Safe["OutputTestArm"]
+ Safe --> DAC["DAC1 / DAC2 codes"]
+ Safe --> GateOut["Gate Out boolean"]
+ TP["Seed testpoint API"] --> Scope["Scope callback pulse"]
+ UART["BSP MIDI UART"] --> MIDI["Foreground MIDI event policy"]
+ Snap --> Foreground["OLED / LED / telemetry"]
+```
diff --git a/field/HydraPulse/docs/system/06_field_hardware.mmd b/field/HydraPulse/docs/system/06_field_hardware.mmd
new file mode 100644
index 000000000..b3802a5e0
--- /dev/null
+++ b/field/HydraPulse/docs/system/06_field_hardware.mmd
@@ -0,0 +1,17 @@
+flowchart LR
+ CodecIn["Stereo codec input"] --> Truth["Truth passthrough"]
+ Beat["Beat stereo renderer"] --> CodecOut["Stereo codec output"]
+ Truth --> CodecOut
+ Pots["8 pots / mux"] --> ADC["DaisyField analog controls"]
+ CV["CV1..4"] --> ADC
+ ADC --> Snap["Diagnostic snapshot"]
+ SR["16-key shift register"] --> HID["DaisyField keyboard scan"]
+ Switches["SW1 / SW2"] --> HID
+ HID --> UI["Pure Controller"]
+ GateIn["Gate In / BSP State"] --> GC["1 kHz sampled edge counter"]
+ SW1["Release-to-arm SW1 hold"] --> Safe["OutputTestArm"]
+ Safe --> DAC["DAC1 / DAC2 codes"]
+ Safe --> GateOut["Gate Out boolean"]
+ TP["Seed testpoint API"] --> Scope["Scope callback pulse"]
+ UART["BSP MIDI UART"] --> MIDI["Foreground MIDI event policy"]
+ Snap --> Foreground["OLED / LED / telemetry"]
diff --git a/field/HydraPulse/docs/system/07_preset_transaction.md b/field/HydraPulse/docs/system/07_preset_transaction.md
new file mode 100644
index 000000000..97f70e2b5
--- /dev/null
+++ b/field/HydraPulse/docs/system/07_preset_transaction.md
@@ -0,0 +1,33 @@
+# Preset Transaction
+
+Presets are generated from presets/tutorials.json by tools/generate_presets.py.
+The JSON is the editable source; the checked-in C++ table is a build asset whose exact
+content is checked. There are eight presets with three banks and four voices each.
+The preset contains rhythm masks, accents, sound parameters, BPM, swing and drive.
+It does not contain master volume, which remains under the user's control.
+
+There is no persistent save in this version. Edits live in RAM and are lost at power-down,
+reset or confirmed preset load. No blank "save" menu implies otherwise.
+The renderer exercises A/B, Fill and panic within each tutorial's ten-second host scenario.
+Later storage requires a versioned schema, CRC, bounds checks, two-slot atomic commit,
+power-interruption tests and a non-audio owner for flash operations.
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant UI as Controller
+ participant E as Engine
+ participant DSP as Voice and mix state
+ User->>UI: Shift + previous/next while stopped
+ UI->>UI: Preview only / five-second timeout
+ User->>UI: Shift + load
+ UI->>E: RequestPreset(id)
+ E->>E: Reject when running, busy or id invalid
+ E->>DSP: Ramp load gain to zero over 5 ms
+ E->>DSP: At zero: reset voices and DC state
+ E->>E: Copy A/B/Fill and parameter table
+ E->>DSP: Ramp load gain back to one
+ E-->>UI: PresetRevision increments
+ UI->>UI: Re-arm pickups against current physical knobs
+ Note over E,DSP: Master is retained, transport remains stopped
+```
diff --git a/field/HydraPulse/docs/system/07_preset_transaction.mmd b/field/HydraPulse/docs/system/07_preset_transaction.mmd
new file mode 100644
index 000000000..2939bf2f9
--- /dev/null
+++ b/field/HydraPulse/docs/system/07_preset_transaction.mmd
@@ -0,0 +1,17 @@
+sequenceDiagram
+ participant User
+ participant UI as Controller
+ participant E as Engine
+ participant DSP as Voice and mix state
+ User->>UI: Shift + previous/next while stopped
+ UI->>UI: Preview only / five-second timeout
+ User->>UI: Shift + load
+ UI->>E: RequestPreset(id)
+ E->>E: Reject when running, busy or id invalid
+ E->>DSP: Ramp load gain to zero over 5 ms
+ E->>DSP: At zero: reset voices and DC state
+ E->>E: Copy A/B/Fill and parameter table
+ E->>DSP: Ramp load gain back to one
+ E-->>UI: PresetRevision increments
+ UI->>UI: Re-arm pickups against current physical knobs
+ Note over E,DSP: Master is retained, transport remains stopped
diff --git a/field/HydraPulse/docs/system/08_verification.md b/field/HydraPulse/docs/system/08_verification.md
new file mode 100644
index 000000000..d33e9afd8
--- /dev/null
+++ b/field/HydraPulse/docs/system/08_verification.md
@@ -0,0 +1,33 @@
+# Verification
+
+A PASS belongs to a specific executed test on a specific artifact. Host tests cover software
+contracts, not ARM timing, codec gain, analog voltage or instrument ergonomics. The contract
+stub compiles source against locally declared API shapes and must never be reported as
+libDaisy or ARM validation. The target script refuses to substitute a native compiler.
+
+Hardware records use VERIFIED, DERIVED, PROPOSED, ASSUMED, HOLD or NOT_RUN.
+A VERIFIED hardware row requires fixture identity, firmware hash, raw artifact paths and
+measurement method. Simulated waveforms cannot occupy measured-hardware fields.
+Public CI never runs DFU/OpenOCD/programming commands. Missing toolchain or missing
+physical hardware holds those gates without erasing completed source work.
+
+```mermaid
+flowchart TD
+ Src["Candidate source + original presets"] --> Host["Native CMake / CTest"]
+ Src --> Python["Schema / source / preservation validators"]
+ Host --> San["ASan + UBSan / negative tests"]
+ Host --> WAV["Deterministic host WAV scenarios"]
+ Src --> Contract["Host adapter stub syntax check"]
+ Contract -. "does not imply ARM success" .-> ARM["Real ARM + libDaisy link"]
+ Pins["Exact dependency checkout and toolchain"] --> ARM
+ ARM --> Binary["ELF + BIN + MAP + hashes + size gate"]
+ Binary --> Flash["Explicit manual flash"]
+ Flash --> P0["Measured Field Truth fixture"]
+ P0 --> P1["Measured Beat and tutorial acceptance"]
+ WAV --> Compare["Host / target numerical and audio comparison"]
+ P1 --> Compare
+ Compare --> Release["Candidate acceptance decision"]
+ Remote["Authenticated target repo inspection"] --> Merge["Additive integration / no sibling changes"]
+ Merge --> CI["CI on exact commit"]
+ CI --> Release
+```
diff --git a/field/HydraPulse/docs/system/08_verification.mmd b/field/HydraPulse/docs/system/08_verification.mmd
new file mode 100644
index 000000000..926f6b2bf
--- /dev/null
+++ b/field/HydraPulse/docs/system/08_verification.mmd
@@ -0,0 +1,18 @@
+flowchart TD
+ Src["Candidate source + original presets"] --> Host["Native CMake / CTest"]
+ Src --> Python["Schema / source / preservation validators"]
+ Host --> San["ASan + UBSan / negative tests"]
+ Host --> WAV["Deterministic host WAV scenarios"]
+ Src --> Contract["Host adapter stub syntax check"]
+ Contract -. "does not imply ARM success" .-> ARM["Real ARM + libDaisy link"]
+ Pins["Exact dependency checkout and toolchain"] --> ARM
+ ARM --> Binary["ELF + BIN + MAP + hashes + size gate"]
+ Binary --> Flash["Explicit manual flash"]
+ Flash --> P0["Measured Field Truth fixture"]
+ P0 --> P1["Measured Beat and tutorial acceptance"]
+ WAV --> Compare["Host / target numerical and audio comparison"]
+ P1 --> Compare
+ Compare --> Release["Candidate acceptance decision"]
+ Remote["Authenticated target repo inspection"] --> Merge["Additive integration / no sibling changes"]
+ Merge --> CI["CI on exact commit"]
+ CI --> Release
diff --git a/field/HydraPulse/docs/system/README.md b/field/HydraPulse/docs/system/README.md
new file mode 100644
index 000000000..5687f1a2a
--- /dev/null
+++ b/field/HydraPulse/docs/system/README.md
@@ -0,0 +1,16 @@
+# System reference
+
+Version 0.2.0-dev. Each Markdown chapter includes its matching editable `.mmd` source.
+
+- [Context](01_context.md)
+- [Firmware Ownership](02_firmware_ownership.md)
+- [Synthesis](03_synthesis.md)
+- [Clock Transport](04_clock_transport.md)
+- [Controls Menu](05_controls_menu.md)
+- [Field Hardware](06_field_hardware.md)
+- [Preset Transaction](07_preset_transaction.md)
+- [Verification](08_verification.md)
+
+These diagrams describe the delivered candidate. Dashed deferred paths are not implemented.
+Use `tools/validate_repo.py` to check source/fence parity. Real Mermaid rendering is a separate
+Codex check: run an installed `mmdc` against each `.mmd` file, preserving its version and errors.
diff --git a/field/HydraPulse/evidence/2026-09-08-fieldtruth-flash.log b/field/HydraPulse/evidence/2026-09-08-fieldtruth-flash.log
new file mode 100644
index 000000000..1171c5b81
--- /dev/null
+++ b/field/HydraPulse/evidence/2026-09-08-fieldtruth-flash.log
@@ -0,0 +1,29 @@
+xPack Open On-Chip Debugger 0.12.0-01004-g9ea7f3d64-dirty (2023-01-30-15:04)
+Licensed under GNU GPL v2
+For bug reports, read
+ http://openocd.org/doc/doxygen/bugs.html
+Info : auto-selecting first available session transport "hla_swd". To override use 'transport select '.
+Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
+Info : clock speed 1800 kHz
+Info : STLINK V3J7M2 (API v3) VID:PID 0483:374E
+Info : Target voltage: 3.243973
+Info : [stm32h7x.cpu0] Cortex-M7 r1p1 processor detected
+Info : [stm32h7x.cpu0] target has 8 breakpoints, 4 watchpoints
+Info : starting gdb server for stm32h7x.cpu0 on 3333
+Info : Listening on port 3333 for gdb connections
+[stm32h7x.cpu0] halted due to debug-request, current mode: Thread
+xPSR: 0x01000000 pc: 0x08000a24 msp: 0x20020000
+Info : Unable to match requested speed 4000 kHz, using 3300 kHz
+Info : Unable to match requested speed 4000 kHz, using 3300 kHz
+** Programming Started **
+Info : Device: STM32H74x/75x
+Info : flash size probed value 128k
+Info : STM32H7 flash has a single bank
+Info : Bank (0) size is 128 kb, base address is 0x08000000
+Info : Padding image section 1 at 0x0801a310 with 16 bytes (bank write end alignment)
+Warn : Adding extra erase range, 0x0801a320 .. 0x0801ffff
+** Programming Finished **
+** Verify Started **
+** Verified OK **
+** Resetting Target **
+shutdown command invoked
diff --git a/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial-recheck-summary.json b/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial-recheck-summary.json
new file mode 100644
index 000000000..fcddb1991
--- /dev/null
+++ b/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial-recheck-summary.json
@@ -0,0 +1,49 @@
+{
+ "classification": "DERIVED",
+ "basis": "captured firmware telemetry only",
+ "records": {
+ "truth": 119,
+ "outputs": 119,
+ "timing": 134,
+ "knobs": 238,
+ "cv": 119,
+ "midi": 119,
+ "boot": 15,
+ "system": 15
+ },
+ "invalid_records": 0,
+ "pressed_raw_indices": [],
+ "maximum_reported_counters": {
+ "over": 0,
+ "late": 0,
+ "drops": 3058,
+ "load_pm": 28
+ },
+ "not_proven": [
+ "cold boot",
+ "voltage",
+ "audio quality",
+ "absolute CPU timing",
+ "P0 completion"
+ ],
+ "capture_first_timing": {
+ "cb": 263959,
+ "load_pm": 28,
+ "over": 0,
+ "late": 0,
+ "drops": 3058
+ },
+ "capture_last_timing": {
+ "cb": 293519,
+ "load_pm": 28,
+ "over": 0,
+ "late": 0,
+ "drops": 3058
+ },
+ "capture_counter_deltas": {
+ "cb": 29560,
+ "over": 0,
+ "late": 0,
+ "drops": 0
+ }
+}
diff --git a/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial-recheck.log b/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial-recheck.log
new file mode 100644
index 000000000..871af4f69
--- /dev/null
+++ b/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial-recheck.log
@@ -0,0 +1,878 @@
+[HPF] truth cb=263959 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=263959 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-124 c3=-147 c4=-130 peakL=6979 peakR=6974
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=264239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=264239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7332 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-122 c3=-148 c4=-131 peakL=7008 peakR=7002
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=264479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=264479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-122 c3=-148 c4=-130 peakL=7024 peakR=7018
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=264719 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=264719 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8395 k8=3641
+[HPF] cv c1=-136 c2=-123 c3=-147 c4=-131 peakL=6996 peakR=6990
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=264959 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=264959 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8395 k8=3641
+[HPF] cv c1=-137 c2=-123 c3=-147 c4=-130 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=265239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=265239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-122 c3=-146 c4=-130 peakL=7022 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=265479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=265479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5531
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-147 c4=-131 peakL=7011 peakR=7004
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=265719 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=265719 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4806 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-130 peakL=7017 peakR=7011
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=265959 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=265959 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7020 peakR=7014
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=266239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=266239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7332 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=266479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=266479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-132 peakL=7001 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=266719 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=266719 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7332 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-130 peakL=7022 peakR=7017
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=266959 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=266959 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-147 c4=-131 peakL=7015 peakR=7008
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=267239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=267239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8395 k8=3641
+[HPF] cv c1=-135 c2=-124 c3=-147 c4=-131 peakL=6990 peakR=6984
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=267479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=267479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7332 k4=5529
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-123 c3=-148 c4=-130 peakL=7024 peakR=7018
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=267719 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=267719 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-134 c2=-122 c3=-148 c4=-130 peakL=7002 peakR=6997
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=267959 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=267959 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5526
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-147 c4=-131 peakL=7026 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=268239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=268239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4962 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-122 c3=-148 c4=-131 peakL=7020 peakR=7014
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=268479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=268479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7332 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-134 c2=-122 c3=-147 c4=-131 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=268719 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=268719 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-137 c2=-121 c3=-147 c4=-130 peakL=6978 peakR=6974
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=268999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=268999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7332 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-122 c3=-147 c4=-131 peakL=7018 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=269239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=269239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4962 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-134 c2=-123 c3=-148 c4=-130 peakL=7026 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=269479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=269479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-123 c3=-147 c4=-132 peakL=7008 peakR=7002
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=269719 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=269719 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7027 peakR=7021
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=269999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=269999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8395 k8=3641
+[HPF] cv c1=-137 c2=-122 c3=-147 c4=-131 peakL=7024 peakR=7018
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=270239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=270239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-123 c3=-147 c4=-130 peakL=7008 peakR=7003
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=270479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=270479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-124 c3=-147 c4=-132 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=270719 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=270719 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4806 k3=7332 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-122 c3=-147 c4=-131 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=270999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=270999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7021 peakR=7015
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=271239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=271239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=7013 peakR=7008
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=271479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=271479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8395 k8=3641
+[HPF] cv c1=-137 c2=-122 c3=-148 c4=-131 peakL=7022 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=271719 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=271719 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-123 c3=-147 c4=-130 peakL=7017 peakR=7011
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=271999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=271999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7332 k4=5530
+[HPF] knobs first=5 k5=4962 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7011 peakR=7005
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=272239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=272239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-124 c3=-149 c4=-131 peakL=7025 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=272479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=272479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-134 c2=-124 c3=-147 c4=-132 peakL=7006 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=272719 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=272719 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-149 c4=-130 peakL=7026 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=272999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=272999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-147 c4=-130 peakL=7019 peakR=7014
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=273239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=273239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-122 c3=-148 c4=-132 peakL=7028 peakR=7022
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=273479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=273479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7332 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=6976 peakR=6972
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=273719 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=273719 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-137 c2=-123 c3=-147 c4=-132 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=273999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=273999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-125 c3=-148 c4=-130 peakL=7021 peakR=7015
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=274239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=274239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4806 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-122 c3=-148 c4=-131 peakL=7008 peakR=7002
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=274479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=274479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6739 k2=4806 k3=7332 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-130 peakL=7018 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=274759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=274759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-137 c2=-122 c3=-147 c4=-130 peakL=7027 peakR=7021
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=274999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=274999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4962 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-130 peakL=7012 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=275239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=275239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-122 c3=-148 c4=-131 peakL=7017 peakR=7011
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=275479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=275479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-149 c4=-131 peakL=7015 peakR=7009
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=275759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=275759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-147 c4=-130 peakL=7023 peakR=7018
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=275999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=275999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-134 c2=-124 c3=-148 c4=-131 peakL=7005 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=276239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=276239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-149 c4=-131 peakL=7016 peakR=7011
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=276479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=276479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-132 peakL=7009 peakR=7003
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=276759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=276759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-125 c3=-148 c4=-133 peakL=7001 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=276999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=276999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5532
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-132 peakL=7024 peakR=7018
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=277239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=277239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-130 peakL=7003 peakR=6997
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=277479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=277479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-123 c3=-147 c4=-130 peakL=7025 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=277759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=277759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-137 c2=-124 c3=-147 c4=-131 peakL=7023 peakR=7017
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=277999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=277999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7332 k4=5531
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-122 c3=-149 c4=-131 peakL=7023 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=278239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=278239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4962 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-122 c3=-147 c4=-130 peakL=6975 peakR=6970
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=278479 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=278479 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-130 peakL=7023 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=278759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=278759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-149 c4=-132 peakL=7016 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=278999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=278999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-134 c2=-122 c3=-147 c4=-132 peakL=7004 peakR=6997
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=279239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=279239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4962 k6=4668 k7=8395 k8=3641
+[HPF] cv c1=-136 c2=-125 c3=-148 c4=-133 peakL=7026 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=279519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=279519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4962 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-123 c3=-150 c4=-132 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=279759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=279759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-125 c3=-149 c4=-131 peakL=7012 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=279999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=279999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-132 peakL=7017 peakR=7011
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=280239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=280239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8395 k8=3641
+[HPF] cv c1=-136 c2=-125 c3=-149 c4=-131 peakL=7023 peakR=7017
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=280519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=280519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4962 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-125 c3=-149 c4=-132 peakL=7026 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=280759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=280759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-124 c3=-147 c4=-130 peakL=7016 peakR=7009
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=280999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=280999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-131 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=281239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=281239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-132 peakL=7003 peakR=6998
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=281519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=281519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-123 c3=-147 c4=-131 peakL=7006 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=281759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=281759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-125 c3=-148 c4=-131 peakL=7028 peakR=7022
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=281999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=281999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7000 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=282239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=282239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=282519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=282519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-134 c2=-124 c3=-149 c4=-131 peakL=7019 peakR=7014
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=282759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=282759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-132 peakL=7018 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=282999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=282999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-130 peakL=6979 peakR=6975
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=283239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=283239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=7021 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=283519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=283519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=7026 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=283759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=283759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-137 c2=-123 c3=-148 c4=-130 peakL=7010 peakR=7004
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=283999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=283999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-134 c2=-124 c3=-148 c4=-130 peakL=7021 peakR=7015
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=284239 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=284239 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-124 c3=-149 c4=-132 peakL=7011 peakR=7005
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=284519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=284519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6739 k2=4808 k3=7332 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-147 c4=-130 peakL=7007 peakR=7001
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=284759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=284759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8395 k8=3642
+[HPF] cv c1=-137 c2=-123 c3=-150 c4=-132 peakL=7022 peakR=7015
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=284999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=284999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-149 c4=-130 peakL=7022 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=285279 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=285279 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-131 peakL=7023 peakR=7018
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=285519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=285519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8395 k8=3641
+[HPF] cv c1=-136 c2=-124 c3=-146 c4=-131 peakL=7006 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=285759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=285759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-123 c3=-148 c4=-132 peakL=7022 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=285999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=285999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-132 peakL=7010 peakR=7004
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=286279 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=286279 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-125 c3=-148 c4=-131 peakL=7013 peakR=7007
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=286519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=286519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-131 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=286759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=286759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-125 c3=-151 c4=-132 peakL=7012 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=286999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=286999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-124 c3=-149 c4=-130 peakL=7026 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=287279 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=287279 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=287519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=287519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4960 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-130 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=287759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=287759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4962 k6=4668 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-147 c4=-131 peakL=6978 peakR=6974
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=287999 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=287999 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-130 peakL=7027 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=288279 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=288279 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-124 c3=-149 c4=-131 peakL=7026 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=288519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=288519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-125 c3=-148 c4=-132 peakL=7001 peakR=6996
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=288759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=288759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-138 c2=-123 c3=-148 c4=-132 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=289039 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=289039 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-125 c3=-149 c4=-131 peakL=7026 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=289279 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=289279 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-124 c3=-149 c4=-130 peakL=7006 peakR=7001
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=289519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=289519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-124 c3=-147 c4=-132 peakL=7023 peakR=7017
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=289759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=289759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-132 peakL=7024 peakR=7018
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=290039 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=290039 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-132 peakL=7020 peakR=7014
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=290279 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=290279 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-131 peakL=7008 peakR=7002
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=290519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=290519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-124 c3=-146 c4=-131 peakL=7025 peakR=7018
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=290759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=290759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5530
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-122 c3=-147 c4=-130 peakL=7017 peakR=7011
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=291039 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=291039 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-132 peakL=7012 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=291279 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=291279 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-138 c2=-124 c3=-149 c4=-132 peakL=7028 peakR=7021
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=291519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=291519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-125 c3=-148 c4=-132 peakL=7005 peakR=6999
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=291759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=291759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7026 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=292039 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=292039 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8395 k8=3641
+[HPF] cv c1=-136 c2=-124 c3=-147 c4=-132 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=292279 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=292279 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5531
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=292519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=292519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4806 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4962 k6=4668 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-122 c3=-148 c4=-130 peakL=6979 peakR=6974
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=292759 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=292759 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3641
+[HPF] cv c1=-135 c2=-122 c3=-148 c4=-131 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=293039 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=293039 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5529
+[HPF] knobs first=5 k5=4961 k6=4668 k7=8395 k8=3641
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7025 peakR=7019
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=293279 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=293279 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5527
+[HPF] knobs first=5 k5=4960 k6=4669 k7=8395 k8=3641
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7012 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=293519 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=293519 load_pm=28 over=0 late=0 drops=3058
+[HPF] knobs first=1 k1=6737 k2=4807 k3=7333 k4=5528
+[HPF] knobs first=5 k5=4961 k6=4669 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-130 peakL=7027 peakR=7020
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
diff --git a/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial-summary.json b/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial-summary.json
new file mode 100644
index 000000000..aa44bdf14
--- /dev/null
+++ b/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial-summary.json
@@ -0,0 +1,36 @@
+{
+ "classification": "DERIVED",
+ "basis": "captured firmware telemetry only",
+ "records": {
+ "outputs": 181,
+ "timing": 205,
+ "key": 20,
+ "truth": 180,
+ "knobs": 360,
+ "cv": 180,
+ "midi": 180,
+ "boot": 23,
+ "system": 23
+ },
+ "invalid_records": 1,
+ "pressed_raw_indices": [
+ 0,
+ 1,
+ 2,
+ 8,
+ 9
+ ],
+ "maximum_reported_counters": {
+ "over": 0,
+ "late": 0,
+ "drops": 857,
+ "load_pm": 28
+ },
+ "not_proven": [
+ "cold boot",
+ "voltage",
+ "audio quality",
+ "absolute CPU timing",
+ "P0 completion"
+ ]
+}
diff --git a/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial.log b/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial.log
new file mode 100644
index 000000000..10953b37a
--- /dev/null
+++ b/field/HydraPulse/evidence/2026-09-08-fieldtruth-serial.log
@@ -0,0 +1,1353 @@
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=479 load_pm=27 over=0 late=0 drops=0
+[HPF] knobs first=1 k1=9997 k2=99$$[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] key raw=0 down=1 cb=97885
+[HPF] key raw=0 down=0 cb=98085
+[HPF] key raw=8 down=1 cb=98240
+[HPF] key raw=9 down=1 cb=98250
+[HPF] key raw=9 down=0 cb=98312
+[HPF] key raw=8 down=0 cb=98586
+[HPF] key raw=9 down=1 cb=98744
+[HPF] key raw=9 down=0 cb=98913
+[HPF] truth cb=95759 seen=128 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=95759 load_pm=28 over=0 late=0 drops=0
+[HPF] knobs first=1 k1=9996 k2=9944 k3=9183 k4=9569
+[HPF] knobs first=5 k5=6833 k6=6853 k7=8398 k8=3652
+[HPF] cv c1=-134 c2=-122 c3=-147 c4=-124 peakL=9999 peakR=9999
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] key raw=8 down=1 cb=112839
+[HPF] key raw=8 down=0 cb=112889
+[HPF] key raw=0 down=1 cb=113074
+[HPF] key raw=0 down=0 cb=113188
+[HPF] key raw=0 down=1 cb=113325
+[HPF] key raw=0 down=0 cb=113564
+[HPF] key raw=1 down=1 cb=113699
+[HPF] key raw=1 down=0 cb=113882
+[HPF] key raw=2 down=1 cb=114007
+[HPF] key raw=1 down=1 cb=114036
+[HPF] key raw=1 down=0 cb=114100
+[HPF] key raw=2 down=0 cb=114160
+[HPF] truth cb=130319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=130319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4816 k3=7335 k4=5549
+[HPF] knobs first=5 k5=4970 k6=4678 k7=8395 k8=3645
+[HPF] cv c1=-137 c2=-123 c3=-149 c4=-130 peakL=6995 peakR=6991
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=130559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=130559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4816 k3=7335 k4=5548
+[HPF] knobs first=5 k5=4971 k6=4678 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-125 c3=-148 c4=-132 peakL=6974 peakR=6969
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=130799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=130799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6741 k2=4816 k3=7335 k4=5548
+[HPF] knobs first=5 k5=4970 k6=4677 k7=8395 k8=3644
+[HPF] cv c1=-135 c2=-124 c3=-147 c4=-133 peakL=7006 peakR=7001
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=131039 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=131039 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4816 k3=7335 k4=5548
+[HPF] knobs first=5 k5=4971 k6=4678 k7=8395 k8=3645
+[HPF] cv c1=-137 c2=-124 c3=-146 c4=-131 peakL=6989 peakR=6984
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=131319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=131319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4815 k3=7335 k4=5549
+[HPF] knobs first=5 k5=4970 k6=4677 k7=8395 k8=3645
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=7006 peakR=7001
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=131559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=131559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4816 k3=7335 k4=5549
+[HPF] knobs first=5 k5=4970 k6=4678 k7=8396 k8=3644
+[HPF] cv c1=-136 c2=-124 c3=-147 c4=-131 peakL=7002 peakR=6997
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=131799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=131799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4815 k3=7335 k4=5548
+[HPF] knobs first=5 k5=4969 k6=4677 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-121 c3=-149 c4=-130 peakL=7001 peakR=6997
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=132039 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=132039 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4816 k3=7335 k4=5549
+[HPF] knobs first=5 k5=4970 k6=4677 k7=8396 k8=3645
+[HPF] cv c1=-135 c2=-123 c3=-147 c4=-131 peakL=6961 peakR=6958
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=132319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=132319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4816 k3=7335 k4=5548
+[HPF] knobs first=5 k5=4969 k6=4677 k7=8396 k8=3644
+[HPF] cv c1=-136 c2=-124 c3=-147 c4=-130 peakL=6997 peakR=6992
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=132559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=132559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4815 k3=7335 k4=5549
+[HPF] knobs first=5 k5=4969 k6=4678 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-125 c3=-148 c4=-131 peakL=7003 peakR=6998
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=132799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=132799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4815 k3=7334 k4=5547
+[HPF] knobs first=5 k5=4969 k6=4677 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-123 c3=-147 c4=-130 peakL=7001 peakR=6997
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=133039 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=133039 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4815 k3=7335 k4=5547
+[HPF] knobs first=5 k5=4970 k6=4677 k7=8396 k8=3644
+[HPF] cv c1=-136 c2=-123 c3=-147 c4=-132 peakL=7003 peakR=6999
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=133319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=133319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6741 k2=4815 k3=7334 k4=5548
+[HPF] knobs first=5 k5=4969 k6=4677 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-124 c3=-150 c4=-131 peakL=7009 peakR=7005
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=133559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=133559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4815 k3=7335 k4=5548
+[HPF] knobs first=5 k5=4969 k6=4676 k7=8396 k8=3644
+[HPF] cv c1=-136 c2=-123 c3=-149 c4=-130 peakL=6995 peakR=6990
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=133799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=133799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4815 k3=7335 k4=5548
+[HPF] knobs first=5 k5=4969 k6=4676 k7=8395 k8=3644
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-132 peakL=7000 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=134039 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=134039 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4814 k3=7335 k4=5547
+[HPF] knobs first=5 k5=4969 k6=4676 k7=8395 k8=3644
+[HPF] cv c1=-135 c2=-123 c3=-150 c4=-130 peakL=7009 peakR=7003
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=134319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=134319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4814 k3=7334 k4=5547
+[HPF] knobs first=5 k5=4969 k6=4676 k7=8395 k8=3644
+[HPF] cv c1=-134 c2=-124 c3=-148 c4=-130 peakL=7009 peakR=7004
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=134559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=134559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4814 k3=7335 k4=5546
+[HPF] knobs first=5 k5=4969 k6=4677 k7=8395 k8=3644
+[HPF] cv c1=-135 c2=-124 c3=-147 c4=-131 peakL=6990 peakR=6984
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=134799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=134799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4814 k3=7335 k4=5547
+[HPF] knobs first=5 k5=4968 k6=4676 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-124 c3=-147 c4=-131 peakL=6991 peakR=6986
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=135079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=135079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4814 k3=7335 k4=5548
+[HPF] knobs first=5 k5=4968 k6=4676 k7=8395 k8=3645
+[HPF] cv c1=-137 c2=-123 c3=-146 c4=-132 peakL=7008 peakR=7004
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=135319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=135319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4815 k3=7335 k4=5547
+[HPF] knobs first=5 k5=4969 k6=4676 k7=8395 k8=3644
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-133 peakL=6995 peakR=6990
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=135559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=135559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4814 k3=7335 k4=5547
+[HPF] knobs first=5 k5=4969 k6=4676 k7=8395 k8=3644
+[HPF] cv c1=-135 c2=-126 c3=-147 c4=-130 peakL=7007 peakR=7002
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=135799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=135799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7335 k4=5546
+[HPF] knobs first=5 k5=4968 k6=4676 k7=8395 k8=3644
+[HPF] cv c1=-137 c2=-124 c3=-147 c4=-131 peakL=6986 peakR=6981
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=136079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=136079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4814 k3=7335 k4=5545
+[HPF] knobs first=5 k5=4968 k6=4676 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-124 c3=-154 c4=-132 peakL=7009 peakR=7004
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=136319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=136319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6741 k2=4814 k3=7335 k4=5546
+[HPF] knobs first=5 k5=4969 k6=4676 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=7005 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=136559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=136559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7335 k4=5546
+[HPF] knobs first=5 k5=4968 k6=4675 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-123 c3=-147 c4=-132 peakL=7010 peakR=7005
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=136799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=136799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7334 k4=5546
+[HPF] knobs first=5 k5=4968 k6=4676 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-130 peakL=6967 peakR=6963
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=137079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=137079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4814 k3=7334 k4=5546
+[HPF] knobs first=5 k5=4968 k6=4675 k7=8395 k8=3644
+[HPF] cv c1=-134 c2=-123 c3=-147 c4=-131 peakL=7000 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=137319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=137319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7334 k4=5547
+[HPF] knobs first=5 k5=4968 k6=4675 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-130 peakL=7005 peakR=6999
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=137559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=137559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7334 k4=5546
+[HPF] knobs first=5 k5=4968 k6=4676 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=6986 peakR=6981
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=137799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=137799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7334 k4=5546
+[HPF] knobs first=5 k5=4968 k6=4675 k7=8395 k8=3644
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-132 peakL=6999 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=138079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=138079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7334 k4=5545
+[HPF] knobs first=5 k5=4968 k6=4675 k7=8395 k8=3644
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7009 peakR=7004
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=138319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=138319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6741 k2=4813 k3=7335 k4=5545
+[HPF] knobs first=5 k5=4968 k6=4675 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-130 peakL=7005 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=138559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=138559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7334 k4=5546
+[HPF] knobs first=5 k5=4967 k6=4675 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-125 c3=-148 c4=-131 peakL=7003 peakR=6998
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=138799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=138799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7334 k4=5545
+[HPF] knobs first=5 k5=4968 k6=4675 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7006 peakR=7001
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=139079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=139079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7334 k4=5546
+[HPF] knobs first=5 k5=4968 k6=4674 k7=8395 k8=3644
+[HPF] cv c1=-136 c2=-123 c3=-149 c4=-133 peakL=7008 peakR=7002
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=139319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=139319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7335 k4=5546
+[HPF] knobs first=5 k5=4967 k6=4675 k7=8395 k8=3644
+[HPF] cv c1=-134 c2=-123 c3=-146 c4=-131 peakL=6991 peakR=6986
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=139559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=139559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4813 k3=7335 k4=5545
+[HPF] knobs first=5 k5=4967 k6=4675 k7=8395 k8=3644
+[HPF] cv c1=-138 c2=-124 c3=-149 c4=-131 peakL=7007 peakR=7002
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=139799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=139799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7335 k4=5545
+[HPF] knobs first=5 k5=4967 k6=4674 k7=8395 k8=3644
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-131 peakL=6992 peakR=6987
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=140079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=140079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4813 k3=7334 k4=5545
+[HPF] knobs first=5 k5=4967 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-124 c3=-149 c4=-131 peakL=6995 peakR=6989
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=140319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=140319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4813 k3=7334 k4=5545
+[HPF] knobs first=5 k5=4967 k6=4675 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-132 peakL=7010 peakR=7005
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=140559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=140559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4812 k3=7334 k4=5544
+[HPF] knobs first=5 k5=4967 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-149 c4=-132 peakL=6992 peakR=6987
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=140799 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=140799 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7335 k4=5544
+[HPF] knobs first=5 k5=4967 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-132 peakL=7013 peakR=7008
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=141079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=141079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5544
+[HPF] knobs first=5 k5=4967 k6=4675 k7=8395 k8=3643
+[HPF] cv c1=-133 c2=-122 c3=-148 c4=-131 peakL=7011 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=141319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=141319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4812 k3=7335 k4=5544
+[HPF] knobs first=5 k5=4967 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7012 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=141559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=141559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5544
+[HPF] knobs first=5 k5=4966 k6=4675 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-130 peakL=6961 peakR=6957
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=141839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=141839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5545
+[HPF] knobs first=5 k5=4966 k6=4674 k7=8395 k8=3644
+[HPF] cv c1=-135 c2=-124 c3=-149 c4=-132 peakL=7003 peakR=6998
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=142079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=142079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4812 k3=7334 k4=5544
+[HPF] knobs first=5 k5=4966 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-149 c4=-132 peakL=7012 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=142319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=142319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5545
+[HPF] knobs first=5 k5=4966 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-150 c4=-131 peakL=6993 peakR=6988
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=142559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=142559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6740 k2=4812 k3=7334 k4=5544
+[HPF] knobs first=5 k5=4966 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7012 peakR=7007
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=142839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=142839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4966 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-134 c2=-124 c3=-148 c4=-131 peakL=7013 peakR=7008
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=143079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=143079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4966 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-132 peakL=6999 peakR=6993
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=143319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=143319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5544
+[HPF] knobs first=5 k5=4966 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7006 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=143559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=143559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5544
+[HPF] knobs first=5 k5=4965 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-133 peakL=7007 peakR=7001
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=143839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=143839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4966 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-132 peakL=7011 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=144079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=144079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4966 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-147 c4=-132 peakL=6995 peakR=6990
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=144319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=144319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4966 k6=4674 k7=8394 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7013 peakR=7008
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=144559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=144559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5544
+[HPF] knobs first=5 k5=4965 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-130 peakL=7001 peakR=6996
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=144839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=144839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4812 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4966 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7000 peakR=6994
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=145079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=145079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4966 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-138 c2=-123 c3=-150 c4=-131 peakL=7015 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=145319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=145319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4966 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-132 peakL=7000 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=145559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=145559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5544
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-124 c3=-147 c4=-130 peakL=7017 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=145839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=145839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4966 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-130 peakL=7009 peakR=7003
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=146079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=146079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-147 c4=-131 peakL=7011 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=146319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=146319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=6971 peakR=6968
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=146559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=146559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7333 k4=5543
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-122 c3=-149 c4=-132 peakL=7013 peakR=7009
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=146839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=146839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-134 c2=-124 c3=-147 c4=-131 peakL=7014 peakR=7009
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=147079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=147079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-134 c2=-123 c3=-148 c4=-131 peakL=7009 peakR=7003
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=147319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=147319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4965 k6=4674 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-147 c4=-131 peakL=7001 peakR=6996
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=147559 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=147559 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5541
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-122 c3=-148 c4=-131 peakL=7001 peakR=6996
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=147839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=147839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5541
+[HPF] knobs first=5 k5=4964 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-134 c2=-124 c3=-148 c4=-130 peakL=7001 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=148079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=148079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4965 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-122 c3=-147 c4=-130 peakL=7013 peakR=7007
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=148319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=148319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-132 peakL=7014 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=148599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=148599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4965 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-122 c3=-148 c4=-132 peakL=7015 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=148839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=148839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-132 peakL=6996 peakR=6991
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=149079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=149079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-123 c3=-148 c4=-131 peakL=7012 peakR=7006
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=149319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=149319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4965 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-125 c3=-149 c4=-132 peakL=6997 peakR=6991
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=149599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=149599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-138 c2=-124 c3=-148 c4=-132 peakL=6992 peakR=6986
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=149839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=149839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4965 k6=4673 k7=8394 k8=3643
+[HPF] cv c1=-137 c2=-123 c3=-148 c4=-131 peakL=7015 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=150079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=150079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4811 k3=7334 k4=5541
+[HPF] knobs first=5 k5=4965 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-149 c4=-130 peakL=7001 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=150319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=150319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-122 c3=-148 c4=-131 peakL=7018 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=150599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=150599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7333 k4=5541
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-132 peakL=7009 peakR=7003
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=150839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=150839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5541
+[HPF] knobs first=5 k5=4964 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7012 peakR=7007
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=151079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=151079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=6972 peakR=6969
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=151319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=151319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5542
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-124 c3=-147 c4=-132 peakL=7016 peakR=7011
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=151599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=151599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5541
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-134 c2=-123 c3=-148 c4=-131 peakL=7014 peakR=7009
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=151839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=151839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5543
+[HPF] knobs first=5 k5=4964 k6=4673 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7004 peakR=6999
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=152079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=152079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-131 peakL=7018 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=152319 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=152319 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5541
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-123 c3=-149 c4=-132 peakL=7002 peakR=6996
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=152599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=152599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5541
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-130 peakL=7008 peakR=7002
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=152839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=152839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5539
+[HPF] knobs first=5 k5=4963 k6=4672 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-133 peakL=7006 peakR=7001
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=153079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=153079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5541
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8394 k8=3643
+[HPF] cv c1=-137 c2=-123 c3=-148 c4=-130 peakL=7016 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=153359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=153359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7333 k4=5540
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-147 c4=-130 peakL=7017 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=153599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=153599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-132 peakL=7002 peakR=6996
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=153839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=153839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4810 k3=7334 k4=5539
+[HPF] knobs first=5 k5=4963 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-149 c4=-130 peakL=7015 peakR=7009
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=154079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=154079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7334 k4=5541
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-122 c3=-147 c4=-133 peakL=7017 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=154359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=154359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-132 peakL=6998 peakR=6993
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=154599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=154599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7018 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=154839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=154839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=7005 peakR=6999
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=155079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=155079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5539
+[HPF] knobs first=5 k5=4964 k6=4671 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7015 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=155359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=155359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4964 k6=4671 k7=8395 k8=3643
+[HPF] cv c1=-135 c2=-125 c3=-149 c4=-131 peakL=7004 peakR=6999
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=155599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=155599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7333 k4=5540
+[HPF] knobs first=5 k5=4963 k6=4672 k7=8394 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-149 c4=-131 peakL=7018 peakR=7011
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=155839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=155839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=6972 peakR=6968
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=156079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=156079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7333 k4=5539
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-123 c3=-148 c4=-132 peakL=7016 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=156359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=156359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-125 c3=-148 c4=-132 peakL=7016 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=156599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=156599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4810 k3=7334 k4=5539
+[HPF] knobs first=5 k5=4964 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-125 c3=-149 c4=-130 peakL=7003 peakR=6998
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=156839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=156839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7333 k4=5539
+[HPF] knobs first=5 k5=4964 k6=4671 k7=8394 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=7009 peakR=7004
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=157079 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=157079 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4964 k6=4671 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-125 c3=-148 c4=-132 peakL=6994 peakR=6989
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=157359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=157359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4810 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4672 k7=8395 k8=3642
+[HPF] cv c1=-135 c2=-125 c3=-148 c4=-131 peakL=7002 peakR=6997
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=157599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=157599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7334 k4=5540
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7006 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=157839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=157839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4964 k6=4672 k7=8394 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7012 peakR=7007
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=158119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=158119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7333 k4=5539
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-149 c4=-131 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=158359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=158359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4672 k7=8395 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-146 c4=-130 peakL=7000 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=158599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=158599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4672 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-132 peakL=7022 peakR=7017
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=158839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=158839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7334 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7005 peakR=6999
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=159119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=159119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-137 c2=-124 c3=-149 c4=-131 peakL=7003 peakR=6997
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=159359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=159359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4808 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-125 c3=-149 c4=-132 peakL=7020 peakR=7014
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=159599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=159599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4964 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-137 c2=-123 c3=-147 c4=-132 peakL=6990 peakR=6984
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=159839 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=159839 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4808 k3=7334 k4=5539
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8395 k8=3643
+[HPF] cv c1=-137 c2=-124 c3=-149 c4=-130 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=160119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=160119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-131 peakL=7020 peakR=7014
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=160359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=160359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7015 peakR=7009
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=160599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=160599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-134 c2=-123 c3=-147 c4=-130 peakL=6973 peakR=6969
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=160879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=160879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4672 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-147 c4=-132 peakL=7009 peakR=7004
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=161119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=161119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-150 c4=-132 peakL=7016 peakR=7011
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=161359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=161359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5537
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3643
+[HPF] cv c1=-136 c2=-122 c3=-148 c4=-132 peakL=7006 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=161599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=161599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7334 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-137 c2=-123 c3=-147 c4=-131 peakL=7011 peakR=7005
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=161879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=161879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7333 k4=5537
+[HPF] knobs first=5 k5=4963 k6=4672 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-125 c3=-149 c4=-129 peakL=7020 peakR=7015
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=162119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=162119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4808 k3=7333 k4=5538
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8394 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-130 peakL=7012 peakR=7007
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=162359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=162359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4963 k6=4672 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-132 peakL=7007 peakR=7002
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=162599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=162599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-147 c4=-131 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=162879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=162879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5537
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-134 c2=-122 c3=-147 c4=-131 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=163119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=163119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-125 c3=-148 c4=-131 peakL=7004 peakR=6998
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=163359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=163359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7334 k4=5539
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-137 c2=-123 c3=-147 c4=-132 peakL=7015 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=163599 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=163599 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7334 k4=5536
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-150 c4=-131 peakL=6998 peakR=6992
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=163879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=163879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5537
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-132 peakL=7000 peakR=6995
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=164119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=164119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4962 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-125 c3=-150 c4=-131 peakL=7015 peakR=7010
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=164359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=164359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7334 k4=5537
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7005 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=164639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=164639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4962 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-134 c2=-124 c3=-149 c4=-131 peakL=7021 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=164879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=164879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7334 k4=5537
+[HPF] knobs first=5 k5=4962 k6=4671 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=7018 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=165119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=165119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7333 k4=5537
+[HPF] knobs first=5 k5=4962 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-137 c2=-124 c3=-149 c4=-131 peakL=7021 peakR=7015
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=165359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=165359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-147 c4=-132 peakL=6975 peakR=6971
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=165639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=165639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-131 peakL=7005 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=165879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=165879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3643
+[HPF] cv c1=-134 c2=-123 c3=-148 c4=-131 peakL=7022 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=166119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=166119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4808 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7000 peakR=6994
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=166359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=166359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-149 c4=-131 peakL=7020 peakR=7015
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=166639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=166639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8394 k8=3643
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-132 peakL=7018 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=166879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=166879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-130 peakL=7009 peakR=7003
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=167119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=167119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4808 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4963 k6=4669 k7=8394 k8=3643
+[HPF] cv c1=-136 c2=-123 c3=-149 c4=-132 peakL=7009 peakR=7003
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=167359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=167359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7334 k4=5533
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-134 c2=-122 c3=-148 c4=-132 peakL=7023 peakR=7017
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=167639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=167639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4962 k6=4671 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7018 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=167879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=167879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-133 peakL=7004 peakR=6999
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=168119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=168119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=168359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=168359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-122 c3=-147 c4=-133 peakL=7004 peakR=6999
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=168639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=168639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4963 k6=4671 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-131 peakL=7006 peakR=7000
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=168879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=168879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-138 c2=-123 c3=-148 c4=-132 peakL=7021 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=169119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=169119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7334 k4=5534
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-130 peakL=6997 peakR=6991
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=169359 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=169359 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4808 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-137 c2=-125 c3=-148 c4=-132 peakL=7022 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=169639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=169639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7334 k4=5536
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-125 c3=-148 c4=-131 peakL=7022 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=169879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=169879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-132 peakL=7018 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=170119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=170119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-135 c2=-122 c3=-147 c4=-130 peakL=6978 peakR=6974
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=170399 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=170399 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4808 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-132 peakL=6999 peakR=6993
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=170639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=170639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4807 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7022 peakR=7017
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=170879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=170879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7008 peakR=7003
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=171119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=171119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4809 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-125 c3=-148 c4=-130 peakL=7019 peakR=7013
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=171399 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=171399 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5533
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-125 c3=-149 c4=-130 peakL=7022 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=171639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=171639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7334 k4=5533
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7010 peakR=7005
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=171879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=171879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7013 peakR=7008
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=172119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=172119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5532
+[HPF] knobs first=5 k5=4963 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-132 peakL=7017 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=172399 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=172399 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-131 peakL=7021 peakR=7015
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=172639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=172639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-149 c4=-133 peakL=7007 peakR=7001
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=172879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=172879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-135 c2=-124 c3=-148 c4=-131 peakL=7021 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=173119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=173119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-135 c2=-123 c3=-148 c4=-132 peakL=7017 peakR=7011
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=173399 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=173399 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-132 peakL=7007 peakR=7001
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=173639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=173639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6739 k2=4807 k3=7333 k4=5535
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-131 peakL=7022 peakR=7016
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=173879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=173879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8394 k8=3642
+[HPF] cv c1=-137 c2=-124 c3=-148 c4=-132 peakL=7003 peakR=6997
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] boot app=truth build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] truth cb=174119 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=174119 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4809 k3=7333 k4=5536
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7023 peakR=7017
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=174399 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=174399 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5533
+[HPF] knobs first=5 k5=4962 k6=4669 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7023 peakR=7017
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=174639 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=174639 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4808 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3642
+[HPF] cv c1=-136 c2=-123 c3=-148 c4=-131 peakL=7018 peakR=7012
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
+[HPF] truth cb=174879 seen=903 gate=0 edges=0
+[HPF] outputs dac1=0 dac2=0 enabled=0
+[HPF] timing cb=174879 load_pm=28 over=0 late=0 drops=857
+[HPF] knobs first=1 k1=6738 k2=4807 k3=7333 k4=5534
+[HPF] knobs first=5 k5=4962 k6=4670 k7=8395 k8=3643
+[HPF] cv c1=-136 c2=-124 c3=-148 c4=-130 peakL=6978 peakR=6973
+[HPF] midi count=128 type=0 channel0=0 d0=0 d1=0
diff --git a/field/HydraPulse/evidence/2026-09-08-hydrapulse-replacement-flash.log b/field/HydraPulse/evidence/2026-09-08-hydrapulse-replacement-flash.log
new file mode 100644
index 000000000..82de0bd7e
--- /dev/null
+++ b/field/HydraPulse/evidence/2026-09-08-hydrapulse-replacement-flash.log
@@ -0,0 +1,29 @@
+xPack Open On-Chip Debugger 0.12.0-01004-g9ea7f3d64-dirty (2023-01-30-15:04)
+Licensed under GNU GPL v2
+For bug reports, read
+ http://openocd.org/doc/doxygen/bugs.html
+Info : auto-selecting first available session transport "hla_swd". To override use 'transport select '.
+Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
+Info : clock speed 1800 kHz
+Info : STLINK V3J7M2 (API v3) VID:PID 0483:374E
+Info : Target voltage: 3.226490
+Info : [stm32h7x.cpu0] Cortex-M7 r1p1 processor detected
+Info : [stm32h7x.cpu0] target has 8 breakpoints, 4 watchpoints
+Info : starting gdb server for stm32h7x.cpu0 on 3333
+Info : Listening on port 3333 for gdb connections
+[stm32h7x.cpu0] halted due to debug-request, current mode: Thread
+xPSR: 0x01000000 pc: 0x08000a10 msp: 0x20020000
+Info : Unable to match requested speed 4000 kHz, using 3300 kHz
+Info : Unable to match requested speed 4000 kHz, using 3300 kHz
+** Programming Started **
+Info : Device: STM32H74x/75x
+Info : flash size probed value 128k
+Info : STM32H7 flash has a single bank
+Info : Bank (0) size is 128 kb, base address is 0x08000000
+Info : Padding image section 1 at 0x0801ddd0 with 16 bytes (bank write end alignment)
+Warn : Adding extra erase range, 0x0801dde0 .. 0x0801ffff
+** Programming Finished **
+** Verify Started **
+** Verified OK **
+** Resetting Target **
+shutdown command invoked
diff --git a/field/HydraPulse/evidence/2026-09-08-hydrapulse-replacement-serial-summary.json b/field/HydraPulse/evidence/2026-09-08-hydrapulse-replacement-serial-summary.json
new file mode 100644
index 000000000..ebe09d5f4
--- /dev/null
+++ b/field/HydraPulse/evidence/2026-09-08-hydrapulse-replacement-serial-summary.json
@@ -0,0 +1,28 @@
+{
+ "classification": "DERIVED",
+ "basis": "captured firmware telemetry only",
+ "records": {
+ "beat": 219,
+ "timing": 247,
+ "queues": 219,
+ "boot": 28,
+ "system": 28
+ },
+ "invalid_records": 0,
+ "pressed_raw_indices": [],
+ "maximum_reported_counters": {
+ "faults": 0,
+ "load_pm": 123,
+ "over": 0,
+ "late": 0,
+ "mdrops": 0,
+ "sdrops": 0
+ },
+ "not_proven": [
+ "cold boot",
+ "voltage",
+ "audio quality",
+ "absolute CPU timing",
+ "P0 completion"
+ ]
+}
diff --git a/field/HydraPulse/evidence/2026-09-08-hydrapulse-replacement-serial.log b/field/HydraPulse/evidence/2026-09-08-hydrapulse-replacement-serial.log
new file mode 100644
index 000000000..728379c25
--- /dev/null
+++ b/field/HydraPulse/evidence/2026-09-08-hydrapulse-replacement-serial.log
@@ -0,0 +1,741 @@
+[HPF] beat cb=93119 load_pm=123 faults=0
+[HPF] timing cb=93119 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=93399 load_pm=123 faults=0
+[HPF] timing cb=93399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=93639 load_pm=123 faults=0
+[HPF] timing cb=93639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=93879 load_pm=123 faults=0
+[HPF] timing cb=93879 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=94119 load_pm=123 faults=0
+[HPF] timing cb=94119 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=94399 load_pm=123 faults=0
+[HPF] timing cb=94399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=94639 load_pm=123 faults=0
+[HPF] timing cb=94639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=94879 load_pm=123 faults=0
+[HPF] timing cb=94879 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=95119 load_pm=123 faults=0
+[HPF] timing cb=95119 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=95399 load_pm=123 faults=0
+[HPF] timing cb=95399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=95639 load_pm=123 faults=0
+[HPF] timing cb=95639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=95879 load_pm=123 faults=0
+[HPF] timing cb=95879 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=96119 load_pm=123 faults=0
+[HPF] timing cb=96119 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=96399 load_pm=123 faults=0
+[HPF] timing cb=96399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=96639 load_pm=123 faults=0
+[HPF] timing cb=96639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=96879 load_pm=123 faults=0
+[HPF] timing cb=96879 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=97159 load_pm=123 faults=0
+[HPF] timing cb=97159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=97399 load_pm=123 faults=0
+[HPF] timing cb=97399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=97639 load_pm=123 faults=0
+[HPF] timing cb=97639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=97879 load_pm=123 faults=0
+[HPF] timing cb=97879 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=98159 load_pm=123 faults=0
+[HPF] timing cb=98159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=98399 load_pm=123 faults=0
+[HPF] timing cb=98399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=98639 load_pm=123 faults=0
+[HPF] timing cb=98639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=98879 load_pm=123 faults=0
+[HPF] timing cb=98879 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=99159 load_pm=123 faults=0
+[HPF] timing cb=99159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=99399 load_pm=123 faults=0
+[HPF] timing cb=99399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=99639 load_pm=123 faults=0
+[HPF] timing cb=99639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=99879 load_pm=123 faults=0
+[HPF] timing cb=99879 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=100159 load_pm=123 faults=0
+[HPF] timing cb=100159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=100399 load_pm=123 faults=0
+[HPF] timing cb=100399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=100639 load_pm=123 faults=0
+[HPF] timing cb=100639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=100879 load_pm=123 faults=0
+[HPF] timing cb=100879 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=101159 load_pm=123 faults=0
+[HPF] timing cb=101159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=101399 load_pm=123 faults=0
+[HPF] timing cb=101399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=101639 load_pm=123 faults=0
+[HPF] timing cb=101639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=101879 load_pm=123 faults=0
+[HPF] timing cb=101879 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=102159 load_pm=123 faults=0
+[HPF] timing cb=102159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=102399 load_pm=123 faults=0
+[HPF] timing cb=102399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=102639 load_pm=123 faults=0
+[HPF] timing cb=102639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=102919 load_pm=123 faults=0
+[HPF] timing cb=102919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=103159 load_pm=123 faults=0
+[HPF] timing cb=103159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=103399 load_pm=123 faults=0
+[HPF] timing cb=103399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=103639 load_pm=123 faults=0
+[HPF] timing cb=103639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=103919 load_pm=123 faults=0
+[HPF] timing cb=103919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=104159 load_pm=123 faults=0
+[HPF] timing cb=104159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=104399 load_pm=123 faults=0
+[HPF] timing cb=104399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=104639 load_pm=123 faults=0
+[HPF] timing cb=104639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=104919 load_pm=123 faults=0
+[HPF] timing cb=104919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=105159 load_pm=123 faults=0
+[HPF] timing cb=105159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=105399 load_pm=123 faults=0
+[HPF] timing cb=105399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=105639 load_pm=123 faults=0
+[HPF] timing cb=105639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=105919 load_pm=123 faults=0
+[HPF] timing cb=105919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=106159 load_pm=123 faults=0
+[HPF] timing cb=106159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=106399 load_pm=123 faults=0
+[HPF] timing cb=106399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=106639 load_pm=123 faults=0
+[HPF] timing cb=106639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=106919 load_pm=123 faults=0
+[HPF] timing cb=106919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=107159 load_pm=123 faults=0
+[HPF] timing cb=107159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=107399 load_pm=123 faults=0
+[HPF] timing cb=107399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=107639 load_pm=123 faults=0
+[HPF] timing cb=107639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=107919 load_pm=123 faults=0
+[HPF] timing cb=107919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=108159 load_pm=123 faults=0
+[HPF] timing cb=108159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=108399 load_pm=123 faults=0
+[HPF] timing cb=108399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=108639 load_pm=123 faults=0
+[HPF] timing cb=108639 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=108919 load_pm=123 faults=0
+[HPF] timing cb=108919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=109159 load_pm=123 faults=0
+[HPF] timing cb=109159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=109399 load_pm=123 faults=0
+[HPF] timing cb=109399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=109679 load_pm=123 faults=0
+[HPF] timing cb=109679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=109919 load_pm=123 faults=0
+[HPF] timing cb=109919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=110159 load_pm=123 faults=0
+[HPF] timing cb=110159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=110399 load_pm=123 faults=0
+[HPF] timing cb=110399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=110679 load_pm=123 faults=0
+[HPF] timing cb=110679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=110919 load_pm=123 faults=0
+[HPF] timing cb=110919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=111159 load_pm=123 faults=0
+[HPF] timing cb=111159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=111399 load_pm=123 faults=0
+[HPF] timing cb=111399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=111679 load_pm=123 faults=0
+[HPF] timing cb=111679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=111919 load_pm=123 faults=0
+[HPF] timing cb=111919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=112159 load_pm=123 faults=0
+[HPF] timing cb=112159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=112399 load_pm=123 faults=0
+[HPF] timing cb=112399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=112679 load_pm=123 faults=0
+[HPF] timing cb=112679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=112919 load_pm=123 faults=0
+[HPF] timing cb=112919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=113159 load_pm=123 faults=0
+[HPF] timing cb=113159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=113399 load_pm=123 faults=0
+[HPF] timing cb=113399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=113679 load_pm=123 faults=0
+[HPF] timing cb=113679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=113919 load_pm=123 faults=0
+[HPF] timing cb=113919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=114159 load_pm=123 faults=0
+[HPF] timing cb=114159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=114399 load_pm=123 faults=0
+[HPF] timing cb=114399 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=114679 load_pm=123 faults=0
+[HPF] timing cb=114679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=114919 load_pm=123 faults=0
+[HPF] timing cb=114919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=115159 load_pm=123 faults=0
+[HPF] timing cb=115159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=115439 load_pm=123 faults=0
+[HPF] timing cb=115439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=115679 load_pm=123 faults=0
+[HPF] timing cb=115679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=115919 load_pm=123 faults=0
+[HPF] timing cb=115919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=116159 load_pm=123 faults=0
+[HPF] timing cb=116159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=116439 load_pm=123 faults=0
+[HPF] timing cb=116439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=116679 load_pm=123 faults=0
+[HPF] timing cb=116679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=116919 load_pm=123 faults=0
+[HPF] timing cb=116919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=117159 load_pm=123 faults=0
+[HPF] timing cb=117159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=117439 load_pm=123 faults=0
+[HPF] timing cb=117439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=117679 load_pm=123 faults=0
+[HPF] timing cb=117679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=117919 load_pm=123 faults=0
+[HPF] timing cb=117919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=118159 load_pm=123 faults=0
+[HPF] timing cb=118159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=118439 load_pm=123 faults=0
+[HPF] timing cb=118439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=118679 load_pm=123 faults=0
+[HPF] timing cb=118679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=118919 load_pm=123 faults=0
+[HPF] timing cb=118919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=119159 load_pm=123 faults=0
+[HPF] timing cb=119159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=119439 load_pm=123 faults=0
+[HPF] timing cb=119439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=119679 load_pm=123 faults=0
+[HPF] timing cb=119679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=119919 load_pm=123 faults=0
+[HPF] timing cb=119919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=120159 load_pm=123 faults=0
+[HPF] timing cb=120159 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=120439 load_pm=123 faults=0
+[HPF] timing cb=120439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=120679 load_pm=123 faults=0
+[HPF] timing cb=120679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=120919 load_pm=123 faults=0
+[HPF] timing cb=120919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=121199 load_pm=123 faults=0
+[HPF] timing cb=121199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=121439 load_pm=123 faults=0
+[HPF] timing cb=121439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=121679 load_pm=123 faults=0
+[HPF] timing cb=121679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=121919 load_pm=123 faults=0
+[HPF] timing cb=121919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=122199 load_pm=123 faults=0
+[HPF] timing cb=122199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=122439 load_pm=123 faults=0
+[HPF] timing cb=122439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=122679 load_pm=123 faults=0
+[HPF] timing cb=122679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=122919 load_pm=123 faults=0
+[HPF] timing cb=122919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=123199 load_pm=123 faults=0
+[HPF] timing cb=123199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=123439 load_pm=123 faults=0
+[HPF] timing cb=123439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=123679 load_pm=123 faults=0
+[HPF] timing cb=123679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=123919 load_pm=123 faults=0
+[HPF] timing cb=123919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=124199 load_pm=123 faults=0
+[HPF] timing cb=124199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=124439 load_pm=123 faults=0
+[HPF] timing cb=124439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=124679 load_pm=123 faults=0
+[HPF] timing cb=124679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=124919 load_pm=123 faults=0
+[HPF] timing cb=124919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=125199 load_pm=123 faults=0
+[HPF] timing cb=125199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=125439 load_pm=123 faults=0
+[HPF] timing cb=125439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=125679 load_pm=123 faults=0
+[HPF] timing cb=125679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=125919 load_pm=123 faults=0
+[HPF] timing cb=125919 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=126199 load_pm=123 faults=0
+[HPF] timing cb=126199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=126439 load_pm=123 faults=0
+[HPF] timing cb=126439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=126679 load_pm=123 faults=0
+[HPF] timing cb=126679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=126959 load_pm=123 faults=0
+[HPF] timing cb=126959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=127199 load_pm=123 faults=0
+[HPF] timing cb=127199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=127439 load_pm=123 faults=0
+[HPF] timing cb=127439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=127679 load_pm=123 faults=0
+[HPF] timing cb=127679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=127959 load_pm=123 faults=0
+[HPF] timing cb=127959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=128199 load_pm=123 faults=0
+[HPF] timing cb=128199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=128439 load_pm=123 faults=0
+[HPF] timing cb=128439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=128679 load_pm=123 faults=0
+[HPF] timing cb=128679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=128959 load_pm=123 faults=0
+[HPF] timing cb=128959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=129199 load_pm=123 faults=0
+[HPF] timing cb=129199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=129439 load_pm=123 faults=0
+[HPF] timing cb=129439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=129679 load_pm=123 faults=0
+[HPF] timing cb=129679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=129959 load_pm=123 faults=0
+[HPF] timing cb=129959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=130199 load_pm=123 faults=0
+[HPF] timing cb=130199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=130439 load_pm=123 faults=0
+[HPF] timing cb=130439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=130679 load_pm=123 faults=0
+[HPF] timing cb=130679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=130959 load_pm=123 faults=0
+[HPF] timing cb=130959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=131199 load_pm=123 faults=0
+[HPF] timing cb=131199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=131439 load_pm=123 faults=0
+[HPF] timing cb=131439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=131679 load_pm=123 faults=0
+[HPF] timing cb=131679 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=131959 load_pm=123 faults=0
+[HPF] timing cb=131959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=132199 load_pm=123 faults=0
+[HPF] timing cb=132199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=132439 load_pm=123 faults=0
+[HPF] timing cb=132439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=132719 load_pm=123 faults=0
+[HPF] timing cb=132719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=132959 load_pm=123 faults=0
+[HPF] timing cb=132959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=133199 load_pm=123 faults=0
+[HPF] timing cb=133199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=133439 load_pm=123 faults=0
+[HPF] timing cb=133439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=133719 load_pm=123 faults=0
+[HPF] timing cb=133719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=133959 load_pm=123 faults=0
+[HPF] timing cb=133959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=134199 load_pm=123 faults=0
+[HPF] timing cb=134199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=134439 load_pm=123 faults=0
+[HPF] timing cb=134439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=134719 load_pm=123 faults=0
+[HPF] timing cb=134719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=134959 load_pm=123 faults=0
+[HPF] timing cb=134959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=135199 load_pm=123 faults=0
+[HPF] timing cb=135199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=135439 load_pm=123 faults=0
+[HPF] timing cb=135439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=135719 load_pm=123 faults=0
+[HPF] timing cb=135719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=135959 load_pm=123 faults=0
+[HPF] timing cb=135959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=136199 load_pm=123 faults=0
+[HPF] timing cb=136199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=136439 load_pm=123 faults=0
+[HPF] timing cb=136439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=136719 load_pm=123 faults=0
+[HPF] timing cb=136719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=136959 load_pm=123 faults=0
+[HPF] timing cb=136959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=137199 load_pm=123 faults=0
+[HPF] timing cb=137199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=137439 load_pm=123 faults=0
+[HPF] timing cb=137439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=137719 load_pm=123 faults=0
+[HPF] timing cb=137719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=137959 load_pm=123 faults=0
+[HPF] timing cb=137959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=138199 load_pm=123 faults=0
+[HPF] timing cb=138199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=138439 load_pm=123 faults=0
+[HPF] timing cb=138439 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=138719 load_pm=123 faults=0
+[HPF] timing cb=138719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=138959 load_pm=123 faults=0
+[HPF] timing cb=138959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=139199 load_pm=123 faults=0
+[HPF] timing cb=139199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=139479 load_pm=123 faults=0
+[HPF] timing cb=139479 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=139719 load_pm=123 faults=0
+[HPF] timing cb=139719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=139959 load_pm=123 faults=0
+[HPF] timing cb=139959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=140199 load_pm=123 faults=0
+[HPF] timing cb=140199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=140479 load_pm=123 faults=0
+[HPF] timing cb=140479 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=140719 load_pm=123 faults=0
+[HPF] timing cb=140719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=140959 load_pm=123 faults=0
+[HPF] timing cb=140959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=141199 load_pm=123 faults=0
+[HPF] timing cb=141199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=141479 load_pm=123 faults=0
+[HPF] timing cb=141479 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=141719 load_pm=123 faults=0
+[HPF] timing cb=141719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=141959 load_pm=123 faults=0
+[HPF] timing cb=141959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=142199 load_pm=123 faults=0
+[HPF] timing cb=142199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=142479 load_pm=123 faults=0
+[HPF] timing cb=142479 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=142719 load_pm=123 faults=0
+[HPF] timing cb=142719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=142959 load_pm=123 faults=0
+[HPF] timing cb=142959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=143199 load_pm=123 faults=0
+[HPF] timing cb=143199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=143479 load_pm=123 faults=0
+[HPF] timing cb=143479 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=143719 load_pm=123 faults=0
+[HPF] timing cb=143719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=143959 load_pm=123 faults=0
+[HPF] timing cb=143959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=144199 load_pm=123 faults=0
+[HPF] timing cb=144199 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=144479 load_pm=123 faults=0
+[HPF] timing cb=144479 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=144719 load_pm=123 faults=0
+[HPF] timing cb=144719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=144959 load_pm=123 faults=0
+[HPF] timing cb=144959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=145239 load_pm=123 faults=0
+[HPF] timing cb=145239 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=145479 load_pm=123 faults=0
+[HPF] timing cb=145479 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=145719 load_pm=123 faults=0
+[HPF] timing cb=145719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=145959 load_pm=123 faults=0
+[HPF] timing cb=145959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=146239 load_pm=123 faults=0
+[HPF] timing cb=146239 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=146479 load_pm=123 faults=0
+[HPF] timing cb=146479 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=146719 load_pm=123 faults=0
+[HPF] timing cb=146719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=146959 load_pm=123 faults=0
+[HPF] timing cb=146959 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=147239 load_pm=123 faults=0
+[HPF] timing cb=147239 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] beat cb=147479 load_pm=123 faults=0
+[HPF] timing cb=147479 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
+[HPF] boot app=beat build=24e035a726a1 sr=48000 block=48 reset=unknown
+[HPF] system board=1 memory=0 loader=1
+[HPF] timing cpu=400000000 tick=200000000
+[HPF] beat cb=147719 load_pm=123 faults=0
+[HPF] timing cb=147719 over=0 late=0
+[HPF] queues mdrops=0 sdrops=0
diff --git a/field/HydraPulse/evidence/arm-build-2026-09-08.json b/field/HydraPulse/evidence/arm-build-2026-09-08.json
new file mode 100644
index 000000000..9adfbf72e
--- /dev/null
+++ b/field/HydraPulse/evidence/arm-build-2026-09-08.json
@@ -0,0 +1,87 @@
+{
+ "status": "VERIFIED",
+ "claim": "ARM build, NOT hardware qualification",
+ "source_sha256": "24e035a726a12d2ad72f8b69d1491a3ad17267acbbcf1a38135e8937bd8250cc",
+ "source_files": {
+ "Makefile": "79e4b92a8706c3f991bc0ce6e66c65aa393b857278864fa7792ea082bcf9bea9",
+ "deps.lock.json": "1021250590eff35545367ba51e81359955fa780bb53dc375156aaa04e508893a",
+ "firmware/FieldAdapter.h": "e2c6eeda2e539ae1b8cdbb13e50fa59de196955640c9ad294002e696692914cc",
+ "firmware/FieldTruth.cpp": "73b013705424e6f3b7256e360948a1ded0202512eaaf49efe7a36843947bf5e2",
+ "firmware/HydraPulse.cpp": "9848ce3dfdb12c79b1c7f3e53dae7caff80852ed3c0d46a2c714fc477f992e56",
+ "src/app/Controller.cpp": "eea781db05042b6a826ef260b2a1e423966841f0c61459acefbcda5defb045a3",
+ "src/app/Controller.h": "140bdd623fa25e3825f680237800f8fff3878349ae18b98dfecbd9b22143c02a",
+ "src/app/Engine.cpp": "24d6d6a5ba9609811ff6175a2eed70a7e1d158b3363d3d81b7027cfca52644fc",
+ "src/app/Engine.h": "80a96be8a476422b30cb1834e5f6df7eec883ea10875887b939eaf9dfc02840e",
+ "src/app/Presets.generated.h": "da25b2f2bee999d3294a30fba1381ef70fc7cf30240263537b4feb60b6b08ab1",
+ "src/app/Types.h": "1d03a79172459ff9cc73c22f257aa9d21f2b36000f3863503314328e37787d4c",
+ "src/core/ControlPickup.h": "1c4552ec20eabfd3dc2eed2e75d698866222ddf539233958c66e39bb8cbf5d4c",
+ "src/core/GrooveClock.h": "5eaec598fba97e468c663e3be504180c16412b5c592a7b5a7ff2c12f3422afbd",
+ "src/core/OutputTestArm.h": "5592b955b51dee2b11f0abf1836393163bebbd5e86ac5c1f4b7af25aaa6b6e5f",
+ "src/core/Pattern16.h": "04177efe8a6587e3b1f68821e02791c89bacb2d200fc38ad41c1f487d4a8f7b5",
+ "src/core/SampleAccurateStepClock.h": "87de93e196fa7c109aa8e7819da0cf7547022303367d697d2013c11a78e4ff6b",
+ "src/core/SpscQueue.h": "f8092a95d08ab54ff657c0b052753d7e75a452a7444bcea4da9d758cc1f8c8c0",
+ "src/core/StepSequencer.h": "be87f9d0a2c1cc87cecb174df6e8627c62198e1b261d016ee80de6d5f0d84097",
+ "src/dsp/Primitives.h": "188afd8c0ca84f7f2f5c0d31751065d515ade676598276247efd96e56d0b044a",
+ "src/dsp/Voices.cpp": "903a53e2e3ccb0c3dd3542b93851333398ce31789f108bce7078f11914a37877",
+ "src/dsp/Voices.h": "ccb3f082a2b168046dcfa64622feffd7162c0bc6f0272e02f1e799f79272d803",
+ "tools/build_target.py": "33fb1b6957ab667995bd53a55a151eeb945db1e1ab1a573f663d83511b3154ff"
+ },
+ "compiler": "arm-none-eabi-g++ (15:13.2.rel1-2) 13.2.1 20231009\nCopyright (C) 2023 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions. There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n",
+ "tools": {
+ "git": "/usr/bin/git",
+ "make": "/usr/bin/make",
+ "arm-none-eabi-gcc": "/usr/bin/arm-none-eabi-gcc",
+ "arm-none-eabi-g++": "/usr/bin/arm-none-eabi-g++",
+ "arm-none-eabi-objcopy": "/usr/bin/arm-none-eabi-objcopy",
+ "arm-none-eabi-size": "/usr/bin/arm-none-eabi-size",
+ "arm-none-eabi-readelf": "/usr/bin/arm-none-eabi-readelf"
+ },
+ "dependency": {
+ "head": "85172e2b5c9abea1bf56dcd5d2c7b3f5717eb66b",
+ "license_blob_sha1": "1c3a251694f1d1842c3f114508d36ca63cddf275",
+ "recursive_gitlinks": [
+ "f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4 tests/googletest (release-1.8.0-2713-gf5e592d8)"
+ ],
+ "tracked_clean": true
+ },
+ "app_type": "BOOT_NONE",
+ "abi": "default-width enums in application and library",
+ "parent_commit": "041bfcbb7975316606ece8f1ccbacbab9c0e1482",
+ "workspace_status": "?? field/HydraPulse/",
+ "images": {
+ "truth": {
+ ".bin": {
+ "file": "HydraPulseTruth.bin",
+ "bytes": 107280,
+ "sha256": "72a21e32621174f5d66cbcade3711b21d5810711813b27fc405c696e48bdab60"
+ },
+ ".elf": {
+ "file": "HydraPulseTruth.elf",
+ "bytes": 1916336,
+ "sha256": "5f8c9535b894f07a9bc230081659078bc3c48b5e0d436733901004d7873712b6"
+ },
+ ".map": {
+ "file": "HydraPulseTruth.map",
+ "bytes": 1443445,
+ "sha256": "1ea6e5fe1c4e92ea6a7076875f06ffa32410173b06ac0b556265856f873ec3cf"
+ }
+ },
+ "beat": {
+ ".bin": {
+ "file": "HydraPulseBeat.bin",
+ "bytes": 122320,
+ "sha256": "6b0d5b518b20ad57720755ee6330b684b2e905704ebaa35d906225a9173dc458"
+ },
+ ".elf": {
+ "file": "HydraPulseBeat.elf",
+ "bytes": 2113276,
+ "sha256": "a020a03a42968e0e7e0112c604c065f4357240730baa0d2dfb1100d82a925160"
+ },
+ ".map": {
+ "file": "HydraPulseBeat.map",
+ "bytes": 1482185,
+ "sha256": "a9d6809d48f46562ba2dc403b83fdd5c179345a379775ff8a6750c0c65244e81"
+ }
+ }
+ }
+}
diff --git a/field/HydraPulse/evidence/ci-arm-build-2026-09-08.json b/field/HydraPulse/evidence/ci-arm-build-2026-09-08.json
new file mode 100644
index 000000000..4b47e19ba
--- /dev/null
+++ b/field/HydraPulse/evidence/ci-arm-build-2026-09-08.json
@@ -0,0 +1,87 @@
+{
+ "status": "VERIFIED",
+ "claim": "ARM build, NOT hardware qualification",
+ "source_sha256": "41e814118b1e35d534c7ab203fc8b8b520f3ae3ba611883819cddc0f05d5493c",
+ "source_files": {
+ "Makefile": "79e4b92a8706c3f991bc0ce6e66c65aa393b857278864fa7792ea082bcf9bea9",
+ "deps.lock.json": "1021250590eff35545367ba51e81359955fa780bb53dc375156aaa04e508893a",
+ "firmware/FieldAdapter.h": "e2c6eeda2e539ae1b8cdbb13e50fa59de196955640c9ad294002e696692914cc",
+ "firmware/FieldTruth.cpp": "73b013705424e6f3b7256e360948a1ded0202512eaaf49efe7a36843947bf5e2",
+ "firmware/HydraPulse.cpp": "9848ce3dfdb12c79b1c7f3e53dae7caff80852ed3c0d46a2c714fc477f992e56",
+ "src/app/Controller.cpp": "eea781db05042b6a826ef260b2a1e423966841f0c61459acefbcda5defb045a3",
+ "src/app/Controller.h": "140bdd623fa25e3825f680237800f8fff3878349ae18b98dfecbd9b22143c02a",
+ "src/app/Engine.cpp": "24d6d6a5ba9609811ff6175a2eed70a7e1d158b3363d3d81b7027cfca52644fc",
+ "src/app/Engine.h": "80a96be8a476422b30cb1834e5f6df7eec883ea10875887b939eaf9dfc02840e",
+ "src/app/Presets.generated.h": "011fc5677bdf00d2d666693a188f6c5fee19c97be09dd7dccd892bd934a2dbb3",
+ "src/app/Types.h": "1d03a79172459ff9cc73c22f257aa9d21f2b36000f3863503314328e37787d4c",
+ "src/core/ControlPickup.h": "1c4552ec20eabfd3dc2eed2e75d698866222ddf539233958c66e39bb8cbf5d4c",
+ "src/core/GrooveClock.h": "5eaec598fba97e468c663e3be504180c16412b5c592a7b5a7ff2c12f3422afbd",
+ "src/core/OutputTestArm.h": "5592b955b51dee2b11f0abf1836393163bebbd5e86ac5c1f4b7af25aaa6b6e5f",
+ "src/core/Pattern16.h": "04177efe8a6587e3b1f68821e02791c89bacb2d200fc38ad41c1f487d4a8f7b5",
+ "src/core/SampleAccurateStepClock.h": "87de93e196fa7c109aa8e7819da0cf7547022303367d697d2013c11a78e4ff6b",
+ "src/core/SpscQueue.h": "f8092a95d08ab54ff657c0b052753d7e75a452a7444bcea4da9d758cc1f8c8c0",
+ "src/core/StepSequencer.h": "be87f9d0a2c1cc87cecb174df6e8627c62198e1b261d016ee80de6d5f0d84097",
+ "src/dsp/Primitives.h": "188afd8c0ca84f7f2f5c0d31751065d515ade676598276247efd96e56d0b044a",
+ "src/dsp/Voices.cpp": "903a53e2e3ccb0c3dd3542b93851333398ce31789f108bce7078f11914a37877",
+ "src/dsp/Voices.h": "ccb3f082a2b168046dcfa64622feffd7162c0bc6f0272e02f1e799f79272d803",
+ "tools/build_target.py": "33fb1b6957ab667995bd53a55a151eeb945db1e1ab1a573f663d83511b3154ff"
+ },
+ "compiler": "arm-none-eabi-g++ (15:13.2.rel1-2) 13.2.1 20231009\nCopyright (C) 2023 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions. There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n",
+ "tools": {
+ "git": "/usr/bin/git",
+ "make": "/usr/bin/make",
+ "arm-none-eabi-gcc": "/usr/bin/arm-none-eabi-gcc",
+ "arm-none-eabi-g++": "/usr/bin/arm-none-eabi-g++",
+ "arm-none-eabi-objcopy": "/usr/bin/arm-none-eabi-objcopy",
+ "arm-none-eabi-size": "/usr/bin/arm-none-eabi-size",
+ "arm-none-eabi-readelf": "/usr/bin/arm-none-eabi-readelf"
+ },
+ "dependency": {
+ "head": "85172e2b5c9abea1bf56dcd5d2c7b3f5717eb66b",
+ "license_blob_sha1": "1c3a251694f1d1842c3f114508d36ca63cddf275",
+ "recursive_gitlinks": [
+ "f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4 tests/googletest (f5e592d)"
+ ],
+ "tracked_clean": true
+ },
+ "app_type": "BOOT_NONE",
+ "abi": "default-width enums in application and library",
+ "parent_commit": "fdd22f363b04447ceb92c2a2774ae9d96edc54df",
+ "workspace_status": "",
+ "images": {
+ "truth": {
+ ".bin": {
+ "file": "HydraPulseTruth.bin",
+ "bytes": 107280,
+ "sha256": "62fa8bc514c56ceccf0caec53bdd4cb2fb7ddc44e10871de7376ddd9097e4ddc"
+ },
+ ".elf": {
+ "file": "HydraPulseTruth.elf",
+ "bytes": 1915244,
+ "sha256": "349fc3dd3d486df36f3718ad07799fff60b907dd132572d0e841b15dca5bdce2"
+ },
+ ".map": {
+ "file": "HydraPulseTruth.map",
+ "bytes": 1000457,
+ "sha256": "27f51d2229f57abe3155d94ffe6ddf81ee1dba49974bb4f6fc1ad3bf5e716c0b"
+ }
+ },
+ "beat": {
+ ".bin": {
+ "file": "HydraPulseBeat.bin",
+ "bytes": 122320,
+ "sha256": "23e8feebd7f0d2e1e92389425f4ad0bdbe203a6f012fc994a6d698744c40e5f5"
+ },
+ ".elf": {
+ "file": "HydraPulseBeat.elf",
+ "bytes": 2112184,
+ "sha256": "482c62e1e0e6f4770eb932636950e2ceca7f3386a4706d310dc6484d082fd665"
+ },
+ ".map": {
+ "file": "HydraPulseBeat.map",
+ "bytes": 1039197,
+ "sha256": "46bdc3903875eba7d679d69d34ff9ff87c4f3c144c86777ee0254be17bdcf813"
+ }
+ }
+ }
+}
diff --git a/field/HydraPulse/evidence/commit-source-bridge-2026-09-08.json b/field/HydraPulse/evidence/commit-source-bridge-2026-09-08.json
new file mode 100644
index 000000000..9f717e52f
--- /dev/null
+++ b/field/HydraPulse/evidence/commit-source-bridge-2026-09-08.json
@@ -0,0 +1,12 @@
+{
+ "source_commit": "fdd22f363b04447ceb92c2a2774ae9d96edc54df",
+ "all_22_inputs_equal_after_line_ending_normalization": true,
+ "byte_differences": [
+ {
+ "file": "src/app/Presets.generated.h",
+ "build_input_sha256": "da25b2f2bee999d3294a30fba1381ef70fc7cf30240263537b4feb60b6b08ab1",
+ "git_blob_content_sha256": "011fc5677bdf00d2d666693a188f6c5fee19c97be09dd7dccd892bd934a2dbb3",
+ "difference": "CRLF in local generated header; LF in Git blob"
+ }
+ ]
+}
diff --git a/field/HydraPulse/evidence/host-qualification-2026-09-08.json b/field/HydraPulse/evidence/host-qualification-2026-09-08.json
new file mode 100644
index 000000000..2ab58866c
--- /dev/null
+++ b/field/HydraPulse/evidence/host-qualification-2026-09-08.json
@@ -0,0 +1,165 @@
+[
+ {
+ "command": [
+ "/usr/bin/python3",
+ "-S",
+ "tools/validate_repo.py"
+ ],
+ "exit": 0,
+ "log": "host-qualification-00.log"
+ },
+ {
+ "command": [
+ "/usr/bin/python3",
+ "-S",
+ "tools/generate_presets.py",
+ "--check"
+ ],
+ "exit": 0,
+ "log": "host-qualification-01.log"
+ },
+ {
+ "command": [
+ "/usr/bin/python3",
+ "-S",
+ "-m",
+ "unittest",
+ "discover",
+ "-s",
+ "tests/python",
+ "-v"
+ ],
+ "exit": 0,
+ "log": "host-qualification-02.log"
+ },
+ {
+ "command": [
+ "cmake",
+ "-S",
+ ".",
+ "-B",
+ "build-host",
+ "-DCMAKE_BUILD_TYPE=Release"
+ ],
+ "exit": 0,
+ "log": "host-qualification-03.log"
+ },
+ {
+ "command": [
+ "cmake",
+ "--build",
+ "build-host",
+ "--parallel",
+ "2"
+ ],
+ "exit": 0,
+ "log": "host-qualification-04.log"
+ },
+ {
+ "command": [
+ "ctest",
+ "--test-dir",
+ "build-host",
+ "--output-on-failure"
+ ],
+ "exit": 0,
+ "log": "host-qualification-05.log"
+ },
+ {
+ "command": [
+ "cmake",
+ "-S",
+ ".",
+ "-B",
+ "build-ubsan",
+ "-DCMAKE_BUILD_TYPE=Debug",
+ "-DHPF_UBSAN_ONLY=ON"
+ ],
+ "exit": 0,
+ "log": "host-qualification-06.log"
+ },
+ {
+ "command": [
+ "cmake",
+ "--build",
+ "build-ubsan",
+ "--parallel",
+ "2"
+ ],
+ "exit": 0,
+ "log": "host-qualification-07.log"
+ },
+ {
+ "command": [
+ "ctest",
+ "--test-dir",
+ "build-ubsan",
+ "--output-on-failure"
+ ],
+ "exit": 0,
+ "log": "host-qualification-08.log"
+ },
+ {
+ "command": [
+ "cmake",
+ "-S",
+ ".",
+ "-B",
+ "build-sanitize",
+ "-DCMAKE_BUILD_TYPE=Debug",
+ "-DHPF_ENABLE_SANITIZERS=ON",
+ "-DCMAKE_CXX_COMPILER=clang++"
+ ],
+ "exit": 0,
+ "log": "host-qualification-09.log"
+ },
+ {
+ "command": [
+ "cmake",
+ "--build",
+ "build-sanitize",
+ "--parallel",
+ "2"
+ ],
+ "exit": 0,
+ "log": "host-qualification-10.log"
+ },
+ {
+ "command": [
+ "ctest",
+ "--test-dir",
+ "build-sanitize",
+ "--output-on-failure"
+ ],
+ "exit": 0,
+ "log": "host-qualification-11.log"
+ },
+ {
+ "command": [
+ "/mnt/c/Users/denko/Gemini/Antigravity/DVPE_Daisy-Visual-Programming-Environment/DaisyExamples/.worktrees/hydrapulse-integration-20260908/field/HydraPulse/build-host/render_scenarios",
+ "/mnt/c/Users/denko/Gemini/Antigravity/DVPE_Daisy-Visual-Programming-Environment/DaisyExamples/.worktrees/hydrapulse-integration-20260908/field/HydraPulse/artifacts/integration/renders-a"
+ ],
+ "exit": 0,
+ "log": "host-qualification-12.log"
+ },
+ {
+ "command": [
+ "/mnt/c/Users/denko/Gemini/Antigravity/DVPE_Daisy-Visual-Programming-Environment/DaisyExamples/.worktrees/hydrapulse-integration-20260908/field/HydraPulse/build-host/render_scenarios",
+ "/mnt/c/Users/denko/Gemini/Antigravity/DVPE_Daisy-Visual-Programming-Environment/DaisyExamples/.worktrees/hydrapulse-integration-20260908/field/HydraPulse/artifacts/integration/renders-b"
+ ],
+ "exit": 0,
+ "log": "host-qualification-13.log"
+ },
+ {
+ "command": [
+ "/usr/bin/python3",
+ "-S",
+ "tools/validate_audio.py",
+ "/mnt/c/Users/denko/Gemini/Antigravity/DVPE_Daisy-Visual-Programming-Environment/DaisyExamples/.worktrees/hydrapulse-integration-20260908/field/HydraPulse/artifacts/integration/renders-a",
+ "--repeat",
+ "/mnt/c/Users/denko/Gemini/Antigravity/DVPE_Daisy-Visual-Programming-Environment/DaisyExamples/.worktrees/hydrapulse-integration-20260908/field/HydraPulse/artifacts/integration/renders-b"
+ ],
+ "exit": 0,
+ "log": "host-qualification-14.log"
+ }
+]
diff --git a/field/HydraPulse/evidence/local/asan-ubsan.log b/field/HydraPulse/evidence/local/asan-ubsan.log
new file mode 100644
index 000000000..4b24d4d4c
--- /dev/null
+++ b/field/HydraPulse/evidence/local/asan-ubsan.log
@@ -0,0 +1,135 @@
+$ cmake -S /mnt/data/HydraPulse_DaisyExamples_candidate/field/HydraPulse -B /mnt/data/hpf_build_asan-ubsan -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHPF_ENABLE_SANITIZERS=ON
+-- The CXX compiler identification is GNU 14.2.0
+-- Detecting CXX compiler ABI info
+-- Detecting CXX compiler ABI info - done
+-- Check for working CXX compiler: /usr/bin/c++ - skipped
+-- Detecting CXX compile features
+-- Detecting CXX compile features - done
+-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
+-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
+-- Found Threads: TRUE
+-- Configuring done (0.3s)
+-- Generating done (0.0s)
+-- Build files have been written to: /mnt/data/hpf_build_asan-ubsan
+
+exit=0
+$ cmake --build /mnt/data/hpf_build_asan-ubsan --parallel 4
+[ 3%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/dsp/Voices.cpp.o[0m
+[ 6%] [32mBuilding CXX object CMakeFiles/contract_FieldTruth.dir/firmware/FieldTruth.cpp.o[0m
+[ 9%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/app/Engine.cpp.o[0m
+[ 12%] [32mBuilding CXX object CMakeFiles/contract_HydraPulse.dir/firmware/HydraPulse.cpp.o[0m
+[ 12%] Built target contract_FieldTruth
+[ 15%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/app/Controller.cpp.o[0m
+[ 15%] Built target contract_HydraPulse
+[ 18%] [32m[1mLinking CXX static library libhydrapulse_engine.a[0m
+[ 18%] Built target hydrapulse_engine
+[ 25%] [32mBuilding CXX object tests/native/CMakeFiles/test_pattern16.dir/test_pattern16.cpp.o[0m
+[ 25%] [32mBuilding CXX object tests/native/CMakeFiles/test_step_sequencer.dir/test_step_sequencer.cpp.o[0m
+[ 28%] [32mBuilding CXX object tests/native/CMakeFiles/test_sample_clock.dir/test_sample_clock.cpp.o[0m
+[ 31%] [32mBuilding CXX object CMakeFiles/render_scenarios.dir/tools/render_scenarios.cpp.o[0m
+[ 34%] [32m[1mLinking CXX executable test_sample_clock[0m
+[ 34%] Built target test_sample_clock
+[ 37%] [32mBuilding CXX object tests/native/CMakeFiles/test_control_pickup.dir/test_control_pickup.cpp.o[0m
+[ 40%] [32m[1mLinking CXX executable test_step_sequencer[0m
+[ 40%] Built target test_step_sequencer
+[ 43%] [32m[1mLinking CXX executable test_pattern16[0m
+[ 46%] [32mBuilding CXX object tests/native/CMakeFiles/test_regressions.dir/test_regressions.cpp.o[0m
+[ 46%] Built target test_pattern16
+[ 50%] [32mBuilding CXX object tests/native/CMakeFiles/test_groove_clock.dir/test_groove_clock.cpp.o[0m
+[ 53%] [32m[1mLinking CXX executable test_control_pickup[0m
+[ 53%] Built target test_control_pickup
+[ 56%] [32mBuilding CXX object tests/native/CMakeFiles/test_spsc.dir/test_spsc.cpp.o[0m
+[ 59%] [32m[1mLinking CXX executable test_groove_clock[0m
+[ 59%] Built target test_groove_clock
+[ 62%] [32m[1mLinking CXX executable test_regressions[0m
+[ 65%] [32mBuilding CXX object tests/native/CMakeFiles/test_engine.dir/test_engine.cpp.o[0m
+[ 65%] Built target test_regressions
+[ 68%] [32mBuilding CXX object tests/native/CMakeFiles/test_controller.dir/test_controller.cpp.o[0m
+[ 71%] [32m[1mLinking CXX executable render_scenarios[0m
+[ 71%] Built target render_scenarios
+[ 75%] [32m[1mLinking CXX executable test_spsc[0m
+[ 78%] [32mBuilding CXX object tests/native/CMakeFiles/test_dsp.dir/test_dsp.cpp.o[0m
+[ 78%] Built target test_spsc
+[ 81%] [32mBuilding CXX object tests/native/CMakeFiles/test_no_allocation.dir/test_no_allocation.cpp.o[0m
+[ 84%] [32m[1mLinking CXX executable test_engine[0m
+[ 84%] Built target test_engine
+[ 87%] [32mBuilding CXX object tests/native/CMakeFiles/test_near_rejects_nan.dir/test_near_rejects_nan.cpp.o[0m
+[ 90%] [32m[1mLinking CXX executable test_controller[0m
+[ 90%] Built target test_controller
+[ 93%] [32m[1mLinking CXX executable test_no_allocation[0m
+[ 96%] [32m[1mLinking CXX executable test_near_rejects_nan[0m
+[ 96%] Built target test_no_allocation
+[ 96%] Built target test_near_rejects_nan
+[100%] [32m[1mLinking CXX executable test_dsp[0m
+[100%] Built target test_dsp
+
+exit=0
+$ ctest --test-dir /mnt/data/hpf_build_asan-ubsan --output-on-failure
+Internal ctest changing into directory: /mnt/data/hpf_build_asan-ubsan
+Test project /mnt/data/hpf_build_asan-ubsan
+ Start 1: test_pattern16
+ 1/12 Test #1: test_pattern16 ...................***Failed 0.00 sec
+==2835==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 2: test_step_sequencer
+ 2/12 Test #2: test_step_sequencer ..............***Failed 0.00 sec
+==2836==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 3: test_sample_clock
+ 3/12 Test #3: test_sample_clock ................***Failed 0.00 sec
+==2837==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 4: test_control_pickup
+ 4/12 Test #4: test_control_pickup ..............***Failed 0.00 sec
+==2838==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 5: test_regressions
+ 5/12 Test #5: test_regressions .................***Failed 0.00 sec
+==2839==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 6: test_groove_clock
+ 6/12 Test #6: test_groove_clock ................***Failed 0.00 sec
+==2840==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 7: test_spsc
+ 7/12 Test #7: test_spsc ........................***Failed 0.00 sec
+==2841==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 8: test_engine
+ 8/12 Test #8: test_engine ......................***Failed 0.00 sec
+==2842==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 9: test_controller
+ 9/12 Test #9: test_controller ..................***Failed 0.00 sec
+==2843==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 10: test_dsp
+10/12 Test #10: test_dsp .........................***Failed 0.00 sec
+==2844==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 11: test_no_allocation
+11/12 Test #11: test_no_allocation ...............***Failed 0.00 sec
+==2845==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+
+ Start 12: test_near_rejects_nan
+12/12 Test #12: test_near_rejects_nan ............ Passed 0.00 sec
+
+8% tests passed[0;0m, [0;31m11 tests failed[0;0m out of 12
+
+Total Test time (real) = 0.03 sec
+
+The following tests FAILED:
+ [0;31m 1 - test_pattern16 (Failed)[0;0m
+ [0;31m 2 - test_step_sequencer (Failed)[0;0m
+ [0;31m 3 - test_sample_clock (Failed)[0;0m
+ [0;31m 4 - test_control_pickup (Failed)[0;0m
+ [0;31m 5 - test_regressions (Failed)[0;0m
+ [0;31m 6 - test_groove_clock (Failed)[0;0m
+ [0;31m 7 - test_spsc (Failed)[0;0m
+ [0;31m 8 - test_engine (Failed)[0;0m
+ [0;31m 9 - test_controller (Failed)[0;0m
+ [0;31m 10 - test_dsp (Failed)[0;0m
+ [0;31m 11 - test_no_allocation (Failed)[0;0m
+Errors while running CTest
+
+exit=8
diff --git a/field/HydraPulse/evidence/local/capabilities.json b/field/HydraPulse/evidence/local/capabilities.json
new file mode 100644
index 000000000..7bc6e5110
--- /dev/null
+++ b/field/HydraPulse/evidence/local/capabilities.json
@@ -0,0 +1,16 @@
+{
+ "date": "2026-09-07",
+ "tools": {
+ "git": true,
+ "cmake": true,
+ "g++": true,
+ "clang++": true,
+ "arm-none-eabi-g++": false,
+ "mmdc": false,
+ "clang-format": false
+ },
+ "github_write": "UNAVAILABLE",
+ "hardware": "UNAVAILABLE",
+ "mermaid_renderer": "NOT_RUN",
+ "target_fork_inspection": "HOLD public retrieval unsuccessful"
+}
diff --git a/field/HydraPulse/evidence/local/document-links.log b/field/HydraPulse/evidence/local/document-links.log
new file mode 100644
index 000000000..64b450efc
--- /dev/null
+++ b/field/HydraPulse/evidence/local/document-links.log
@@ -0,0 +1 @@
+PASS: checked local Markdown link targets in active README/docs; historical archive excluded.
diff --git a/field/HydraPulse/evidence/local/final-build-clang-asan.log b/field/HydraPulse/evidence/local/final-build-clang-asan.log
new file mode 100644
index 000000000..f96e16225
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-build-clang-asan.log
@@ -0,0 +1,57 @@
+$ cmake --build /mnt/data/hpf_build_clang_asan --parallel 2
+[ 2%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/dsp/Voices.cpp.o[0m
+[ 5%] [32mBuilding CXX object CMakeFiles/contract_FieldTruth.dir/firmware/FieldTruth.cpp.o[0m
+[ 8%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/app/Engine.cpp.o[0m
+[ 8%] Built target contract_FieldTruth
+[ 11%] [32mBuilding CXX object CMakeFiles/contract_HydraPulse.dir/firmware/HydraPulse.cpp.o[0m
+[ 13%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/app/Controller.cpp.o[0m
+[ 13%] Built target contract_HydraPulse
+[ 16%] [32m[1mLinking CXX static library libhydrapulse_engine.a[0m
+[ 16%] Built target hydrapulse_engine
+[ 19%] [32mBuilding CXX object CMakeFiles/render_scenarios.dir/tools/render_scenarios.cpp.o[0m
+[ 22%] [32mBuilding CXX object tests/native/CMakeFiles/test_pattern16.dir/test_pattern16.cpp.o[0m
+[ 25%] [32m[1mLinking CXX executable test_pattern16[0m
+[ 27%] [32m[1mLinking CXX executable render_scenarios[0m
+[ 27%] Built target test_pattern16
+[ 30%] [32mBuilding CXX object tests/native/CMakeFiles/test_step_sequencer.dir/test_step_sequencer.cpp.o[0m
+[ 30%] Built target render_scenarios
+[ 33%] [32mBuilding CXX object tests/native/CMakeFiles/test_sample_clock.dir/test_sample_clock.cpp.o[0m
+[ 36%] [32m[1mLinking CXX executable test_step_sequencer[0m
+[ 38%] [32m[1mLinking CXX executable test_sample_clock[0m
+[ 38%] Built target test_step_sequencer
+[ 38%] Built target test_sample_clock
+[ 41%] [32mBuilding CXX object tests/native/CMakeFiles/test_control_pickup.dir/test_control_pickup.cpp.o[0m
+[ 44%] [32mBuilding CXX object tests/native/CMakeFiles/test_regressions.dir/test_regressions.cpp.o[0m
+[ 47%] [32m[1mLinking CXX executable test_control_pickup[0m
+[ 50%] [32m[1mLinking CXX executable test_regressions[0m
+[ 50%] Built target test_control_pickup
+[ 50%] Built target test_regressions
+[ 52%] [32mBuilding CXX object tests/native/CMakeFiles/test_groove_clock.dir/test_groove_clock.cpp.o[0m
+[ 55%] [32mBuilding CXX object tests/native/CMakeFiles/test_spsc.dir/test_spsc.cpp.o[0m
+[ 58%] [32m[1mLinking CXX executable test_groove_clock[0m
+[ 61%] [32m[1mLinking CXX executable test_spsc[0m
+[ 61%] Built target test_groove_clock
+[ 63%] [32mBuilding CXX object tests/native/CMakeFiles/test_engine.dir/test_engine.cpp.o[0m
+[ 63%] Built target test_spsc
+[ 66%] [32mBuilding CXX object tests/native/CMakeFiles/test_controller.dir/test_controller.cpp.o[0m
+[ 69%] [32m[1mLinking CXX executable test_engine[0m
+[ 72%] [32m[1mLinking CXX executable test_controller[0m
+[ 72%] Built target test_engine
+[ 75%] [32mBuilding CXX object tests/native/CMakeFiles/test_dsp.dir/test_dsp.cpp.o[0m
+[ 75%] Built target test_controller
+[ 77%] [32mBuilding CXX object tests/native/CMakeFiles/test_no_allocation.dir/test_no_allocation.cpp.o[0m
+[ 80%] [32m[1mLinking CXX executable test_dsp[0m
+[ 83%] [32m[1mLinking CXX executable test_no_allocation[0m
+[ 83%] Built target test_dsp
+[ 86%] [32mBuilding CXX object tests/native/CMakeFiles/test_near_rejects_nan.dir/test_near_rejects_nan.cpp.o[0m
+[ 86%] Built target test_no_allocation
+[ 88%] [32mBuilding CXX object tests/native/CMakeFiles/test_transitions.dir/test_transitions.cpp.o[0m
+[ 91%] [32m[1mLinking CXX executable test_near_rejects_nan[0m
+[ 91%] Built target test_near_rejects_nan
+[ 97%] [32mBuilding CXX object tests/native/CMakeFiles/test_adapter_midi.dir/test_adapter_midi.cpp.o[0m
+[ 97%] [32m[1mLinking CXX executable test_transitions[0m
+[ 97%] Built target test_transitions
+[100%] [32m[1mLinking CXX executable test_adapter_midi[0m
+[100%] Built target test_adapter_midi
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-build-clang.log b/field/HydraPulse/evidence/local/final-build-clang.log
new file mode 100644
index 000000000..80cc173dd
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-build-clang.log
@@ -0,0 +1,57 @@
+$ cmake --build /mnt/data/hpf_build_clang --parallel 2
+[ 5%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/dsp/Voices.cpp.o[0m
+[ 5%] [32mBuilding CXX object CMakeFiles/contract_FieldTruth.dir/firmware/FieldTruth.cpp.o[0m
+[ 5%] Built target contract_FieldTruth
+[ 8%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/app/Engine.cpp.o[0m
+[ 11%] [32mBuilding CXX object CMakeFiles/contract_HydraPulse.dir/firmware/HydraPulse.cpp.o[0m
+[ 13%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/app/Controller.cpp.o[0m
+[ 13%] Built target contract_HydraPulse
+[ 16%] [32m[1mLinking CXX static library libhydrapulse_engine.a[0m
+[ 16%] Built target hydrapulse_engine
+[ 22%] [32mBuilding CXX object CMakeFiles/render_scenarios.dir/tools/render_scenarios.cpp.o[0m
+[ 22%] [32mBuilding CXX object tests/native/CMakeFiles/test_pattern16.dir/test_pattern16.cpp.o[0m
+[ 25%] [32m[1mLinking CXX executable test_pattern16[0m
+[ 25%] Built target test_pattern16
+[ 27%] [32mBuilding CXX object tests/native/CMakeFiles/test_step_sequencer.dir/test_step_sequencer.cpp.o[0m
+[ 30%] [32m[1mLinking CXX executable render_scenarios[0m
+[ 30%] Built target render_scenarios
+[ 33%] [32mBuilding CXX object tests/native/CMakeFiles/test_sample_clock.dir/test_sample_clock.cpp.o[0m
+[ 36%] [32m[1mLinking CXX executable test_step_sequencer[0m
+[ 36%] Built target test_step_sequencer
+[ 38%] [32mBuilding CXX object tests/native/CMakeFiles/test_control_pickup.dir/test_control_pickup.cpp.o[0m
+[ 41%] [32m[1mLinking CXX executable test_sample_clock[0m
+[ 41%] Built target test_sample_clock
+[ 44%] [32mBuilding CXX object tests/native/CMakeFiles/test_regressions.dir/test_regressions.cpp.o[0m
+[ 47%] [32m[1mLinking CXX executable test_control_pickup[0m
+[ 47%] Built target test_control_pickup
+[ 50%] [32mBuilding CXX object tests/native/CMakeFiles/test_groove_clock.dir/test_groove_clock.cpp.o[0m
+[ 52%] [32m[1mLinking CXX executable test_regressions[0m
+[ 52%] Built target test_regressions
+[ 55%] [32mBuilding CXX object tests/native/CMakeFiles/test_spsc.dir/test_spsc.cpp.o[0m
+[ 58%] [32m[1mLinking CXX executable test_groove_clock[0m
+[ 58%] Built target test_groove_clock
+[ 61%] [32mBuilding CXX object tests/native/CMakeFiles/test_engine.dir/test_engine.cpp.o[0m
+[ 63%] [32m[1mLinking CXX executable test_spsc[0m
+[ 63%] Built target test_spsc
+[ 66%] [32mBuilding CXX object tests/native/CMakeFiles/test_controller.dir/test_controller.cpp.o[0m
+[ 69%] [32m[1mLinking CXX executable test_engine[0m
+[ 69%] Built target test_engine
+[ 72%] [32mBuilding CXX object tests/native/CMakeFiles/test_dsp.dir/test_dsp.cpp.o[0m
+[ 75%] [32m[1mLinking CXX executable test_controller[0m
+[ 75%] Built target test_controller
+[ 77%] [32mBuilding CXX object tests/native/CMakeFiles/test_no_allocation.dir/test_no_allocation.cpp.o[0m
+[ 80%] [32m[1mLinking CXX executable test_dsp[0m
+[ 80%] Built target test_dsp
+[ 83%] [32mBuilding CXX object tests/native/CMakeFiles/test_near_rejects_nan.dir/test_near_rejects_nan.cpp.o[0m
+[ 86%] [32m[1mLinking CXX executable test_no_allocation[0m
+[ 86%] Built target test_no_allocation
+[ 88%] [32mBuilding CXX object tests/native/CMakeFiles/test_transitions.dir/test_transitions.cpp.o[0m
+[ 91%] [32m[1mLinking CXX executable test_near_rejects_nan[0m
+[ 91%] Built target test_near_rejects_nan
+[ 94%] [32mBuilding CXX object tests/native/CMakeFiles/test_adapter_midi.dir/test_adapter_midi.cpp.o[0m
+[ 97%] [32m[1mLinking CXX executable test_transitions[0m
+[ 97%] Built target test_transitions
+[100%] [32m[1mLinking CXX executable test_adapter_midi[0m
+[100%] Built target test_adapter_midi
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-build-release.log b/field/HydraPulse/evidence/local/final-build-release.log
new file mode 100644
index 000000000..efea34b16
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-build-release.log
@@ -0,0 +1,25 @@
+$ cmake --build /mnt/data/hpf_build_release --parallel 2
+[ 2%] [32mBuilding CXX object CMakeFiles/contract_FieldTruth.dir/firmware/FieldTruth.cpp.o[0m
+[ 13%] Built target hydrapulse_engine
+[ 16%] [32mBuilding CXX object CMakeFiles/contract_HydraPulse.dir/firmware/HydraPulse.cpp.o[0m
+[ 16%] Built target contract_FieldTruth
+[ 22%] Built target render_scenarios
+[ 27%] Built target test_pattern16
+[ 27%] Built target contract_HydraPulse
+[ 38%] Built target test_sample_clock
+[ 38%] Built target test_step_sequencer
+[ 50%] Built target test_control_pickup
+[ 50%] Built target test_regressions
+[ 55%] Built target test_groove_clock
+[ 61%] Built target test_spsc
+[ 72%] Built target test_engine
+[ 72%] Built target test_controller
+[ 77%] Built target test_dsp
+[ 83%] Built target test_no_allocation
+[ 88%] Built target test_near_rejects_nan
+[ 94%] Built target test_transitions
+[ 97%] [32mBuilding CXX object tests/native/CMakeFiles/test_adapter_midi.dir/test_adapter_midi.cpp.o[0m
+[100%] [32m[1mLinking CXX executable test_adapter_midi[0m
+[100%] Built target test_adapter_midi
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-build-ubsan.log b/field/HydraPulse/evidence/local/final-build-ubsan.log
new file mode 100644
index 000000000..4362f57d7
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-build-ubsan.log
@@ -0,0 +1,57 @@
+$ cmake --build /mnt/data/hpf_build_ubsan --parallel 2
+[ 2%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/dsp/Voices.cpp.o[0m
+[ 5%] [32mBuilding CXX object CMakeFiles/contract_FieldTruth.dir/firmware/FieldTruth.cpp.o[0m
+[ 8%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/app/Engine.cpp.o[0m
+[ 8%] Built target contract_FieldTruth
+[ 11%] [32mBuilding CXX object CMakeFiles/contract_HydraPulse.dir/firmware/HydraPulse.cpp.o[0m
+[ 13%] [32mBuilding CXX object CMakeFiles/hydrapulse_engine.dir/src/app/Controller.cpp.o[0m
+[ 13%] Built target contract_HydraPulse
+[ 16%] [32m[1mLinking CXX static library libhydrapulse_engine.a[0m
+[ 16%] Built target hydrapulse_engine
+[ 22%] [32mBuilding CXX object CMakeFiles/render_scenarios.dir/tools/render_scenarios.cpp.o[0m
+[ 22%] [32mBuilding CXX object tests/native/CMakeFiles/test_pattern16.dir/test_pattern16.cpp.o[0m
+[ 25%] [32m[1mLinking CXX executable test_pattern16[0m
+[ 25%] Built target test_pattern16
+[ 27%] [32mBuilding CXX object tests/native/CMakeFiles/test_step_sequencer.dir/test_step_sequencer.cpp.o[0m
+[ 30%] [32m[1mLinking CXX executable render_scenarios[0m
+[ 30%] Built target render_scenarios
+[ 33%] [32mBuilding CXX object tests/native/CMakeFiles/test_sample_clock.dir/test_sample_clock.cpp.o[0m
+[ 36%] [32m[1mLinking CXX executable test_step_sequencer[0m
+[ 36%] Built target test_step_sequencer
+[ 38%] [32mBuilding CXX object tests/native/CMakeFiles/test_control_pickup.dir/test_control_pickup.cpp.o[0m
+[ 41%] [32m[1mLinking CXX executable test_sample_clock[0m
+[ 41%] Built target test_sample_clock
+[ 44%] [32mBuilding CXX object tests/native/CMakeFiles/test_regressions.dir/test_regressions.cpp.o[0m
+[ 47%] [32m[1mLinking CXX executable test_control_pickup[0m
+[ 47%] Built target test_control_pickup
+[ 50%] [32mBuilding CXX object tests/native/CMakeFiles/test_groove_clock.dir/test_groove_clock.cpp.o[0m
+[ 52%] [32m[1mLinking CXX executable test_regressions[0m
+[ 52%] Built target test_regressions
+[ 55%] [32mBuilding CXX object tests/native/CMakeFiles/test_spsc.dir/test_spsc.cpp.o[0m
+[ 58%] [32m[1mLinking CXX executable test_groove_clock[0m
+[ 58%] Built target test_groove_clock
+[ 61%] [32mBuilding CXX object tests/native/CMakeFiles/test_engine.dir/test_engine.cpp.o[0m
+[ 63%] [32m[1mLinking CXX executable test_spsc[0m
+[ 63%] Built target test_spsc
+[ 66%] [32mBuilding CXX object tests/native/CMakeFiles/test_controller.dir/test_controller.cpp.o[0m
+[ 69%] [32m[1mLinking CXX executable test_engine[0m
+[ 69%] Built target test_engine
+[ 72%] [32mBuilding CXX object tests/native/CMakeFiles/test_dsp.dir/test_dsp.cpp.o[0m
+[ 75%] [32m[1mLinking CXX executable test_controller[0m
+[ 75%] Built target test_controller
+[ 77%] [32mBuilding CXX object tests/native/CMakeFiles/test_no_allocation.dir/test_no_allocation.cpp.o[0m
+[ 80%] [32m[1mLinking CXX executable test_dsp[0m
+[ 80%] Built target test_dsp
+[ 83%] [32mBuilding CXX object tests/native/CMakeFiles/test_near_rejects_nan.dir/test_near_rejects_nan.cpp.o[0m
+[ 86%] [32m[1mLinking CXX executable test_no_allocation[0m
+[ 86%] Built target test_no_allocation
+[ 88%] [32mBuilding CXX object tests/native/CMakeFiles/test_transitions.dir/test_transitions.cpp.o[0m
+[ 91%] [32m[1mLinking CXX executable test_near_rejects_nan[0m
+[ 91%] Built target test_near_rejects_nan
+[ 94%] [32mBuilding CXX object tests/native/CMakeFiles/test_adapter_midi.dir/test_adapter_midi.cpp.o[0m
+[ 97%] [32m[1mLinking CXX executable test_transitions[0m
+[ 97%] Built target test_transitions
+[100%] [32m[1mLinking CXX executable test_adapter_midi[0m
+[100%] Built target test_adapter_midi
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-configure-clang-asan.log b/field/HydraPulse/evidence/local/final-configure-clang-asan.log
new file mode 100644
index 000000000..f910f56a8
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-configure-clang-asan.log
@@ -0,0 +1,15 @@
+$ cmake -S /mnt/data/HydraPulse_DaisyExamples_candidate/field/HydraPulse -B /mnt/data/hpf_build_clang_asan -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug -DHPF_ENABLE_SANITIZERS=ON
+-- The CXX compiler identification is Clang 17.0.0
+-- Detecting CXX compiler ABI info
+-- Detecting CXX compiler ABI info - done
+-- Check for working CXX compiler: /usr/local/swift/usr/bin/clang++ - skipped
+-- Detecting CXX compile features
+-- Detecting CXX compile features - done
+-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
+-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
+-- Found Threads: TRUE
+-- Configuring done (0.5s)
+-- Generating done (0.0s)
+-- Build files have been written to: /mnt/data/hpf_build_clang_asan
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-configure-clang.log b/field/HydraPulse/evidence/local/final-configure-clang.log
new file mode 100644
index 000000000..797948353
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-configure-clang.log
@@ -0,0 +1,6 @@
+$ cmake -S /mnt/data/HydraPulse_DaisyExamples_candidate/field/HydraPulse -B /mnt/data/hpf_build_clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release
+-- Configuring done (0.0s)
+-- Generating done (0.0s)
+-- Build files have been written to: /mnt/data/hpf_build_clang
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-configure-release.log b/field/HydraPulse/evidence/local/final-configure-release.log
new file mode 100644
index 000000000..64b2f0d1b
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-configure-release.log
@@ -0,0 +1,6 @@
+$ cmake -S /mnt/data/HydraPulse_DaisyExamples_candidate/field/HydraPulse -B /mnt/data/hpf_build_release -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON
+-- Configuring done (0.0s)
+-- Generating done (0.0s)
+-- Build files have been written to: /mnt/data/hpf_build_release
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-configure-ubsan.log b/field/HydraPulse/evidence/local/final-configure-ubsan.log
new file mode 100644
index 000000000..7f88d0d87
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-configure-ubsan.log
@@ -0,0 +1,6 @@
+$ cmake -S /mnt/data/HydraPulse_DaisyExamples_candidate/field/HydraPulse -B /mnt/data/hpf_build_ubsan -DCMAKE_BUILD_TYPE=Debug -DHPF_UBSAN_ONLY=ON
+-- Configuring done (0.0s)
+-- Generating done (0.0s)
+-- Build files have been written to: /mnt/data/hpf_build_ubsan
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-ctest-clang-asan.log b/field/HydraPulse/evidence/local/final-ctest-clang-asan.log
new file mode 100644
index 000000000..ebe0a767e
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-ctest-clang-asan.log
@@ -0,0 +1,37 @@
+$ ctest --test-dir /mnt/data/hpf_build_clang_asan --output-on-failure
+Internal ctest changing into directory: /mnt/data/hpf_build_clang_asan
+Test project /mnt/data/hpf_build_clang_asan
+ Start 1: test_pattern16
+ 1/14 Test #1: test_pattern16 ................... Passed 0.01 sec
+ Start 2: test_step_sequencer
+ 2/14 Test #2: test_step_sequencer .............. Passed 0.01 sec
+ Start 3: test_sample_clock
+ 3/14 Test #3: test_sample_clock ................ Passed 0.04 sec
+ Start 4: test_control_pickup
+ 4/14 Test #4: test_control_pickup .............. Passed 0.01 sec
+ Start 5: test_regressions
+ 5/14 Test #5: test_regressions ................. Passed 0.01 sec
+ Start 6: test_groove_clock
+ 6/14 Test #6: test_groove_clock ................ Passed 0.46 sec
+ Start 7: test_spsc
+ 7/14 Test #7: test_spsc ........................ Passed 0.02 sec
+ Start 8: test_engine
+ 8/14 Test #8: test_engine ...................... Passed 0.11 sec
+ Start 9: test_controller
+ 9/14 Test #9: test_controller .................. Passed 0.01 sec
+ Start 10: test_dsp
+10/14 Test #10: test_dsp ......................... Passed 4.50 sec
+ Start 11: test_no_allocation
+11/14 Test #11: test_no_allocation ............... Passed 0.18 sec
+ Start 12: test_near_rejects_nan
+12/14 Test #12: test_near_rejects_nan ............ Passed 0.01 sec
+ Start 13: test_transitions
+13/14 Test #13: test_transitions ................. Passed 0.02 sec
+ Start 14: test_adapter_midi
+14/14 Test #14: test_adapter_midi ................ Passed 0.02 sec
+
+[0;32m100% tests passed[0;0m, 0 tests failed[0;0m out of 14
+
+Total Test time (real) = 5.40 sec
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-ctest-clang.log b/field/HydraPulse/evidence/local/final-ctest-clang.log
new file mode 100644
index 000000000..24a309c5e
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-ctest-clang.log
@@ -0,0 +1,37 @@
+$ ctest --test-dir /mnt/data/hpf_build_clang --output-on-failure
+Internal ctest changing into directory: /mnt/data/hpf_build_clang
+Test project /mnt/data/hpf_build_clang
+ Start 1: test_pattern16
+ 1/14 Test #1: test_pattern16 ................... Passed 0.00 sec
+ Start 2: test_step_sequencer
+ 2/14 Test #2: test_step_sequencer .............. Passed 0.00 sec
+ Start 3: test_sample_clock
+ 3/14 Test #3: test_sample_clock ................ Passed 0.01 sec
+ Start 4: test_control_pickup
+ 4/14 Test #4: test_control_pickup .............. Passed 0.00 sec
+ Start 5: test_regressions
+ 5/14 Test #5: test_regressions ................. Passed 0.00 sec
+ Start 6: test_groove_clock
+ 6/14 Test #6: test_groove_clock ................ Passed 0.14 sec
+ Start 7: test_spsc
+ 7/14 Test #7: test_spsc ........................ Passed 0.00 sec
+ Start 8: test_engine
+ 8/14 Test #8: test_engine ...................... Passed 0.01 sec
+ Start 9: test_controller
+ 9/14 Test #9: test_controller .................. Passed 0.00 sec
+ Start 10: test_dsp
+10/14 Test #10: test_dsp ......................... Passed 0.37 sec
+ Start 11: test_no_allocation
+11/14 Test #11: test_no_allocation ............... Passed 0.01 sec
+ Start 12: test_near_rejects_nan
+12/14 Test #12: test_near_rejects_nan ............ Passed 0.00 sec
+ Start 13: test_transitions
+13/14 Test #13: test_transitions ................. Passed 0.00 sec
+ Start 14: test_adapter_midi
+14/14 Test #14: test_adapter_midi ................ Passed 0.00 sec
+
+[0;32m100% tests passed[0;0m, 0 tests failed[0;0m out of 14
+
+Total Test time (real) = 0.57 sec
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-ctest-release.log b/field/HydraPulse/evidence/local/final-ctest-release.log
new file mode 100644
index 000000000..de6bc4f1d
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-ctest-release.log
@@ -0,0 +1,37 @@
+$ ctest --test-dir /mnt/data/hpf_build_release --output-on-failure
+Internal ctest changing into directory: /mnt/data/hpf_build_release
+Test project /mnt/data/hpf_build_release
+ Start 1: test_pattern16
+ 1/14 Test #1: test_pattern16 ................... Passed 0.00 sec
+ Start 2: test_step_sequencer
+ 2/14 Test #2: test_step_sequencer .............. Passed 0.00 sec
+ Start 3: test_sample_clock
+ 3/14 Test #3: test_sample_clock ................ Passed 0.01 sec
+ Start 4: test_control_pickup
+ 4/14 Test #4: test_control_pickup .............. Passed 0.00 sec
+ Start 5: test_regressions
+ 5/14 Test #5: test_regressions ................. Passed 0.00 sec
+ Start 6: test_groove_clock
+ 6/14 Test #6: test_groove_clock ................ Passed 0.01 sec
+ Start 7: test_spsc
+ 7/14 Test #7: test_spsc ........................ Passed 0.00 sec
+ Start 8: test_engine
+ 8/14 Test #8: test_engine ...................... Passed 0.01 sec
+ Start 9: test_controller
+ 9/14 Test #9: test_controller .................. Passed 0.00 sec
+ Start 10: test_dsp
+10/14 Test #10: test_dsp ......................... Passed 0.46 sec
+ Start 11: test_no_allocation
+11/14 Test #11: test_no_allocation ............... Passed 0.01 sec
+ Start 12: test_near_rejects_nan
+12/14 Test #12: test_near_rejects_nan ............ Passed 0.00 sec
+ Start 13: test_transitions
+13/14 Test #13: test_transitions ................. Passed 0.00 sec
+ Start 14: test_adapter_midi
+14/14 Test #14: test_adapter_midi ................ Passed 0.00 sec
+
+[0;32m100% tests passed[0;0m, 0 tests failed[0;0m out of 14
+
+Total Test time (real) = 0.52 sec
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-ctest-ubsan.log b/field/HydraPulse/evidence/local/final-ctest-ubsan.log
new file mode 100644
index 000000000..66a209188
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-ctest-ubsan.log
@@ -0,0 +1,37 @@
+$ ctest --test-dir /mnt/data/hpf_build_ubsan --output-on-failure
+Internal ctest changing into directory: /mnt/data/hpf_build_ubsan
+Test project /mnt/data/hpf_build_ubsan
+ Start 1: test_pattern16
+ 1/14 Test #1: test_pattern16 ................... Passed 0.01 sec
+ Start 2: test_step_sequencer
+ 2/14 Test #2: test_step_sequencer .............. Passed 0.00 sec
+ Start 3: test_sample_clock
+ 3/14 Test #3: test_sample_clock ................ Passed 0.04 sec
+ Start 4: test_control_pickup
+ 4/14 Test #4: test_control_pickup .............. Passed 0.00 sec
+ Start 5: test_regressions
+ 5/14 Test #5: test_regressions ................. Passed 0.00 sec
+ Start 6: test_groove_clock
+ 6/14 Test #6: test_groove_clock ................ Passed 0.49 sec
+ Start 7: test_spsc
+ 7/14 Test #7: test_spsc ........................ Passed 0.01 sec
+ Start 8: test_engine
+ 8/14 Test #8: test_engine ...................... Passed 0.07 sec
+ Start 9: test_controller
+ 9/14 Test #9: test_controller .................. Passed 0.00 sec
+ Start 10: test_dsp
+10/14 Test #10: test_dsp ......................... Passed 3.01 sec
+ Start 11: test_no_allocation
+11/14 Test #11: test_no_allocation ............... Passed 0.11 sec
+ Start 12: test_near_rejects_nan
+12/14 Test #12: test_near_rejects_nan ............ Passed 0.00 sec
+ Start 13: test_transitions
+13/14 Test #13: test_transitions ................. Passed 0.00 sec
+ Start 14: test_adapter_midi
+14/14 Test #14: test_adapter_midi ................ Passed 0.00 sec
+
+[0;32m100% tests passed[0;0m, 0 tests failed[0;0m out of 14
+
+Total Test time (real) = 3.77 sec
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-dependency-preflight.log b/field/HydraPulse/evidence/local/final-dependency-preflight.log
new file mode 100644
index 000000000..26e8edced
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-dependency-preflight.log
@@ -0,0 +1,4 @@
+$ /opt/pyvenv/bin/python -S tools/verify_dependencies.py .deps/libDaisy
+dependency verification: FAIL: libDaisy checkout missing: /mnt/data/HydraPulse_DaisyExamples_candidate/field/HydraPulse/.deps/libDaisy
+
+exit=1
diff --git a/field/HydraPulse/evidence/local/final-evidence-template-validation.log b/field/HydraPulse/evidence/local/final-evidence-template-validation.log
new file mode 100644
index 000000000..444be1a35
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-evidence-template-validation.log
@@ -0,0 +1,4 @@
+$ /opt/pyvenv/bin/python -S tools/validate_evidence.py hardware/p0_record.template.json
+evidence structure: PASS (not an independent measurement audit)
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-presets-validation.log b/field/HydraPulse/evidence/local/final-presets-validation.log
new file mode 100644
index 000000000..af41412a0
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-presets-validation.log
@@ -0,0 +1,4 @@
+$ /opt/pyvenv/bin/python -S tools/generate_presets.py --check
+presets: PASS
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-python-tests.log b/field/HydraPulse/evidence/local/final-python-tests.log
new file mode 100644
index 000000000..8ab10d169
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-python-tests.log
@@ -0,0 +1,46 @@
+$ /opt/pyvenv/bin/python -S -m unittest discover -s tests/python -v
+test_corrupted_preserved_r0_is_rejected (test_tools.DependencyAndRepoTests.test_corrupted_preserved_r0_is_rejected) ... ok
+test_missing_required_readme_is_rejected (test_tools.DependencyAndRepoTests.test_missing_required_readme_is_rejected) ... ok
+test_missing_toolchain_fails_loudly (test_tools.DependencyAndRepoTests.test_missing_toolchain_fails_loudly) ... ok
+test_repository_and_preserved_archive (test_tools.DependencyAndRepoTests.test_repository_and_preserved_archive) ... ok
+test_short_dependency_pin_rejected (test_tools.DependencyAndRepoTests.test_short_dependency_pin_rejected) ... ok
+test_source_fingerprint_is_sorted_and_stable (test_tools.DependencyAndRepoTests.test_source_fingerprint_is_sorted_and_stable) ... ok
+test_uninitialized_gitlink_rejected (test_tools.DependencyAndRepoTests.test_uninitialized_gitlink_rejected) ... ok
+test_wrong_real_checkout_head_rejected (test_tools.DependencyAndRepoTests.test_wrong_real_checkout_head_rejected) ... ok
+test_bad_type_and_duplicate_records_rejected (test_tools.EvidenceTests.test_bad_type_and_duplicate_records_rejected) ... ok
+test_complete_synthetic_record_is_valid_metadata (test_tools.EvidenceTests.test_complete_synthetic_record_is_valid_metadata) ... ok
+test_invalid_utc_rejected (test_tools.EvidenceTests.test_invalid_utc_rejected) ... ok
+test_missing_artifact_rejected (test_tools.EvidenceTests.test_missing_artifact_rejected) ... ok
+test_missing_hardware_fixture_rejected (test_tools.EvidenceTests.test_missing_hardware_fixture_rejected) ... ok
+test_modified_raw_artifact_rejected (test_tools.EvidenceTests.test_modified_raw_artifact_rejected) ... ok
+test_nonfinite_measurement_rejected (test_tools.EvidenceTests.test_nonfinite_measurement_rejected) ... ok
+test_not_run_template_is_valid_metadata (test_tools.EvidenceTests.test_not_run_template_is_valid_metadata) ... ok
+test_unsafe_raw_artifact_path_rejected (test_tools.EvidenceTests.test_unsafe_raw_artifact_path_rejected) ... ok
+test_addition_and_idempotence_preserve_unrelated (test_tools.InstallTests.test_addition_and_idempotence_preserve_unrelated) ... ok
+test_conflict_preflight_writes_nothing (test_tools.InstallTests.test_conflict_preflight_writes_nothing) ... ok
+test_destination_symlink_is_conflict (test_tools.InstallTests.test_destination_symlink_is_conflict) ... ok
+test_inventory_hash_mismatch_rejected (test_tools.InstallTests.test_inventory_hash_mismatch_rejected) ... ok
+test_inventory_out_of_scope_rejected (test_tools.InstallTests.test_inventory_out_of_scope_rejected) ... ok
+test_parent_file_is_conflict (test_tools.InstallTests.test_parent_file_is_conflict) ... ok
+test_source_symlink_and_traversal_rejected (test_tools.InstallTests.test_source_symlink_and_traversal_rejected) ... ok
+test_accent_without_note_rejected (test_tools.PresetTests.test_accent_without_note_rejected) ... ok
+test_bad_bank_shape_rejected (test_tools.PresetTests.test_bad_bank_shape_rejected) ... ok
+test_boolean_parameter_rejected (test_tools.PresetTests.test_boolean_parameter_rejected) ... ok
+test_duplicate_identity_rejected (test_tools.PresetTests.test_duplicate_identity_rejected) ... ok
+test_generated_table_matches (test_tools.PresetTests.test_generated_table_matches) ... ok
+test_infinite_tempo_rejected (test_tools.PresetTests.test_infinite_tempo_rejected) ... ok
+test_nan_parameter_rejected (test_tools.PresetTests.test_nan_parameter_rejected) ... ok
+test_duplicate_field_rejected (test_tools.TelemetryTests.test_duplicate_field_rejected) ... ok
+test_key_and_new_split_record_kinds (test_tools.TelemetryTests.test_key_and_new_split_record_kinds) ... ok
+test_key_outside_hardware_rejected (test_tools.TelemetryTests.test_key_outside_hardware_rejected) ... ok
+test_logger_buffer_maximum_widths (test_tools.TelemetryTests.test_logger_buffer_maximum_widths) ... ok
+test_nontelemetry_ignored (test_tools.TelemetryTests.test_nontelemetry_ignored) ... ok
+test_summary_preserves_invalid_status (test_tools.TelemetryTests.test_summary_preserves_invalid_status) ... ok
+test_unknown_record_rejected (test_tools.TelemetryTests.test_unknown_record_rejected) ... ok
+
+----------------------------------------------------------------------
+Ran 38 tests in 0.050s
+
+OK
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-render-first.log b/field/HydraPulse/evidence/local/final-render-first.log
new file mode 100644
index 000000000..e2606c302
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-render-first.log
@@ -0,0 +1,3 @@
+$ /mnt/data/hpf_build_release/render_scenarios /mnt/data/HydraPulse_host_demos
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-render-repeat.log b/field/HydraPulse/evidence/local/final-render-repeat.log
new file mode 100644
index 000000000..75946f155
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-render-repeat.log
@@ -0,0 +1,3 @@
+$ /mnt/data/hpf_build_release/render_scenarios /mnt/data/HydraPulse_host_demos_repeat
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-render-validation.log b/field/HydraPulse/evidence/local/final-render-validation.log
new file mode 100644
index 000000000..9e6fb3947
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-render-validation.log
@@ -0,0 +1,4 @@
+$ /opt/pyvenv/bin/python -S tools/validate_audio.py /mnt/data/HydraPulse_host_demos --repeat /mnt/data/HydraPulse_host_demos_repeat
+host sonic scenarios: PASS; listening, aliasing and hardware audio remain separate gates
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-repository-validation.log b/field/HydraPulse/evidence/local/final-repository-validation.log
new file mode 100644
index 000000000..4f1d012f5
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-repository-validation.log
@@ -0,0 +1,5 @@
+$ /opt/pyvenv/bin/python -S tools/validate_repo.py
+repository/provenance format, R0 preservation and Mermaid pair consistency: PASS
+upstream checkout verification and Mermaid rendering are separate checks
+
+exit=0
diff --git a/field/HydraPulse/evidence/local/final-target-preflight.log b/field/HydraPulse/evidence/local/final-target-preflight.log
new file mode 100644
index 000000000..dba34d013
--- /dev/null
+++ b/field/HydraPulse/evidence/local/final-target-preflight.log
@@ -0,0 +1,4 @@
+$ /opt/pyvenv/bin/python -S tools/build_target.py --libdaisy .deps/libDaisy
+ARM build: FAIL: missing tools: arm-none-eabi-gcc, arm-none-eabi-g++, arm-none-eabi-objcopy, arm-none-eabi-size, arm-none-eabi-readelf; use the documented ARM environment, not the adapter stub
+
+exit=1
diff --git a/field/HydraPulse/evidence/local/manual-pdf-qa.log b/field/HydraPulse/evidence/local/manual-pdf-qa.log
new file mode 100644
index 000000000..edeaeeccb
--- /dev/null
+++ b/field/HydraPulse/evidence/local/manual-pdf-qa.log
@@ -0,0 +1,4 @@
+PASS: final 18-page PDF generated from active USER_MANUAL/QUICK_REFERENCE/PRESETS.
+All-page layout contact sheet inspected; final panel/tutorial pages rendered and inspected with pdftoppm.
+Page text bounding boxes and required chapter/preset names checked. No outside-page text found.
+PDF is a separate delivery artifact, not a redistributed commercial reference manual.
diff --git a/field/HydraPulse/evidence/local/negative-helper-expected-failure.log b/field/HydraPulse/evidence/local/negative-helper-expected-failure.log
new file mode 100644
index 000000000..9ae235143
--- /dev/null
+++ b/field/HydraPulse/evidence/local/negative-helper-expected-failure.log
@@ -0,0 +1,4 @@
+$ /mnt/data/hpf_build_clang_asan/tests/native/test_near_rejects_nan
+/mnt/data/HydraPulse_DaisyExamples_candidate/field/HydraPulse/tests/native/test_near_rejects_nan.cpp:5 CHECK_NEAR failed: std::numeric_limits::quiet_NaN() vs 0.0f (nan vs 0, tol=0.1)
+
+exit=1
diff --git a/field/HydraPulse/evidence/local/results.json b/field/HydraPulse/evidence/local/results.json
new file mode 100644
index 000000000..dbc40ca09
--- /dev/null
+++ b/field/HydraPulse/evidence/local/results.json
@@ -0,0 +1,108 @@
+{
+ "schema_version": 1,
+ "date": "2026-09-07",
+ "candidate": "HydraPulse Field 0.2.0",
+ "runtime_source_sha256": "7aa39d796ad68faea522189185c2f451230aa92a77b5996aa73af942c78d41f8",
+ "source_fingerprint_basis": "Makefile, dependency lock, build helper and all src/firmware .cpp/.h",
+ "checks": [
+ {
+ "id": "gcc-release",
+ "classification": "VERIFIED",
+ "result": "PASS",
+ "tests": 14,
+ "log": "final-ctest-release.log"
+ },
+ {
+ "id": "gcc-ubsan",
+ "classification": "VERIFIED",
+ "result": "PASS",
+ "tests": 14,
+ "log": "final-ctest-ubsan.log"
+ },
+ {
+ "id": "clang-release",
+ "classification": "VERIFIED",
+ "result": "PASS",
+ "tests": 14,
+ "log": "final-ctest-clang.log"
+ },
+ {
+ "id": "clang-asan-ubsan",
+ "classification": "VERIFIED",
+ "result": "PASS",
+ "tests": 14,
+ "log": "final-ctest-clang-asan.log"
+ },
+ {
+ "id": "python",
+ "classification": "VERIFIED",
+ "result": "PASS",
+ "tests": 38,
+ "log": "final-python-tests.log"
+ },
+ {
+ "id": "gcc-asan-earlier-route",
+ "classification": "HOLD",
+ "result": "runtime library startup conflict",
+ "log": "asan-ubsan.log"
+ },
+ {
+ "id": "host-audio",
+ "classification": "VERIFIED",
+ "result": "8 scenarios repeat identically; PCM gates pass",
+ "log": "final-render-validation.log"
+ },
+ {
+ "id": "actual-arm",
+ "classification": "NOT_RUN",
+ "result": "preflight rejected absent ARM tools and libDaisy checkout",
+ "log": "final-target-preflight.log"
+ },
+ {
+ "id": "hardware-p0-p1",
+ "classification": "NOT_RUN",
+ "result": "no physical Field fixture"
+ },
+ {
+ "id": "github-push-ci",
+ "classification": "NOT_RUN",
+ "result": "no GitHub write capability"
+ },
+ {
+ "id": "mermaid-render",
+ "classification": "NOT_RUN",
+ "result": "no actual Mermaid CLI; source/fence consistency only"
+ },
+ {
+ "id": "manual-pdf",
+ "classification": "VERIFIED",
+ "result": "18 pages; rendered visual QA and text bounds",
+ "log": "manual-pdf-qa.log"
+ }
+ ],
+ "runtime_files": {
+ "Makefile": "b1a647c995c3359d4c100f3a715ac1ec050c04ad42eb774b26eb69e523deee4e",
+ "deps.lock.json": "c59db8c327b09cfe2cee78d0d32fb5f8af68218c282c13ede085c0e401b4f61d",
+ "firmware/FieldAdapter.h": "856bcc26f00812622b09ece8c87129bc38c40c835c2448a764b69555ff0c444b",
+ "firmware/FieldTruth.cpp": "20ef270a84a44c7dac0d5691b65467230278f6426c095425809488f4f0684a6b",
+ "firmware/HydraPulse.cpp": "17311d8cac722674c1d6f3fa9c5211808c1d7a8b868ff050ccf1822c7c70dbdf",
+ "src/app/Controller.cpp": "9feb520976e966e5453745a68774d3684b028f6d0c9824368aa53a0e57bf3987",
+ "src/app/Controller.h": "bc0f96e53567fdc6ef3ecd5a1582a2fffdcaf1406ce60fbe347dbb2d2fdb7407",
+ "src/app/Engine.cpp": "e35bbb8cbf94b7298509d321938d507f98d934dc0a5e14da39e1bbbfdbb07987",
+ "src/app/Engine.h": "560547d9a02df26a4bdb042982ef8d96a3bc5c71a81dbb99520157ddea599170",
+ "src/app/Presets.generated.h": "1bf775a4de76128d88ab42fc095a44b62de60070115b8d9c3a5a2ded8815ac43",
+ "src/app/Types.h": "2668c6c1aa27955f8471a81e26c939aa1f94c1bd4c22930e79d6e3167035bd8d",
+ "src/core/ControlPickup.h": "ee550a1ee797376b78d0ab5a7fc108add3699fc2d7dad928f1e550afcea8f87a",
+ "src/core/GrooveClock.h": "5e37b5c3b08eba016f2bc382f81fdb802a37d67731e6422d7e291c37ef7e5a90",
+ "src/core/OutputTestArm.h": "dd9dda4f7d7769cf9c7df7254c899b12c4b3c278574c1292cebbeb3ce8891063",
+ "src/core/Pattern16.h": "1e18985219ba1ea6c5b4d66eaf223098483b7457af945217b3a07145c8ea70a6",
+ "src/core/SampleAccurateStepClock.h": "3dd3293680f014081d94dd8c4d158e3e13653353870f6bf766a6ea50ddd589b1",
+ "src/core/SpscQueue.h": "b99421411f208fe69e4a100bb5a3615b48c39d17a99407c13f4ef8f54f13d42a",
+ "src/core/StepSequencer.h": "ee6dd997505023870b534f3320df8b50a2fbc3770c28d4b1758a6942bd81b55f",
+ "src/dsp/Primitives.h": "3e9aa5e019094be38aa5c96b2babe35934cc6d44ceadede3db094012110f9fac",
+ "src/dsp/Voices.cpp": "183ced39a1bdcea96baee2cf7d1bc42b8a8b5ccbb033e1df1690e078dedae6e8",
+ "src/dsp/Voices.h": "644c98395f04d5add62ae352cb1a9d1e7e39b38ea42c92b24d5a235f00a3ed6b",
+ "tools/build_target.py": "880efef9d9daffc542a47ee2c76ca3dc71d1e436494a4ecbfb0a7ae51a40fcfc"
+ },
+ "remote_commit": null
+}
diff --git a/field/HydraPulse/evidence/local/sonic_metrics.csv b/field/HydraPulse/evidence/local/sonic_metrics.csv
new file mode 100644
index 000000000..57ada0aa0
--- /dev/null
+++ b/field/HydraPulse/evidence/local/sonic_metrics.csv
@@ -0,0 +1,9 @@
+preset,frames,peak,rms,dc,max_delta,tail_peak,faults
+0,480000,0,0,0,0,0,0
+1,480000,0.09677,0.00765006,-1.68702e-06,0.0353578,0,0
+2,480000,0.119169,0.00653665,-2.05323e-06,0.0698876,0,0
+3,480000,0.165765,0.00945537,-2.35373e-06,0.0740981,0,0
+4,480000,0.166809,0.0127806,-2.21398e-06,0.103022,0,0
+5,480000,0.20142,0.0134012,-1.89008e-06,0.0688161,0,0
+6,480000,0.276446,0.0323573,-6.54579e-06,0.164896,0,0
+7,480000,0.300022,0.0343962,-4.06048e-06,0.241626,0,0
diff --git a/field/HydraPulse/evidence/local/tool_versions.json b/field/HydraPulse/evidence/local/tool_versions.json
new file mode 100644
index 000000000..448e8ebbc
--- /dev/null
+++ b/field/HydraPulse/evidence/local/tool_versions.json
@@ -0,0 +1,8 @@
+{
+ "gcc": "g++ (Debian 14.2.0-19) 14.2.0\nCopyright (C) 2024 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions. There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.",
+ "clang": "clang version 17.0.0 (https://github.com/swiftlang/llvm-project.git 10999b6d034fe318f3d56c83bddb6572593a8bb0)\nTarget: x86_64-unknown-linux-gnu\nThread model: posix\nInstalledDir: /usr/local/swift/usr/bin",
+ "cmake": "cmake version 3.31.6\n\nCMake suite maintained and supported by Kitware (kitware.com/cmake).",
+ "git": "git version 2.47.3",
+ "python": "3.13.5 (main, Jul 15 2026, 20:25:40) [GCC 14.2.0]",
+ "platform": "linux"
+}
diff --git a/field/HydraPulse/evidence/local/transitions-after-repair.log b/field/HydraPulse/evidence/local/transitions-after-repair.log
new file mode 100644
index 000000000..3a464e53d
--- /dev/null
+++ b/field/HydraPulse/evidence/local/transitions-after-repair.log
@@ -0,0 +1 @@
+retrigger first-sample deviation 0.0320582; parameter first-sample deviation 0.0263815
diff --git a/field/HydraPulse/evidence/local/transitions-before-repair.log b/field/HydraPulse/evidence/local/transitions-before-repair.log
new file mode 100644
index 000000000..21e9cfb68
--- /dev/null
+++ b/field/HydraPulse/evidence/local/transitions-before-repair.log
@@ -0,0 +1,2 @@
+retrigger first-sample deviation 1.42942; parameter first-sample deviation 0.210695
+/mnt/data/HydraPulse_DaisyExamples_candidate/field/HydraPulse/tests/native/test_transitions.cpp:32 CHECK failed: max_retrigger_error<.05f
diff --git a/field/HydraPulse/evidence/local/workflow-structure.log b/field/HydraPulse/evidence/local/workflow-structure.log
new file mode 100644
index 000000000..36b404225
--- /dev/null
+++ b/field/HydraPulse/evidence/local/workflow-structure.log
@@ -0,0 +1 @@
+PASS: YAML parsed; job/dependency/no-flash structural assertions. Actions NOT_RUN.
diff --git a/field/HydraPulse/firmware/FieldAdapter.h b/field/HydraPulse/firmware/FieldAdapter.h
new file mode 100644
index 000000000..04d6499ce
--- /dev/null
+++ b/field/HydraPulse/firmware/FieldAdapter.h
@@ -0,0 +1,156 @@
+#pragma once
+#include "daisy_field.h"
+#include "src/app/Types.h"
+#include "util/CpuLoadMeter.h"
+#include
+#include
+#include
+#include
+#include
+
+#ifndef HPF_BUILD_ID
+#define HPF_BUILD_ID "unrecorded"
+#endif
+#ifndef HPF_TIMING_TESTPOINT
+#define HPF_TIMING_TESTPOINT 1
+#endif
+
+namespace hydrapulse::field
+{
+static_assert(sizeof(daisy::MidiMessageType) == sizeof(int),
+ "Use default-width BSP enums consistently for app and libDaisy");
+constexpr std::uint32_t kSampleRate = 48000, kBlockSize = 48;
+// Provisional source-order mapping. P0 must qualify the physical mapping on the actual Field.
+constexpr std::array kLogicalToBspKey{
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}};
+constexpr std::array kLogicalToLed{
+ {daisy::DaisyField::LED_KEY_A1, daisy::DaisyField::LED_KEY_A2, daisy::DaisyField::LED_KEY_A3,
+ daisy::DaisyField::LED_KEY_A4, daisy::DaisyField::LED_KEY_A5, daisy::DaisyField::LED_KEY_A6,
+ daisy::DaisyField::LED_KEY_A7, daisy::DaisyField::LED_KEY_A8, daisy::DaisyField::LED_KEY_B1,
+ daisy::DaisyField::LED_KEY_B2, daisy::DaisyField::LED_KEY_B3, daisy::DaisyField::LED_KEY_B4,
+ daisy::DaisyField::LED_KEY_B5, daisy::DaisyField::LED_KEY_B6, daisy::DaisyField::LED_KEY_B7,
+ daisy::DaisyField::LED_KEY_B8}};
+inline void OutputsOff(daisy::DaisyField &hw)
+{
+ hw.SetCvOut1(0);
+ hw.SetCvOut2(0);
+ dsy_gpio_write(&hw.gate_out, 0);
+}
+
+inline InputFrame ReadControls(daisy::DaisyField &hw)
+{
+ hw.ProcessAnalogControls();
+ hw.ProcessDigitalControls();
+ InputFrame f{};
+ for (std::size_t i = 0; i < 8; ++i)
+ f.knobs[i] = hw.GetKnobValue(i);
+ for (std::size_t i = 0; i < 16; ++i)
+ if (hw.KeyboardState(kLogicalToBspKey[i]))
+ f.keys |= std::uint16_t(1u << i);
+ f.play = hw.sw[daisy::DaisyField::SW_1].Pressed();
+ f.shift = hw.sw[daisy::DaisyField::SW_2].Pressed();
+ return f;
+}
+inline void Text(daisy::DaisyField &hw, std::uint8_t line, const char *text)
+{
+ hw.display.SetCursor(0, static_cast(line * 8u));
+ char bounded[22];
+ std::snprintf(bounded, sizeof(bounded), "%.21s", text);
+ hw.display.WriteString(bounded, Font_6x8, true);
+}
+
+struct BootRecord
+{
+ unsigned board{0}, memory{0}, bootloader{0};
+ unsigned cpu_hz{0}, tick_hz{0};
+ void Capture(daisy::DaisyField &hw)
+ {
+ board = static_cast(hw.seed.CheckBoardVersion());
+ memory = static_cast(daisy::System::GetProgramMemoryRegion());
+ bootloader = static_cast(daisy::System::GetBootloaderVersion());
+ cpu_hz = daisy::System::GetSysClkFreq();
+ tick_hz = daisy::System::GetTickFreq();
+ }
+ void Print(daisy::DaisyField &hw, const char *app) const
+ {
+ // This is replayable boot metadata, not an electrical reset-cause measurement.
+ // Keep each record within libDaisy's documented 128-byte logger buffer.
+ hw.seed.PrintLine("[HPF] boot app=%.5s build=%.12s sr=48000 block=48 reset=unknown", app,
+ HPF_BUILD_ID);
+ hw.seed.PrintLine("[HPF] system board=%u memory=%u loader=%u", board, memory, bootloader);
+ hw.seed.PrintLine("[HPF] timing cpu=%u tick=%u", cpu_hz, tick_hz);
+ }
+};
+
+class CallbackMeter
+{
+ public:
+ void Init()
+ {
+ meter_.Init(float(kSampleRate), int(kBlockSize));
+ ticks_per_block_ = daisy::System::GetTickFreq() / 1000u;
+ }
+ void Begin(daisy::DaisyField &hw)
+ {
+#if HPF_TIMING_TESTPOINT
+ hw.seed.SetTestPoint(true);
+#endif
+ const auto now = daisy::System::GetTick();
+ if (count_ && now - previous_start_ > ticks_per_block_ + ticks_per_block_ / 2u)
+ ++late_;
+ previous_start_ = now;
+ meter_.OnBlockStart();
+ }
+ void End(daisy::DaisyField &hw)
+ {
+ meter_.OnBlockEnd();
+ if (daisy::System::GetTick() - previous_start_ > ticks_per_block_)
+ ++overruns_;
+ ++count_;
+#if HPF_TIMING_TESTPOINT
+ hw.seed.SetTestPoint(false);
+#else
+ (void)hw;
+#endif
+ }
+ std::uint32_t Count() const
+ {
+ return count_;
+ }
+ std::uint32_t MaxPermille() const
+ {
+ const float load = meter_.GetMaxCpuLoad();
+ return std::isfinite(load) ? static_cast(std::clamp(load * 1000.0f, 0.0f, 1000000.0f))
+ : 0;
+ }
+ std::uint32_t Overruns() const
+ {
+ return overruns_;
+ }
+ std::uint32_t LateStarts() const
+ {
+ return late_;
+ }
+
+ private:
+ daisy::CpuLoadMeter meter_;
+ std::uint32_t count_{0}, overruns_{0}, late_{0}, previous_start_{0}, ticks_per_block_{0};
+};
+inline void InitHardware(daisy::DaisyField &hw)
+{
+ hw.Init(false);
+ OutputsOff(hw);
+ hw.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_48KHZ);
+ hw.SetAudioBlockSize(kBlockSize);
+ hw.seed.SetTestPoint(false);
+ hw.seed.StartLog(false);
+ hw.StartAdc();
+ // Flush the keyboard history and settle ADC filtering before constructing UI state.
+ for (unsigned i = 0; i < 32; ++i)
+ {
+ (void)ReadControls(hw);
+ daisy::System::Delay(1);
+ }
+ hw.midi.StartReceive();
+}
+} // namespace hydrapulse::field
diff --git a/field/HydraPulse/firmware/FieldTruth.cpp b/field/HydraPulse/firmware/FieldTruth.cpp
new file mode 100644
index 000000000..9e827fc76
--- /dev/null
+++ b/field/HydraPulse/firmware/FieldTruth.cpp
@@ -0,0 +1,199 @@
+#include "daisy_field.h"
+#include "firmware/FieldAdapter.h"
+#include "src/core/OutputTestArm.h"
+#include "src/core/SpscQueue.h"
+#include "src/dsp/Primitives.h"
+#include
+#include
+
+namespace
+{
+daisy::DaisyField hw;
+hydrapulse::field::CallbackMeter meter;
+hydrapulse::field::BootRecord boot;
+hydrapulse::core::OutputTestArm output_arm;
+struct TruthSnapshot
+{
+ std::array knobs{};
+ std::array cv{};
+ std::uint16_t keys{0}, seen{0}, dac1{0}, dac2{0};
+ std::uint32_t cb{0}, load{0}, overruns{0}, late{0}, gate_rises{0}, drops{0};
+ float peak_l{0}, peak_r{0};
+ bool gate{false}, armed_output{false}, vegas{false};
+};
+struct KeyEvent
+{
+ std::uint8_t raw_index{0};
+ bool down{false};
+ std::uint32_t callback{0};
+};
+hydrapulse::core::SpscQueue snapshots;
+hydrapulse::core::SpscQueue keys;
+std::uint32_t blocks = 0, drops = 0, gate_rises = 0;
+std::uint16_t previous_keys = 0, seen = 0;
+bool previous_gate = false;
+float peak_l = 0, peak_r = 0;
+
+void AudioCallback(daisy::AudioHandle::InputBuffer input, daisy::AudioHandle::OutputBuffer output,
+ std::size_t size)
+{
+ meter.Begin(hw);
+ const auto f = hydrapulse::field::ReadControls(hw);
+ std::uint16_t raw = 0;
+ for (std::uint8_t i = 0; i < 16; ++i)
+ {
+ if (hw.KeyboardState(i))
+ raw |= std::uint16_t(1u << i);
+ if (((raw ^ previous_keys) & (1u << i)) != 0)
+ {
+ const bool down = (raw & (1u << i)) != 0;
+ if (down)
+ seen |= std::uint16_t(1u << i);
+ if (!keys.Push({i, down, meter.Count()}))
+ ++drops;
+ }
+ }
+ previous_keys = raw;
+ const bool gate = hw.gate_in.State();
+ if (gate && !previous_gate)
+ ++gate_rises;
+ previous_gate = gate;
+ const bool test = output_arm.Update(f.play, f.shift);
+ constexpr std::uint16_t codes[5] = {0, 1024, 2048, 3072, 4095};
+ const auto c1 = codes[std::min(4u, unsigned(hydrapulse::dsp::Unit(f.knobs[0]) * 5.0f))];
+ const auto c2 = codes[std::min(4u, unsigned(hydrapulse::dsp::Unit(f.knobs[1]) * 5.0f))];
+ hw.SetCvOut1(test ? c1 : 0);
+ hw.SetCvOut2(test ? c2 : 0);
+ dsy_gpio_write(&hw.gate_out, test ? 1 : 0);
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ // Deliberately no gain/limiter: measure the actual passthrough path with low-level stimulus.
+ output[0][i] = std::isfinite(input[0][i]) ? input[0][i] : 0;
+ output[1][i] = std::isfinite(input[1][i]) ? input[1][i] : 0;
+ peak_l = std::max(peak_l, std::fabs(output[0][i]));
+ peak_r = std::max(peak_r, std::fabs(output[1][i]));
+ }
+ if (++blocks % 40u == 0)
+ {
+ TruthSnapshot s{};
+ s.knobs = f.knobs;
+ for (std::size_t i = 0; i < 4; ++i)
+ s.cv[i] = hw.GetCvValue(i);
+ s.keys = raw;
+ s.seen = seen;
+ s.dac1 = test ? c1 : 0;
+ s.dac2 = test ? c2 : 0;
+ s.gate = gate;
+ s.armed_output = test;
+ s.vegas = f.shift && !f.play;
+ s.cb = meter.Count();
+ s.load = meter.MaxPermille();
+ s.overruns = meter.Overruns();
+ s.late = meter.LateStarts();
+ s.gate_rises = gate_rises;
+ s.drops = drops;
+ s.peak_l = peak_l;
+ s.peak_r = peak_r;
+ if (!snapshots.Push(s))
+ ++drops;
+ peak_l = peak_r = 0;
+ }
+ meter.End(hw);
+}
+void Draw(const TruthSnapshot &s)
+{
+ if (s.vegas)
+ {
+ hw.VegasMode();
+ return;
+ }
+ char line[32];
+ hw.display.Fill(false);
+ hydrapulse::field::Text(hw, 0, "HYDRAPULSE FIELD TRUTH");
+ std::snprintf(line, sizeof(line), "KEY:%04X SEEN:%04X", unsigned(s.keys), unsigned(s.seen));
+ hydrapulse::field::Text(hw, 1, line);
+ std::snprintf(line, sizeof(line), "GATE:%u EDGES:%lu", unsigned(s.gate),
+ static_cast(s.gate_rises));
+ hydrapulse::field::Text(hw, 2, line);
+ std::snprintf(line, sizeof(line), "DAC:%04u %04u", unsigned(s.dac1), unsigned(s.dac2));
+ hydrapulse::field::Text(hw, 3, line);
+ hydrapulse::field::Text(hw, 4, s.armed_output ? "OUTPUT TEST ACTIVE" : "OUTPUTS OFF");
+ std::snprintf(line, sizeof(line), "CPUmax %u.%u%%", unsigned(s.load / 10u), unsigned(s.load % 10u));
+ hydrapulse::field::Text(hw, 5, line);
+ hydrapulse::field::Text(hw, 6, "SW1 HOLD OUTPUT TEST");
+ hydrapulse::field::Text(hw, 7, "SW2 LED/OLED TEST");
+ hw.display.Update();
+ for (std::size_t i = 0; i < daisy::DaisyField::LED_LAST; ++i)
+ hw.led_driver.SetLed(i, 0.0f);
+ for (std::size_t i = 0; i < 16; ++i)
+ hw.led_driver.SetLed(hydrapulse::field::kLogicalToLed[i], (s.keys & (1u << i)) ? 0.6f : 0);
+ hw.led_driver.SwapBuffersAndTransmit();
+}
+int Norm(float x)
+{
+ return std::isfinite(x) ? int(std::clamp(x, -4.0f, 4.0f) * 10000.0f) : 0;
+}
+} // namespace
+int main()
+{
+ hydrapulse::field::InitHardware(hw);
+ boot.Capture(hw);
+ meter.Init();
+ hw.StartAudio(AudioCallback);
+ TruthSnapshot current{};
+ std::uint32_t last_log = 0, last_boot = 0, midi_count = 0;
+ unsigned last_type = 0, last_channel = 0, last_data0 = 0, last_data1 = 0;
+ while (true)
+ {
+ hw.midi.Listen();
+ for (unsigned i = 0; i < 32 && hw.midi.HasEvents(); ++i)
+ {
+ const auto event = hw.midi.PopEvent();
+ ++midi_count;
+ last_type = unsigned(event.type);
+ last_channel = unsigned(event.channel);
+ last_data0 = event.data[0];
+ last_data1 = event.data[1];
+ }
+ KeyEvent key{};
+ for (unsigned i = 0; i < 8 && keys.Pop(key); ++i)
+ hw.seed.PrintLine("[HPF] key raw=%u down=%u cb=%lu", unsigned(key.raw_index), unsigned(key.down),
+ static_cast(key.callback));
+ bool updated = false;
+ for (unsigned i = 0; i < 8 && snapshots.Pop(current); ++i)
+ updated = true;
+ if (updated)
+ Draw(current);
+ const auto now = daisy::System::GetNow();
+ if (now - last_log >= 250u)
+ {
+ last_log = now;
+ hw.seed.PrintLine("[HPF] truth cb=%lu seen=%u gate=%u edges=%lu",
+ static_cast(current.cb), unsigned(current.seen),
+ unsigned(current.gate), static_cast(current.gate_rises));
+ hw.seed.PrintLine("[HPF] outputs dac1=%u dac2=%u enabled=%u", unsigned(current.dac1),
+ unsigned(current.dac2), unsigned(current.armed_output));
+ hw.seed.PrintLine(
+ "[HPF] timing cb=%lu load_pm=%lu over=%lu late=%lu drops=%lu",
+ static_cast(current.cb), static_cast(current.load),
+ static_cast(current.overruns), static_cast(current.late),
+ static_cast(current.drops));
+ hw.seed.PrintLine("[HPF] knobs first=1 k1=%d k2=%d k3=%d k4=%d", Norm(current.knobs[0]),
+ Norm(current.knobs[1]), Norm(current.knobs[2]), Norm(current.knobs[3]));
+ hw.seed.PrintLine("[HPF] knobs first=5 k5=%d k6=%d k7=%d k8=%d", Norm(current.knobs[4]),
+ Norm(current.knobs[5]), Norm(current.knobs[6]), Norm(current.knobs[7]));
+ hw.seed.PrintLine("[HPF] cv c1=%d c2=%d c3=%d c4=%d peakL=%d peakR=%d", Norm(current.cv[0]),
+ Norm(current.cv[1]), Norm(current.cv[2]), Norm(current.cv[3]),
+ Norm(current.peak_l), Norm(current.peak_r));
+ hw.seed.PrintLine("[HPF] midi count=%lu type=%u channel0=%u d0=%u d1=%u",
+ static_cast(midi_count), last_type, last_channel, last_data0,
+ last_data1);
+ }
+ if (now - last_boot >= 2000u)
+ {
+ last_boot = now;
+ boot.Print(hw, "truth");
+ }
+ daisy::System::Delay(1);
+ }
+}
diff --git a/field/HydraPulse/firmware/HydraPulse.cpp b/field/HydraPulse/firmware/HydraPulse.cpp
new file mode 100644
index 000000000..b6d9b9731
--- /dev/null
+++ b/field/HydraPulse/firmware/HydraPulse.cpp
@@ -0,0 +1,220 @@
+#include "daisy_field.h"
+#include "firmware/FieldAdapter.h"
+#include "src/app/Controller.h"
+#include "src/app/Presets.generated.h"
+#include "src/core/SpscQueue.h"
+#include
+#include
+
+namespace
+{
+daisy::DaisyField hw;
+hydrapulse::Engine engine;
+hydrapulse::Controller controller;
+hydrapulse::field::CallbackMeter meter;
+hydrapulse::field::BootRecord boot;
+hydrapulse::core::SpscQueue snapshots;
+hydrapulse::core::SpscQueue midi_commands;
+std::atomic urgent{0}, midi_drops{0};
+std::uint32_t snapshot_drops = 0, blocks = 0;
+
+void AudioCallback(daisy::AudioHandle::InputBuffer, daisy::AudioHandle::OutputBuffer output, std::size_t size)
+{
+ meter.Begin(hw);
+ const auto frame = hydrapulse::field::ReadControls(hw);
+ controller.Tick(engine, frame);
+ const auto urgent_now = urgent.exchange(0, std::memory_order_acq_rel);
+ hydrapulse::MidiCommand command{};
+ if (urgent_now)
+ {
+ engine.Panic();
+ // Flush pre-stop commands so an old Note On cannot restart a silenced voice.
+ for (unsigned i = 0; i < 32 && midi_commands.Pop(command); ++i)
+ {
+ }
+ }
+ else
+ {
+ for (unsigned i = 0; i < 8 && midi_commands.Pop(command); ++i)
+ if (!controller.SuppressMidi())
+ engine.HandleMidi(command);
+ }
+ hydrapulse::field::OutputsOff(hw); // No live CV/Gate integration before P0 qualification.
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ const auto audio = engine.Process();
+ output[0][i] = audio.left;
+ output[1][i] = audio.right;
+ }
+ if (++blocks % 40u == 0)
+ {
+ auto s = controller.Snapshot(engine);
+ s.callback_count = meter.Count();
+ s.load_max_per_mille = meter.MaxPermille();
+ s.overruns = meter.Overruns();
+ s.late_starts = meter.LateStarts();
+ s.snapshot_drops = snapshot_drops;
+ s.midi_drops = midi_drops.load(std::memory_order_relaxed);
+ if (!snapshots.Push(s))
+ ++snapshot_drops;
+ }
+ meter.End(hw);
+}
+
+void Midi(daisy::MidiEvent event)
+{
+ using hydrapulse::MidiCommand;
+ using hydrapulse::MidiCommandType;
+ MidiCommand command{};
+ bool send = false;
+ if (event.type == daisy::SystemRealTime)
+ {
+ if (event.srt_type == daisy::Start)
+ {
+ command.type = MidiCommandType::Start;
+ send = true;
+ }
+ else if (event.srt_type == daisy::Stop || event.srt_type == daisy::Reset)
+ urgent.fetch_or(1u, std::memory_order_release);
+ // TimingClock and Continue intentionally unsupported; internal tempo is authoritative.
+ }
+ else if (event.channel == 9 && event.type == daisy::NoteOn && event.data[1] > 0)
+ {
+ constexpr std::uint8_t notes[4] = {36, 38, 42, 39};
+ for (std::uint8_t t = 0; t < 4; ++t)
+ if (event.data[0] == notes[t])
+ {
+ command = {MidiCommandType::Trigger, t, event.data[1]};
+ send = true;
+ break;
+ }
+ }
+ else if (event.channel == 9 && (event.type == daisy::ChannelMode || event.type == daisy::ControlChange))
+ {
+ if (event.data[0] == 120 || event.data[0] == 123)
+ urgent.fetch_or(1u, std::memory_order_release);
+ }
+ if (send && !midi_commands.Push(command))
+ midi_drops.fetch_add(1, std::memory_order_relaxed);
+}
+void Draw(const hydrapulse::UiSnapshot &s)
+{
+ const char *voices[4] = {"HAMMER", "CRACK", "STEEL", "ARC"};
+ const char banks[3] = {'A', 'B', 'F'};
+ char line[32];
+ hw.display.Fill(false);
+ if (s.shift)
+ {
+ hydrapulse::field::Text(hw, 0, "SHIFT: RELEASE=HOME");
+ hydrapulse::field::Text(hw, 1, "1-4 VOICE 5=A 6=B");
+ hydrapulse::field::Text(hw, 2, "7=EDIT F 8=HOLD FILL");
+ hydrapulse::field::Text(hw, 3, "9-12=MUTE VOICES");
+ hydrapulse::field::Text(hw, 4, "13=HOLD CLEAR (STOP)");
+ hydrapulse::field::Text(hw, 5, "14/15 PREV/NEXT PRE");
+ if (s.preset_armed)
+ {
+ std::snprintf(line, sizeof(line), "LOAD? %s",
+ hydrapulse::kTutorialPresets[s.pending_preset].name);
+ hydrapulse::field::Text(hw, 6, line);
+ hydrapulse::field::Text(hw, 7, "16=LOAD RELEASE=HOME");
+ }
+ else
+ {
+ hydrapulse::field::Text(hw, 6, "16=LOAD (STOP ONLY)");
+ hydrapulse::field::Text(hw, 7, "BOTH 0.5s = PANIC");
+ }
+ }
+ else
+ {
+ std::snprintf(line, sizeof(line), "HPF %s %c>%c %03u", s.running ? "RUN" : "STOP", banks[s.edit_bank],
+ banks[s.play_bank], unsigned(s.bpm));
+ hydrapulse::field::Text(hw, 0, line);
+ std::snprintf(line, sizeof(line), "%s %02u %s", voices[s.selected], unsigned(s.step + 1u),
+ s.fill ? "FILL" : "");
+ hydrapulse::field::Text(hw, 1, line);
+ std::snprintf(line, sizeof(line), "TUN%03u DEC%03u", unsigned(s.params[0] * 100),
+ unsigned(s.params[1] * 100));
+ hydrapulse::field::Text(hw, 2, line);
+ std::snprintf(line, sizeof(line), "CHR%03u LEV%03u", unsigned(s.params[2] * 100),
+ unsigned(s.params[3] * 100));
+ hydrapulse::field::Text(hw, 3, line);
+ std::snprintf(line, sizeof(line), "SW%02u DR%02u VOL%02u", unsigned(50 + 20 * s.swing),
+ unsigned(100 * s.drive), unsigned(100 * s.master));
+ hydrapulse::field::Text(hw, 4, line);
+ if (s.loading)
+ hydrapulse::field::Text(hw, 5, "LOADING...");
+ else if (s.preset_armed)
+ {
+ std::snprintf(line, sizeof(line), "LOAD? %s",
+ hydrapulse::kTutorialPresets[s.pending_preset].name);
+ hydrapulse::field::Text(hw, 5, line);
+ }
+ else
+ hydrapulse::field::Text(hw, 5, hydrapulse::kTutorialPresets[s.preset].name);
+ std::snprintf(line, sizeof(line), "PICKUP:%02X MUTE:%X", unsigned(s.pickup_mask), unsigned(s.mutes));
+ hydrapulse::field::Text(hw, 6, line);
+ std::snprintf(line, sizeof(line), "CPUmax %u.%u%% #%u", unsigned(s.load_max_per_mille / 10u),
+ unsigned(s.load_max_per_mille % 10u), unsigned(s.faults));
+ hydrapulse::field::Text(hw, 7, line);
+ }
+ hw.display.Update();
+ for (std::size_t i = 0; i < daisy::DaisyField::LED_LAST; ++i)
+ hw.led_driver.SetLed(i, 0.0f);
+ for (std::size_t i = 0; i < 16; ++i)
+ {
+ float brightness = (s.notes & (1u << i)) ? ((s.accents & (1u << i)) ? 0.7f : 0.25f) : 0.0f;
+ if (s.running && s.step == i)
+ brightness = 1.0f;
+ if (s.shift && i < 4)
+ brightness = (i == s.selected) ? 1.0f : 0.15f;
+ hw.led_driver.SetLed(hydrapulse::field::kLogicalToLed[i], brightness);
+ }
+ hw.led_driver.SetLed(daisy::DaisyField::LED_SW_1, s.running ? 0.6f : 0);
+ hw.led_driver.SetLed(daisy::DaisyField::LED_SW_2, s.shift ? 0.6f : 0);
+ hw.led_driver.SwapBuffersAndTransmit();
+}
+} // namespace
+int main()
+{
+ hydrapulse::field::InitHardware(hw);
+ boot.Capture(hw);
+ engine.Init(hydrapulse::field::kSampleRate);
+ controller.Init(engine, hydrapulse::field::ReadControls(hw));
+ meter.Init();
+ hw.StartAudio(AudioCallback);
+ hydrapulse::UiSnapshot current{};
+ std::uint32_t last_log = 0, last_boot = 0;
+ while (true)
+ {
+ hw.midi.Listen();
+ for (unsigned i = 0; i < 32 && hw.midi.HasEvents(); ++i)
+ Midi(hw.midi.PopEvent());
+ bool updated = false;
+ for (unsigned i = 0; i < 8 && snapshots.Pop(current); ++i)
+ updated = true;
+ if (updated)
+ Draw(current);
+ const auto now = daisy::System::GetNow();
+ if (now - last_log >= 250u)
+ {
+ last_log = now;
+ hw.seed.PrintLine("[HPF] beat cb=%lu load_pm=%lu faults=%lu",
+ static_cast(current.callback_count),
+ static_cast(current.load_max_per_mille),
+ static_cast(current.faults));
+ hw.seed.PrintLine("[HPF] timing cb=%lu over=%lu late=%lu",
+ static_cast(current.callback_count),
+ static_cast(current.overruns),
+ static_cast(current.late_starts));
+ hw.seed.PrintLine("[HPF] queues mdrops=%lu sdrops=%lu",
+ static_cast(current.midi_drops),
+ static_cast(current.snapshot_drops));
+ }
+ if (now - last_boot >= 2000u)
+ {
+ last_boot = now;
+ boot.Print(hw, "beat");
+ }
+ daisy::System::Delay(1);
+ }
+}
diff --git a/field/HydraPulse/hardware/control_map.json b/field/HydraPulse/hardware/control_map.json
new file mode 100644
index 000000000..51b0e818e
--- /dev/null
+++ b/field/HydraPulse/hardware/control_map.json
@@ -0,0 +1,121 @@
+{
+ "schema_version": 1,
+ "target": "stock Daisy Field",
+ "classification": "PROPOSED",
+ "physical_verified": false,
+ "mapping_source": "firmware/FieldAdapter.h; raw BSP indices logged by Field Truth",
+ "keys": [
+ {
+ "logical": 1,
+ "bsp_index": 0,
+ "led": "LED_KEY_A1",
+ "physical_label": null
+ },
+ {
+ "logical": 2,
+ "bsp_index": 1,
+ "led": "LED_KEY_A2",
+ "physical_label": null
+ },
+ {
+ "logical": 3,
+ "bsp_index": 2,
+ "led": "LED_KEY_A3",
+ "physical_label": null
+ },
+ {
+ "logical": 4,
+ "bsp_index": 3,
+ "led": "LED_KEY_A4",
+ "physical_label": null
+ },
+ {
+ "logical": 5,
+ "bsp_index": 4,
+ "led": "LED_KEY_A5",
+ "physical_label": null
+ },
+ {
+ "logical": 6,
+ "bsp_index": 5,
+ "led": "LED_KEY_A6",
+ "physical_label": null
+ },
+ {
+ "logical": 7,
+ "bsp_index": 6,
+ "led": "LED_KEY_A7",
+ "physical_label": null
+ },
+ {
+ "logical": 8,
+ "bsp_index": 7,
+ "led": "LED_KEY_A8",
+ "physical_label": null
+ },
+ {
+ "logical": 9,
+ "bsp_index": 8,
+ "led": "LED_KEY_B1",
+ "physical_label": null
+ },
+ {
+ "logical": 10,
+ "bsp_index": 9,
+ "led": "LED_KEY_B2",
+ "physical_label": null
+ },
+ {
+ "logical": 11,
+ "bsp_index": 10,
+ "led": "LED_KEY_B3",
+ "physical_label": null
+ },
+ {
+ "logical": 12,
+ "bsp_index": 11,
+ "led": "LED_KEY_B4",
+ "physical_label": null
+ },
+ {
+ "logical": 13,
+ "bsp_index": 12,
+ "led": "LED_KEY_B5",
+ "physical_label": null
+ },
+ {
+ "logical": 14,
+ "bsp_index": 13,
+ "led": "LED_KEY_B6",
+ "physical_label": null
+ },
+ {
+ "logical": 15,
+ "bsp_index": 14,
+ "led": "LED_KEY_B7",
+ "physical_label": null
+ },
+ {
+ "logical": 16,
+ "bsp_index": 15,
+ "led": "LED_KEY_B8",
+ "physical_label": null
+ }
+ ],
+ "knobs": [
+ "Tune",
+ "Decay",
+ "Character",
+ "Level",
+ "Tempo",
+ "Swing",
+ "Drive",
+ "Master"
+ ],
+ "switches": {
+ "SW_1": "Play/Stop on release; held diagnostic output enable in Truth",
+ "SW_2": "momentary Shift; VegasMode in Truth",
+ "BOTH": "500 ms Panic in Beat"
+ },
+ "warning": "Confirm physical key/LED ordering and electrical behavior on the actual assembled Field."
+}
diff --git a/field/HydraPulse/hardware/key_map.template.csv b/field/HydraPulse/hardware/key_map.template.csv
new file mode 100644
index 000000000..e2d13a6ad
--- /dev/null
+++ b/field/HydraPulse/hardware/key_map.template.csv
@@ -0,0 +1,17 @@
+logical,bsp_index,led,physical_label,classification
+1,0,LED_KEY_A1,,PROPOSED
+2,1,LED_KEY_A2,,PROPOSED
+3,2,LED_KEY_A3,,PROPOSED
+4,3,LED_KEY_A4,,PROPOSED
+5,4,LED_KEY_A5,,PROPOSED
+6,5,LED_KEY_A6,,PROPOSED
+7,6,LED_KEY_A7,,PROPOSED
+8,7,LED_KEY_A8,,PROPOSED
+9,8,LED_KEY_B1,,PROPOSED
+10,9,LED_KEY_B2,,PROPOSED
+11,10,LED_KEY_B3,,PROPOSED
+12,11,LED_KEY_B4,,PROPOSED
+13,12,LED_KEY_B5,,PROPOSED
+14,13,LED_KEY_B6,,PROPOSED
+15,14,LED_KEY_B7,,PROPOSED
+16,15,LED_KEY_B8,,PROPOSED
diff --git a/field/HydraPulse/hardware/p0_record.template.json b/field/HydraPulse/hardware/p0_record.template.json
new file mode 100644
index 000000000..d9fef410d
--- /dev/null
+++ b/field/HydraPulse/hardware/p0_record.template.json
@@ -0,0 +1,149 @@
+{
+ "schema_version": 1,
+ "note": "Template only. Do not promote fields without raw measurements.",
+ "records": [
+ {
+ "id": "P0-BOOT",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "Cold power cycle, source image identity and startup behavior",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ },
+ {
+ "id": "P0-KEYS",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "All sixteen physical keys, raw indices, LEDs and release behavior",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ },
+ {
+ "id": "P0-KNOBS",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "All eight knob endpoints, noise, monotonicity and pickup behavior",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ },
+ {
+ "id": "P0-CVIN",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "Four CV input transfer curves against measured voltages",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ },
+ {
+ "id": "P0-GATEIN",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "Gate input polarity, levels and capture limits",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ },
+ {
+ "id": "P0-CVOUT",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "DAC1/2 voltage against commanded code under specified load",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ },
+ {
+ "id": "P0-GATEOUT",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "Gate output inactive/active voltages and release latency",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ },
+ {
+ "id": "P0-MIDI",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "Known channel/note/control/realtime event reception",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ },
+ {
+ "id": "P0-DISPLAY",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "OLED and all LEDs under concurrent audio operation",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ },
+ {
+ "id": "P0-AUDIO",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "Stereo calibrated loopback gain, bandwidth, noise, clipping and crosstalk",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ },
+ {
+ "id": "P0-TIMING",
+ "domain": "hardware",
+ "classification": "NOT_RUN",
+ "claim": "Callback-body timing, actual period, stress and deadline margin",
+ "fixture": "",
+ "operator": "",
+ "utc": "",
+ "method": "",
+ "result": "",
+ "firmware_sha256": "",
+ "artifacts": []
+ }
+ ]
+}
diff --git a/field/HydraPulse/presets/tutorials.json b/field/HydraPulse/presets/tutorials.json
new file mode 100644
index 000000000..2e53bb62f
--- /dev/null
+++ b/field/HydraPulse/presets/tutorials.json
@@ -0,0 +1,598 @@
+{
+ "schema": 1,
+ "origin": "Original HydraPulse tutorial patterns; no manual pattern transcription.",
+ "presets": [
+ {
+ "id": 0,
+ "name": "Blank Canvas",
+ "bpm": 110,
+ "swing": 0,
+ "drive": 0,
+ "voices": [
+ {
+ "tune": 0.36,
+ "decay": 0.32,
+ "character": 0.28,
+ "level": 0.8
+ },
+ {
+ "tune": 0.45,
+ "decay": 0.28,
+ "character": 0.5,
+ "level": 0.72
+ },
+ {
+ "tune": 0.45,
+ "decay": 0.25,
+ "character": 0.4,
+ "level": 0.55
+ },
+ {
+ "tune": 0.4,
+ "decay": 0.4,
+ "character": 0.3,
+ "level": 0.65
+ }
+ ],
+ "notes": [
+ [
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ ],
+ "accents": [
+ [
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ ],
+ "lesson": "Build a rhythm from silence."
+ },
+ {
+ "id": 1,
+ "name": "First Pulse",
+ "bpm": 110,
+ "swing": 0,
+ "drive": 0.05,
+ "voices": [
+ {
+ "tune": 0.32,
+ "decay": 0.35,
+ "character": 0.35,
+ "level": 0.85
+ },
+ {
+ "tune": 0.4,
+ "decay": 0.28,
+ "character": 0.5,
+ "level": 0.7
+ },
+ {
+ "tune": 0.4,
+ "decay": 0.2,
+ "character": 0.4,
+ "level": 0.55
+ },
+ {
+ "tune": 0.35,
+ "decay": 0.4,
+ "character": 0.25,
+ "level": 0.6
+ }
+ ],
+ "notes": [
+ [
+ 4369,
+ 0,
+ 0,
+ 0
+ ],
+ [
+ 4497,
+ 0,
+ 0,
+ 0
+ ],
+ [
+ 4369,
+ 59392,
+ 65280,
+ 0
+ ]
+ ],
+ "accents": [
+ [
+ 4369,
+ 0,
+ 0,
+ 0
+ ],
+ [
+ 4369,
+ 0,
+ 0,
+ 0
+ ],
+ [
+ 4369,
+ 0,
+ 4352,
+ 0
+ ]
+ ],
+ "lesson": "Hear pitch sweep and decay separately."
+ },
+ {
+ "id": 2,
+ "name": "Backbeat",
+ "bpm": 120,
+ "swing": 0,
+ "drive": 0.1,
+ "voices": [
+ {
+ "tune": 0.4,
+ "decay": 0.28,
+ "character": 0.25,
+ "level": 0.8
+ },
+ {
+ "tune": 0.35,
+ "decay": 0.28,
+ "character": 0.55,
+ "level": 0.74
+ },
+ {
+ "tune": 0.48,
+ "decay": 0.2,
+ "character": 0.35,
+ "level": 0.55
+ },
+ {
+ "tune": 0.4,
+ "decay": 0.3,
+ "character": 0.35,
+ "level": 0.65
+ }
+ ],
+ "notes": [
+ [
+ 257,
+ 4112,
+ 17476,
+ 0
+ ],
+ [
+ 16705,
+ 4112,
+ 17476,
+ 0
+ ],
+ [
+ 257,
+ 63504,
+ 65348,
+ 0
+ ]
+ ],
+ "accents": [
+ [
+ 257,
+ 4112,
+ 0,
+ 0
+ ],
+ [
+ 257,
+ 4112,
+ 0,
+ 0
+ ],
+ [
+ 257,
+ 4112,
+ 4352,
+ 0
+ ]
+ ],
+ "lesson": "Balance kick, snare and offbeat hats."
+ },
+ {
+ "id": 3,
+ "name": "Broken Grid",
+ "bpm": 126,
+ "swing": 0,
+ "drive": 0.16,
+ "voices": [
+ {
+ "tune": 0.45,
+ "decay": 0.24,
+ "character": 0.48,
+ "level": 0.82
+ },
+ {
+ "tune": 0.4,
+ "decay": 0.24,
+ "character": 0.65,
+ "level": 0.75
+ },
+ {
+ "tune": 0.58,
+ "decay": 0.18,
+ "character": 0.6,
+ "level": 0.5
+ },
+ {
+ "tune": 0.45,
+ "decay": 0.25,
+ "character": 0.35,
+ "level": 0.62
+ }
+ ],
+ "notes": [
+ [
+ 2369,
+ 4112,
+ 25957,
+ 2056
+ ],
+ [
+ 18689,
+ 4112,
+ 25957,
+ 34952
+ ],
+ [
+ 2369,
+ 63504,
+ 65381,
+ 2056
+ ]
+ ],
+ "accents": [
+ [
+ 257,
+ 4112,
+ 257,
+ 0
+ ],
+ [
+ 257,
+ 4112,
+ 257,
+ 0
+ ],
+ [
+ 257,
+ 4112,
+ 4353,
+ 0
+ ]
+ ],
+ "lesson": "Hear syncopation without probability."
+ },
+ {
+ "id": 4,
+ "name": "Alloy Swing",
+ "bpm": 124,
+ "swing": 0.55,
+ "drive": 0.18,
+ "voices": [
+ {
+ "tune": 0.3,
+ "decay": 0.35,
+ "character": 0.38,
+ "level": 0.82
+ },
+ {
+ "tune": 0.46,
+ "decay": 0.34,
+ "character": 0.7,
+ "level": 0.72
+ },
+ {
+ "tune": 0.55,
+ "decay": 0.42,
+ "character": 0.55,
+ "level": 0.56
+ },
+ {
+ "tune": 0.55,
+ "decay": 0.35,
+ "character": 0.45,
+ "level": 0.6
+ }
+ ],
+ "notes": [
+ [
+ 385,
+ 4112,
+ 21845,
+ 33288
+ ],
+ [
+ 16833,
+ 4112,
+ 21845,
+ 33416
+ ],
+ [
+ 385,
+ 63504,
+ 65365,
+ 33288
+ ]
+ ],
+ "accents": [
+ [
+ 257,
+ 4112,
+ 4369,
+ 0
+ ],
+ [
+ 257,
+ 4112,
+ 4369,
+ 0
+ ],
+ [
+ 257,
+ 4112,
+ 4369,
+ 0
+ ]
+ ],
+ "lesson": "Compare straight and delayed offbeats."
+ },
+ {
+ "id": 5,
+ "name": "Arc Study",
+ "bpm": 105,
+ "swing": 0,
+ "drive": 0.12,
+ "voices": [
+ {
+ "tune": 0.4,
+ "decay": 0.28,
+ "character": 0.35,
+ "level": 0.8
+ },
+ {
+ "tune": 0.5,
+ "decay": 0.3,
+ "character": 0.45,
+ "level": 0.7
+ },
+ {
+ "tune": 0.4,
+ "decay": 0.2,
+ "character": 0.4,
+ "level": 0.5
+ },
+ {
+ "tune": 0.62,
+ "decay": 0.55,
+ "character": 0.6,
+ "level": 0.62
+ }
+ ],
+ "notes": [
+ [
+ 257,
+ 4096,
+ 17476,
+ 19017
+ ],
+ [
+ 16705,
+ 4096,
+ 17476,
+ 51913
+ ],
+ [
+ 257,
+ 63488,
+ 65348,
+ 59977
+ ]
+ ],
+ "accents": [
+ [
+ 257,
+ 4096,
+ 0,
+ 1
+ ],
+ [
+ 257,
+ 4096,
+ 0,
+ 1
+ ],
+ [
+ 257,
+ 4096,
+ 4352,
+ 1
+ ]
+ ],
+ "lesson": "Explore phase modulation, tune and decay."
+ },
+ {
+ "id": 6,
+ "name": "Night Shift",
+ "bpm": 132,
+ "swing": 0.2,
+ "drive": 0.38,
+ "voices": [
+ {
+ "tune": 0.25,
+ "decay": 0.5,
+ "character": 0.7,
+ "level": 0.86
+ },
+ {
+ "tune": 0.3,
+ "decay": 0.4,
+ "character": 0.8,
+ "level": 0.74
+ },
+ {
+ "tune": 0.5,
+ "decay": 0.5,
+ "character": 0.6,
+ "level": 0.52
+ },
+ {
+ "tune": 0.3,
+ "decay": 0.6,
+ "character": 0.68,
+ "level": 0.6
+ }
+ ],
+ "notes": [
+ [
+ 4497,
+ 6160,
+ 21845,
+ 33928
+ ],
+ [
+ 20945,
+ 6160,
+ 21845,
+ 33928
+ ],
+ [
+ 4497,
+ 63504,
+ 65365,
+ 44680
+ ]
+ ],
+ "accents": [
+ [
+ 4369,
+ 4112,
+ 4369,
+ 0
+ ],
+ [
+ 4369,
+ 4112,
+ 4369,
+ 0
+ ],
+ [
+ 4369,
+ 4112,
+ 4369,
+ 0
+ ]
+ ],
+ "lesson": "Perform A/B, mutes and momentary Fill."
+ },
+ {
+ "id": 7,
+ "name": "Fracture Lab",
+ "bpm": 144,
+ "swing": 0.35,
+ "drive": 0.52,
+ "voices": [
+ {
+ "tune": 0.45,
+ "decay": 0.4,
+ "character": 0.9,
+ "level": 0.84
+ },
+ {
+ "tune": 0.65,
+ "decay": 0.38,
+ "character": 0.9,
+ "level": 0.68
+ },
+ {
+ "tune": 0.68,
+ "decay": 0.5,
+ "character": 0.7,
+ "level": 0.46
+ },
+ {
+ "tune": 0.72,
+ "decay": 0.55,
+ "character": 0.85,
+ "level": 0.56
+ }
+ ],
+ "notes": [
+ [
+ 18761,
+ 37008,
+ 65535,
+ 10786
+ ],
+ [
+ 2313,
+ 37008,
+ 65535,
+ 43682
+ ],
+ [
+ 18761,
+ 63632,
+ 65535,
+ 43554
+ ]
+ ],
+ "accents": [
+ [
+ 257,
+ 4112,
+ 4369,
+ 0
+ ],
+ [
+ 257,
+ 4112,
+ 4369,
+ 0
+ ],
+ [
+ 257,
+ 4112,
+ 4369,
+ 0
+ ]
+ ],
+ "lesson": "Dense stress lesson: arrange with subtraction."
+ }
+ ]
+}
diff --git a/field/HydraPulse/references/r0/.clang-format b/field/HydraPulse/references/r0/.clang-format
new file mode 100644
index 000000000..971a1688e
--- /dev/null
+++ b/field/HydraPulse/references/r0/.clang-format
@@ -0,0 +1,7 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+ColumnLimit: 100
+AllowShortFunctionsOnASingleLine: Empty
+DerivePointerAlignment: false
+PointerAlignment: Left
+SortIncludes: CaseSensitive
diff --git a/field/HydraPulse/references/r0/.github/workflows/ci.yml b/field/HydraPulse/references/r0/.github/workflows/ci.yml
new file mode 100644
index 000000000..f547fbcfa
--- /dev/null
+++ b/field/HydraPulse/references/r0/.github/workflows/ci.yml
@@ -0,0 +1,41 @@
+name: CI
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ native:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Configure native tests
+ run: cmake -S . -B build -DBUILD_TESTING=ON
+ - name: Build native tests
+ run: cmake --build build --parallel
+ - name: Run native tests
+ run: ctest --test-dir build --output-on-failure
+ - name: Validate repository
+ run: python tools/validate_repo.py
+ - name: Validate provenance
+ run: python tools/validate_provenance.py
+ - name: Run Python tests
+ run: python -m unittest discover -s tests/python -v
+
+ sanitizers:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Configure ASan + UBSan
+ run: >-
+ cmake -S . -B build-sanitize
+ -DBUILD_TESTING=ON
+ -DHPF_ENABLE_SANITIZERS=ON
+ -DCMAKE_BUILD_TYPE=Debug
+ - name: Build ASan + UBSan
+ run: cmake --build build-sanitize --parallel
+ - name: Run ASan + UBSan
+ env:
+ ASAN_OPTIONS: detect_leaks=1:halt_on_error=1
+ UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
+ run: ctest --test-dir build-sanitize --output-on-failure
diff --git a/field/HydraPulse/references/r0/.gitignore b/field/HydraPulse/references/r0/.gitignore
new file mode 100644
index 000000000..d09ffca4e
--- /dev/null
+++ b/field/HydraPulse/references/r0/.gitignore
@@ -0,0 +1,13 @@
+/build/
+/build-*/
+/.cache/
+/.vscode/
+/.idea/
+*.elf
+*.bin
+*.hex
+*.map
+*.o
+__pycache__/
+*.pyc
+.DS_Store
diff --git a/field/HydraPulse/references/r0/CMakeLists.txt b/field/HydraPulse/references/r0/CMakeLists.txt
new file mode 100644
index 000000000..141c49ecd
--- /dev/null
+++ b/field/HydraPulse/references/r0/CMakeLists.txt
@@ -0,0 +1,43 @@
+cmake_minimum_required(VERSION 3.20)
+project(HydraPulseField VERSION 0.1.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+option(HPF_ENABLE_SANITIZERS "Enable AddressSanitizer and UndefinedBehaviorSanitizer" OFF)
+
+add_library(hydrapulse_core INTERFACE)
+target_include_directories(hydrapulse_core INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
+
+if(MSVC)
+ target_compile_options(hydrapulse_core INTERFACE /W4 /permissive-)
+else()
+ target_compile_options(hydrapulse_core INTERFACE
+ -Wall
+ -Wextra
+ -Wpedantic
+ -Wconversion
+ -Wshadow
+ )
+endif()
+
+if(HPF_ENABLE_SANITIZERS)
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
+ target_compile_options(hydrapulse_core INTERFACE
+ -fsanitize=address,undefined
+ -fno-omit-frame-pointer
+ )
+ target_link_options(hydrapulse_core INTERFACE
+ -fsanitize=address,undefined
+ -fno-omit-frame-pointer
+ )
+ else()
+ message(FATAL_ERROR "HPF_ENABLE_SANITIZERS currently requires GCC or Clang")
+ endif()
+endif()
+
+include(CTest)
+if(BUILD_TESTING)
+ add_subdirectory(tests/native)
+endif()
diff --git a/field/HydraPulse/references/r0/README.md b/field/HydraPulse/references/r0/README.md
new file mode 100644
index 000000000..6b1dc5d31
--- /dev/null
+++ b/field/HydraPulse/references/r0/README.md
@@ -0,0 +1,105 @@
+# HydraPulse Field
+
+HydraPulse Field is a staged, standalone drum/performance instrument for the stock Electro-Smith Daisy Field.
+
+The project is deliberately proving the instrument in layers instead of implementing the whole product at once. The first musical target is an independent Field drum machine. Hydrasynth-specific MIDI/CV/audio interaction is deferred until the standalone foundation is credible.
+
+## Scope
+
+### Product direction
+
+- Stock Daisy Field is the first hardware target.
+- Normal interaction stays centered on 16 visible steps.
+- Beginner invariant: **select drum -> tap 16 steps -> edit sound -> Play**.
+- Common voice language: **Tune | Decay | Character | Level**.
+- First four synthesized voices: **Hammer** (kick), **Crack** (snare), **Steel** (closed hat / metallic), **Arc** (FM percussion).
+- A/B/Fill precedes deep trig-lock, probability, ratchet, and microtiming systems.
+- Hydrasynth integration is intentionally deferred.
+
+### What this bootstrap implements
+
+This repository bootstrap is the pre-hardware software foundation, called **R0** so it is not confused with P0 hardware truth.
+
+Implemented now:
+
+- hardware-independent C++17 core;
+- 16-step pattern container;
+- deterministic step sequencer;
+- sample-domain integer timing clock;
+- soft-takeover / pickup primitive;
+- native CMake + CTest harness;
+- deterministic stress and long-run timing tests;
+- repository/provenance validators;
+- GitHub CI for native tests and ASan+UBSan;
+- pinned upstream provenance records;
+- P0 Field Truth implementation contract and evidence layout.
+
+Not implemented yet:
+
+- Daisy Field firmware binary;
+- ARM cross-build verification;
+- any physical Daisy Field measurement;
+- audio/CV/Gate/MIDI hardware evidence;
+- P1 drum synthesis;
+- A/B/Fill;
+- advanced sequencing;
+- Hydrasynth integration.
+
+A compile is not hardware evidence, and a host test is not ARM timing evidence. This repository treats those as different claims on purpose.
+
+## Repository layout
+
+```text
+.
+├── CMakeLists.txt
+├── deps.lock.json
+├── src/core/ # hardware-independent deterministic core
+├── tests/native/ # ordinary C++ tests
+├── tests/python/ # repository/provenance validator tests
+├── tools/ # repository validators
+├── docs/ # architecture, POC sequence, test strategy, sources
+├── firmware/field_truth/ # P0 contract; firmware implementation follows R0
+├── hardware/ # fixture notes and future measured mappings
+├── evidence/ # raw P0/P1 evidence, never simulated as "measured"
+└── third_party/manifest.json # source/provenance ledger
+```
+
+## Build and test R0
+
+```bash
+cmake -S . -B build -DBUILD_TESTING=ON
+cmake --build build --parallel
+ctest --test-dir build --output-on-failure
+python tools/validate_repo.py
+python tools/validate_provenance.py
+python -m unittest discover -s tests/python -v
+```
+
+Sanitizers on GCC/Clang:
+
+```bash
+cmake -S . -B build-sanitize -DBUILD_TESTING=ON -DHPF_ENABLE_SANITIZERS=ON
+cmake --build build-sanitize --parallel
+ctest --test-dir build-sanitize --output-on-failure
+```
+
+## Milestones
+
+- **R0 - Repository foundation:** deterministic host core and automation. Implemented in this bootstrap.
+- **P0 - Field Truth Test:** actual stock Field controls, audio, CV/Gate, MIDI, display/LED behavior, and callback timing measured on hardware.
+- **P1 - Beat Core:** Hammer / Crack / Steel / Arc with deterministic sonic scenarios.
+- **P2 - Performance layer:** A/B/Fill and direct live gestures.
+- **P3 - Sequencing depth:** microtiming, ratchets, probability, parameter locks only after the direct workflow remains coherent.
+- **P4 - Hydrasynth interaction:** MIDI/CV/clock/audio-through only after standalone proof.
+
+See [docs/POC_SEQUENCE.md](docs/POC_SEQUENCE.md) for completion gates.
+
+## Upstream boundaries
+
+R0 does not vendor libDaisy or DaisySP. Exact upstream commits inspected for this bootstrap are recorded in `deps.lock.json` and `third_party/manifest.json`.
+
+The source review also records useful architecture/test references found through `Denys/embedded-audio-mine`. Those references are evidence and design inputs, not proof that their implementation belongs in HydraPulse.
+
+## Root license
+
+No HydraPulse root license is selected in this bootstrap. That decision remains explicit rather than being silently inferred from dependencies or references.
diff --git a/field/HydraPulse/references/r0/deps.lock.json b/field/HydraPulse/references/r0/deps.lock.json
new file mode 100644
index 000000000..874582a6e
--- /dev/null
+++ b/field/HydraPulse/references/r0/deps.lock.json
@@ -0,0 +1,20 @@
+{
+ "schema_version": 1,
+ "generated_utc_date": "2026-08-16",
+ "dependencies": [
+ {
+ "name": "libDaisy",
+ "repository": "https://github.com/electro-smith/libDaisy",
+ "ref": "cc146d5065dd8286078a662e2830bf820c37a612",
+ "role": "P0 Daisy Field BSP / hardware dependency",
+ "state": "planned_not_vendored"
+ },
+ {
+ "name": "DaisySP",
+ "repository": "https://github.com/electro-smith/DaisySP",
+ "ref": "599511b740f8f3a9b8db72a0642aa45b8a23c3a3",
+ "role": "P1 temporary synthesis/DSP implementation source",
+ "state": "deferred_not_vendored"
+ }
+ ]
+}
diff --git a/field/HydraPulse/references/r0/docs/ARCHITECTURE.md b/field/HydraPulse/references/r0/docs/ARCHITECTURE.md
new file mode 100644
index 000000000..30acc41bd
--- /dev/null
+++ b/field/HydraPulse/references/r0/docs/ARCHITECTURE.md
@@ -0,0 +1,79 @@
+# Architecture
+
+## Design rule
+
+HydraPulse keeps musical timing and pattern semantics independent of libDaisy wherever practical. Hardware code adapts real Field I/O into stable core-facing values; it does not own sequencer semantics.
+
+```text
++--------------------------------------------------------------+
+| UI / performance semantics |
+| select voice | 16 steps | Tune Decay Character Level | Fill |
++-----------------------------+--------------------------------+
+ |
++-----------------------------v--------------------------------+
+| Pure C++ core |
+| Pattern16 | StepSequencer | SampleAccurateStepClock | Pickup |
++-----------------------------+--------------------------------+
+ |
++-----------------------------v--------------------------------+
+| Voice layer (P1) |
+| Hammer | Crack | Steel | Arc |
+| stable HydraPulse voice interfaces; DaisySP may sit behind it|
++-----------------------------+--------------------------------+
+ |
++-----------------------------v--------------------------------+
+| Field adapter (P0) |
+| keys knobs CV gate MIDI OLED LEDs audio callback |
++-----------------------------+--------------------------------+
+ |
++-----------------------------v--------------------------------+
+| libDaisy / Daisy Field hardware |
++--------------------------------------------------------------+
+```
+
+## R0 core
+
+### `Pattern16`
+
+Fixed-size, allocation-free 16-step bit patterns. Bounds failures are explicit boolean results rather than exceptions. The structure is intentionally boring because musical pattern state should not depend on heap behavior.
+
+### `StepSequencer`
+
+Deterministic transport-to-pattern bridge. `Start()` restarts at step zero. Each `EmitAndAdvance()` returns a fixed-size tick containing the step index and track bitmask. Trigger enumeration is always ascending track order.
+
+### `SampleAccurateStepClock`
+
+Integer phase accumulator. BPM is quantized to one micro-BPM and accumulated in sample domain. This avoids cumulative floating-point drift and naturally alternates adjacent integer sample intervals when the exact interval is fractional.
+
+The clock only defines boundaries. It does not decide whether Start should emit step zero immediately; that policy belongs to the transport/sequencer integration.
+
+### `ControlPickup`
+
+Normalized `[0,1]` soft takeover. A physical control remains disconnected until it reaches or crosses the stored target, then tracks directly.
+
+## P0 Field adapter contract
+
+The P0 adapter must prove, not assume:
+
+- physical order of all 16 key scan indices;
+- eight knob endpoints and monotonic direction;
+- four CV input usable ranges;
+- Gate In polarity/threshold behavior at the assembled Field;
+- Gate Out electrical levels;
+- both CV output transfer functions;
+- MIDI receive path;
+- stereo audio path and real gain/headroom/noise/crosstalk;
+- OLED and LED behavior;
+- callback period, execution distribution, worst observed execution, and margin.
+
+The official `DaisyField` API inspected for R0 exposes `KNOB_1..8`, `CV_1..4`, keyboard state/edge methods, `gate_in`, `gate_out`, `midi`, OLED display, LED driver, and `SetCvOut1/2()`.
+
+## Real-time boundary
+
+The audio callback may perform bounded DSP, state snapshots, and constant-time instrumentation. It must not perform serial logging, OLED writes, storage access, dynamic allocation, or unbounded work.
+
+Human-readable telemetry is emitted outside the callback from data captured by bounded instrumentation.
+
+## Future portability
+
+If the concept proves worthwhile, the pure core and stable voice interfaces become the portable layer. The first product should not pay the abstraction tax of a hypothetical future hardware platform before the Field instrument itself is useful.
diff --git a/field/HydraPulse/references/r0/docs/LOCAL_VALIDATION.md b/field/HydraPulse/references/r0/docs/LOCAL_VALIDATION.md
new file mode 100644
index 000000000..a8ee0e5b4
--- /dev/null
+++ b/field/HydraPulse/references/r0/docs/LOCAL_VALIDATION.md
@@ -0,0 +1,41 @@
+# Local validation
+
+Date: 2026-08-16
+
+This file records checks actually executed against this bootstrap package in the generation runtime.
+
+## Environment
+
+- CMake: detected and used successfully
+- C++ compiler: GNU 14.2.0
+- Host target: Linux container
+- C++ standard: C++17
+
+## Results
+
+| Check | Result | Evidence |
+|---|---|---|
+| CMake configure, Release | VERIFIED / PASS | `cmake -S . -B build -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release` |
+| Native build | VERIFIED / PASS | four test executables built |
+| CTest | VERIFIED / PASS | 4/4 tests passed |
+| Repository validator | VERIFIED / PASS | `repository structure: PASS` |
+| Provenance validator | VERIFIED / PASS | `provenance validation: PASS` |
+| Python validator tests | VERIFIED / PASS | 2/2 tests passed |
+| ASan + UBSan configure/build | VERIFIED / PASS | GCC sanitizer build completed |
+| ASan + UBSan CTest | VERIFIED / PASS | 4/4 tests passed |
+| ARM/libDaisy build | NOT_RUN | no target toolchain/dependency checkout executed in this bootstrap runtime |
+| Daisy Field hardware | NOT_RUN | no physical hardware attached to this runtime |
+| GitHub Actions | NOT_RUN | repository remains empty and nothing was pushed by this task |
+
+## Native tests executed
+
+- `test_pattern16`
+- `test_step_sequencer`
+- `test_sample_clock`
+- `test_control_pickup`
+
+The pattern test includes a deterministic 20,000-operation state stress sequence. The sample-clock test checks 1,024 non-integer-BPM step events against ideal sample-domain timing and requires each event to remain within one sample.
+
+## Scope of claim
+
+These results verify the generated R0 host core and repository automation in this runtime. They do not verify ARM behavior, libDaisy integration, callback timing on STM32H7, or any analog/electrical/audio performance of Daisy Field.
diff --git a/field/HydraPulse/references/r0/docs/POC_SEQUENCE.md b/field/HydraPulse/references/r0/docs/POC_SEQUENCE.md
new file mode 100644
index 000000000..fcc08efa5
--- /dev/null
+++ b/field/HydraPulse/references/r0/docs/POC_SEQUENCE.md
@@ -0,0 +1,114 @@
+# Staged proof-of-concept sequence
+
+HydraPulse uses completion gates that separate software plausibility from physical truth.
+
+## R0 - Repository foundation
+
+**Goal:** create a deterministic, hardware-independent base that can be tested on every commit.
+
+Implemented in this bootstrap:
+
+- fixed-size 16-step pattern state;
+- deterministic sequencer;
+- sample-domain tempo clock;
+- soft takeover;
+- host tests and deterministic stress tests;
+- repository/provenance validation;
+- ASan+UBSan CI definition.
+
+**Gate:** native build + CTest + validators pass. Sanitizer CI is defined but is not considered executed until GitHub runs it.
+
+## P0 - Field Truth Test
+
+P0 is a hardware characterization milestone, not a feature demo.
+
+### P0.1 Build truth
+
+- add `firmware/field_truth/FieldTruth.cpp` and project-local build metadata;
+- pin and fetch libDaisy;
+- build libDaisy;
+- build Field Truth for ARM;
+- preserve exact build log and firmware hash.
+
+**Gate:** ARM build passes. This does **not** complete P0.
+
+### P0.2 Control truth
+
+Measure and record:
+
+- all 16 raw keyboard scan indices versus physical keys;
+- eight knob raw/min/max and direction;
+- four CV input raw/min/max across known applied voltages;
+- Gate In low/high behavior and edge detection;
+- MIDI receive event types and raw values;
+- OLED/LED basic functionality.
+
+### P0.3 Output truth
+
+Measure:
+
+- CV Out 1 and 2 voltage versus requested code;
+- Gate Out low/high voltage;
+- output safety behavior during boot/reset.
+
+### P0.4 Audio truth
+
+With a documented source/load/measurement fixture:
+
+- stereo passthrough gain;
+- frequency response sufficient to expose gross path errors;
+- clipping/headroom;
+- noise floor under a defined bandwidth/method;
+- channel crosstalk;
+- cold-boot behavior.
+
+### P0.5 Callback truth
+
+Instrument full callback entry-to-exit time. Record:
+
+- sample rate;
+- block size;
+- callback period;
+- min/mean/p99/p99.9/max or the closest feasible distribution summary;
+- overrun/deadline count;
+- test duration and load condition;
+- probe point / GPIO method if external timing is used.
+
+**P0 completion gate:** physical evidence exists for controls, audio, CV/Gate, MIDI, and callback timing. Compilation alone cannot satisfy P0.
+
+## P1 - Beat Core
+
+Only after P0:
+
+1. Add stable voice interface.
+2. Implement Hammer / Crack / Steel / Arc.
+3. Keep Tune / Decay / Character / Level semantics common.
+4. Drive voices from the R0 sequencer/clock.
+5. Add deterministic sonic scenarios on host where practical.
+6. Compare host numerical behavior with target captures where practical.
+7. Add audio loopback evidence on Field.
+
+**P1 completion gate:** four voices sequence reliably on Field, deterministic regressions pass, and real target audio/timing evidence shows credible margin.
+
+## P2 - A/B/Fill
+
+Add direct performance variation before deeper sequencing. Preserve the 16-step surface.
+
+## P3 - Advanced sequencing
+
+Add only when direct operation remains coherent:
+
+- microtiming;
+- ratchets/retrigs;
+- probability/conditions;
+- parameter locks.
+
+## P4 - Hydrasynth interaction
+
+Deferred until standalone proof:
+
+- MIDI clock/control;
+- analog Pitch/Gate/Mod interaction;
+- clock jitter/drift comparison;
+- optional stereo audio-through;
+- ducking / rhythmic destruction.
diff --git a/field/HydraPulse/references/r0/docs/SOURCE_REVIEW.md b/field/HydraPulse/references/r0/docs/SOURCE_REVIEW.md
new file mode 100644
index 000000000..3e1b9956f
--- /dev/null
+++ b/field/HydraPulse/references/r0/docs/SOURCE_REVIEW.md
@@ -0,0 +1,106 @@
+# Source review used for R0 bootstrap
+
+Date: 2026-08-16
+
+This review records sources actually inspected before creating the bootstrap. `Denys/embedded-audio-mine` was used as a discovery index; upstream repositories were then inspected directly for concrete files/APIs.
+
+## Repository state
+
+### `Denys/HydraPulse-Field`
+
+- configured default branch: `main`;
+- repository visibility observed through the connected GitHub app: private;
+- GitHub contents API returned: **repository is empty**;
+- no branch ref / HEAD commit exists yet, so there is no main SHA to preserve.
+
+### `Denys/embedded-audio-mine`
+
+Inspected commit:
+
+`1113f0c6255c24a6655be968fb5aa115b8d5f7e9`
+
+Its README explicitly separates published digests, selected projects, and anti-repeat evidence. A selected project is not automatically a reusable dependency. That distinction is retained here.
+
+## Official Daisy sources
+
+### `electro-smith/libDaisy`
+
+Inspected commit:
+
+`cc146d5065dd8286078a662e2830bf820c37a612`
+
+Verified public Field API in `src/daisy_field.h` includes:
+
+- eight `KNOB_*` entries;
+- four `CV_*` entries;
+- 16-key keyboard state/rising/falling edge functions;
+- `SetCvOut1()` and `SetCvOut2()`;
+- `gate_in` and `gate_out`;
+- `midi`;
+- `display`;
+- Field LED driver;
+- audio/ADC lifecycle methods.
+
+`src/daisy_seed.h` also exposes `SetLed()`, `SetTestPoint()`, `StartLog()`, `Print()` and `PrintLine()`.
+
+`src/hid/gatein.h` exposes both edge-style `Trig()` and current `State()`.
+
+### `electro-smith/DaisyExamples`
+
+Inspected commit:
+
+`259ed82c7d4c0d7699f695945e6e111b7dc7cb27`
+
+Verified examples:
+
+- `field/KeyboardTest/KeyboardTest.cpp` uses `DaisyField`, 16 keyboard states, eight knobs, four CV inputs, LED driver and DAC output;
+- `field/Midi/Midi.cpp` uses `hw.midi.StartReceive()`, `Listen()`, `HasEvents()` and `PopEvent()`;
+- `field/KeyboardTest/Makefile` shows the conventional libDaisy/DaisySP relative layout and core Makefile include.
+
+These are the preferred starting references for P0 because they exercise the actual target BSP.
+
+### `electro-smith/DaisySP`
+
+Inspected commit:
+
+`599511b740f8f3a9b8db72a0642aa45b8a23c3a3`
+
+Reserved for P1. R0 does not depend on DaisySP.
+
+## Embedded Audio Mine leads reverified upstream
+
+### `bkshepherd/DaisySeedProjects`
+
+Inspected commit:
+
+`80feee11f26a401ae324de75d9266e63ed82deb2`
+
+Verified `BaseHardwareModule` API includes audio lifecycle, control processing, hardware capability queries, MIDI/display members, and protected initialization helpers. `GuitarPedal125B` derives from that base and overrides `Init()`.
+
+**Borrowed idea:** keep hardware-facing details behind a stable adapter rather than allowing application semantics to spread through BSP calls.
+
+### `hasanalpdoyduk/Teensy_Drum_Machine`
+
+Inspected commit:
+
+`187393d087a406134deee944edcd400aad7c7367`
+
+Verified repository tree contains separate `ClockEngine`, `InstrumentManager`, `StorageManager`, `MidiEngine`, input managers and `Sequencer`. `Firmware/Sequencer.h` exposes direct `toggleStep`, `setStep`, `advanceStep`, `resetStep`, and selected-instrument state around a fixed pattern array.
+
+**Borrowed idea:** direct 16-step semantics plus clear subsystem boundaries. HydraPulse core code was written independently for a fixed-size, libDaisy-free host-testable implementation.
+
+### `kencul/STM32F4-AudioSynthesis`
+
+Inspected commit:
+
+`b1de88d6a3967327cc8a0eeccba1ebad524ccbd2`
+
+Verified `.github/workflows/tests.yml` configures/builds/runs `tests` with CMake/CTest. `tests/CMakeLists.txt` explicitly compiles hardware-independent DSP sources on host while excluding HAL/hardware dependencies. The target application also uses Cortex DWT cycle-counter instrumentation.
+
+**Borrowed idea:** host CMake/CTest for pure DSP/core plus separate target timing instrumentation.
+
+## Not imported into R0
+
+No community project source file is copied wholesale into the R0 core. The source ledger preserves exact upstream commits and the architectural concepts used.
+
+License constraints were explicitly disabled by the user for this bootstrap turn; provenance was still retained so later release policy can be re-enabled without reconstructing history.
diff --git a/field/HydraPulse/references/r0/docs/STATUS.md b/field/HydraPulse/references/r0/docs/STATUS.md
new file mode 100644
index 000000000..a7a11cb36
--- /dev/null
+++ b/field/HydraPulse/references/r0/docs/STATUS.md
@@ -0,0 +1,35 @@
+# Status
+
+## Implemented in this bootstrap
+
+Evidence class: **VERIFIED locally** only after the accompanying validation commands are run in the generated package.
+
+- R0 pure C++ core files exist.
+- Native test sources exist.
+- CMake/CTest harness exists.
+- Repository/provenance validators exist.
+- GitHub CI definition exists.
+- Upstream references are pinned to full inspected commit SHAs.
+- P0 evidence structure and implementation contract exist.
+
+## Future work / not claimed
+
+- **NOT_RUN:** GitHub CI for this bootstrap until it is actually pushed.
+- **NOT_RUN:** ARM/libDaisy build.
+- **NOT_RUN:** Field firmware execution.
+- **NOT_RUN:** all physical P0 measurements.
+- **PROPOSED:** P1 four-voice synthesis and sonic regression.
+- **PROPOSED:** A/B/Fill.
+- **DEFERRED:** advanced sequencing.
+- **DEFERRED:** Hydrasynth MIDI/CV/audio integration.
+
+## Evidence vocabulary
+
+Use only:
+
+- `VERIFIED` - directly observed/measured in the stated fixture or executed test;
+- `DERIVED` - computed from verified evidence with method shown;
+- `PROPOSED` - design or intended implementation;
+- `ASSUMED` - assumption needed to proceed and not yet verified;
+- `HOLD` - blocked or evidence too weak;
+- `NOT_RUN` - test or action has not been executed.
diff --git a/field/HydraPulse/references/r0/docs/TEST_STRATEGY.md b/field/HydraPulse/references/r0/docs/TEST_STRATEGY.md
new file mode 100644
index 000000000..a18f092fb
--- /dev/null
+++ b/field/HydraPulse/references/r0/docs/TEST_STRATEGY.md
@@ -0,0 +1,72 @@
+# Test strategy
+
+## Evidence layers
+
+HydraPulse does not collapse unlike tests into one green badge.
+
+| Layer | What it proves | What it does not prove |
+|---|---|---|
+| Host unit tests | deterministic core semantics | ARM timing, Field I/O, audio quality |
+| Deterministic stress tests | repeatable invariants under many operations | true fuzz coverage or target behavior |
+| ASan/UBSan | common host memory/UB failures | embedded memory map correctness |
+| ARM build | toolchain/API/build integration | electrical behavior or real-time margin |
+| Target instrumentation | execution on MCU | analog audio/CV quality by itself |
+| Bench measurements | actual physical behavior | general correctness outside measured conditions |
+
+## R0 native tests
+
+### Pattern
+
+- default clear state;
+- set/toggle/clear;
+- bounds behavior;
+- full 16-bit mask;
+- deterministic 20k-operation property stress against a reference model.
+
+### Sequencer
+
+- stopped state emits nothing;
+- Start begins at step zero;
+- 16-step wrap;
+- deterministic ascending track trigger order;
+- Stop halts emission;
+- restart policy is explicit.
+
+### Sample clock
+
+- exact 120 BPM / 48 kHz / 16th-note interval;
+- non-integer BPM long-run schedule;
+- event location remains within one sample of ideal time;
+- invalid tempo/sample-rate rejection.
+
+### Pickup
+
+- no premature parameter jump;
+- capture inside tolerance;
+- capture on crossing;
+- direct tracking after capture;
+- normalized clamping.
+
+## CI policy
+
+R0 CI runs:
+
+1. ordinary host configure/build/CTest;
+2. repository validator;
+3. provenance validator;
+4. Python validator tests;
+5. independent ASan+UBSan build/test job.
+
+ARM/libDaisy build enters CI with P0. No public CI workflow may flash physical hardware.
+
+## Future P1 sonic regression
+
+Each synthesized voice will receive deterministic scenarios such as:
+
+- fixed trigger cadence;
+- fixed parameter tuples including corners;
+- fixed sample rate/block size;
+- statistical and waveform-derived signatures that tolerate intended floating-point variation;
+- explicit target-vs-host comparison where the same algorithm runs on both.
+
+Waveform hashing alone is not sufficient if benign compiler/FPU differences cause false failures. Use decision-useful metrics: peak/RMS, decay time, spectral centroid/bands, zero-crossing or envelope characteristics, and bounded numerical tolerances.
diff --git a/field/HydraPulse/references/r0/evidence/README.md b/field/HydraPulse/references/r0/evidence/README.md
new file mode 100644
index 000000000..34771a8e1
--- /dev/null
+++ b/field/HydraPulse/references/r0/evidence/README.md
@@ -0,0 +1,28 @@
+# Evidence
+
+This directory stores raw and derived evidence without promoting a test simply because a file exists.
+
+Recommended P0 structure:
+
+```text
+evidence/p0//
+├── fixture.md
+├── build.log
+├── firmware.sha256
+├── cold_boot_serial.log
+├── controls.csv
+├── cv_inputs.csv
+├── cv_outputs.csv
+├── gate_io.csv
+├── midi.log
+├── audio/
+│ ├── method.md
+│ └── raw-captures...
+└── timing/
+ ├── method.md
+ └── raw-captures...
+```
+
+Each run should state one of `VERIFIED`, `DERIVED`, `PROPOSED`, `ASSUMED`, `HOLD`, or `NOT_RUN` for every claimed result.
+
+Do not commit commercial manuals or copyrighted factory-pattern books merely to make a provenance folder look busy.
diff --git a/field/HydraPulse/references/r0/firmware/field_truth/README.md b/field/HydraPulse/references/r0/firmware/field_truth/README.md
new file mode 100644
index 000000000..f1699642b
--- /dev/null
+++ b/field/HydraPulse/references/r0/firmware/field_truth/README.md
@@ -0,0 +1,39 @@
+# P0 Field Truth firmware contract
+
+This directory intentionally contains the P0 implementation contract before a target firmware claim is made.
+
+## Verified libDaisy API surface at pinned R0 reference
+
+At `libDaisy` commit `cc146d5065dd8286078a662e2830bf820c37a612`, `DaisyField` exposes the primitives needed for P0:
+
+- `Init()`;
+- `StartAudio()` / `StopAudio()`;
+- `StartAdc()`;
+- `ProcessAnalogControls()` / `ProcessDigitalControls()`;
+- `KeyboardState()` / rising/falling edges;
+- `GetKnobValue()`;
+- `GetCvValue()`;
+- `GetSwitch()`;
+- `SetCvOut1()` / `SetCvOut2()`;
+- public `gate_in`, `gate_out`, `midi`, `display`, `led_driver`, `seed`.
+
+`DaisySeed` exposes `SetLed()`, `SetTestPoint()`, `StartLog()`, `Print()` and `PrintLine()`.
+
+## Required P0 firmware behavior
+
+The first implementation should:
+
+1. stereo-pass audio in the callback without serial/display work in that callback;
+2. collect bounded callback timing instrumentation for the full callback interval;
+3. log raw 16-key scan indices outside the callback;
+4. report eight knob and four CV normalized readings at a rate-limited interval;
+5. report Gate In state/edges;
+6. report received MIDI events;
+7. provide OLED/LED functional indication outside the callback;
+8. keep CV/Gate outputs in a defined safe state except during an explicit momentary output test;
+9. emit machine-parseable telemetry such as `[HPF] key index=...`;
+10. never claim a physical mapping until the bench log ties raw indices to physical keys.
+
+## Completion
+
+A successful ARM build will be recorded as build evidence only. P0 remains incomplete until `evidence/p0/` contains real hardware data for controls, I/O, audio, MIDI, and timing.
diff --git a/field/HydraPulse/references/r0/hardware/README.md b/field/HydraPulse/references/r0/hardware/README.md
new file mode 100644
index 000000000..76d7b0826
--- /dev/null
+++ b/field/HydraPulse/references/r0/hardware/README.md
@@ -0,0 +1,15 @@
+# Hardware notes
+
+Hardware-specific mappings are deliberately absent from R0 unless directly measured or taken from the verified official BSP.
+
+P0 will add:
+
+- Field serial number / hardware revision where available;
+- test fixture and instruments;
+- key scan-index map;
+- knob endpoint/direction table;
+- CV input calibration observations;
+- CV output code/voltage measurements;
+- Gate I/O levels;
+- audio loopback fixture details;
+- callback probe point / GPIO method.
diff --git a/field/HydraPulse/references/r0/src/core/ControlPickup.h b/field/HydraPulse/references/r0/src/core/ControlPickup.h
new file mode 100644
index 000000000..174c17cb7
--- /dev/null
+++ b/field/HydraPulse/references/r0/src/core/ControlPickup.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include
+#include
+
+namespace hydrapulse::core
+{
+
+class ControlPickup
+{
+ public:
+ explicit ControlPickup(float tolerance = 0.015f) noexcept
+ : tolerance_(std::max(0.0f, tolerance))
+ {
+ }
+
+ void Reset(float target, float physical) noexcept
+ {
+ target_ = Clamp(target);
+ value_ = target_;
+ previous_ = Clamp(physical);
+ has_previous_ = true;
+ captured_ = std::fabs(previous_ - target_) <= tolerance_;
+ if(captured_)
+ value_ = previous_;
+ }
+
+ [[nodiscard]] bool Update(float physical) noexcept
+ {
+ const float current = Clamp(physical);
+
+ if(!has_previous_)
+ {
+ previous_ = current;
+ has_previous_ = true;
+ }
+
+ if(captured_)
+ {
+ previous_ = current;
+ value_ = current;
+ return true;
+ }
+
+ const bool inside = std::fabs(current - target_) <= tolerance_;
+ const bool crossed = (previous_ <= target_ && current >= target_)
+ || (previous_ >= target_ && current <= target_);
+
+ previous_ = current;
+ if(inside || crossed)
+ {
+ captured_ = true;
+ value_ = current;
+ return true;
+ }
+
+ return false;
+ }
+
+ [[nodiscard]] bool Captured() const noexcept { return captured_; }
+ [[nodiscard]] float Value() const noexcept { return value_; }
+ [[nodiscard]] float Target() const noexcept { return target_; }
+
+ private:
+ [[nodiscard]] static float Clamp(float value) noexcept
+ {
+ return std::clamp(value, 0.0f, 1.0f);
+ }
+
+ float tolerance_{0.015f};
+ float target_{0.0f};
+ float value_{0.0f};
+ float previous_{0.0f};
+ bool has_previous_{false};
+ bool captured_{false};
+};
+
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/references/r0/src/core/Pattern16.h b/field/HydraPulse/references/r0/src/core/Pattern16.h
new file mode 100644
index 000000000..4711ba015
--- /dev/null
+++ b/field/HydraPulse/references/r0/src/core/Pattern16.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include
+#include
+#include
+
+namespace hydrapulse::core
+{
+
+template
+class Pattern16
+{
+ public:
+ static_assert(Tracks > 0, "Pattern16 requires at least one track");
+ static_assert(Tracks <= 32, "Pattern16 track masks are limited to 32 tracks");
+
+ static constexpr std::size_t kSteps = 16;
+
+ bool Set(std::size_t track, std::size_t step, bool active) noexcept
+ {
+ if(!Valid(track, step))
+ return false;
+
+ const auto bit = static_cast(std::uint16_t{1u} << step);
+ if(active)
+ masks_[track] = static_cast(masks_[track] | bit);
+ else
+ masks_[track] = static_cast(masks_[track] & ~bit);
+ return true;
+ }
+
+ bool Toggle(std::size_t track, std::size_t step) noexcept
+ {
+ if(!Valid(track, step))
+ return false;
+
+ const auto bit = static_cast(std::uint16_t{1u} << step);
+ masks_[track] = static_cast(masks_[track] ^ bit);
+ return true;
+ }
+
+ [[nodiscard]] bool IsActive(std::size_t track, std::size_t step) const noexcept
+ {
+ if(!Valid(track, step))
+ return false;
+
+ const auto bit = static_cast(std::uint16_t{1u} << step);
+ return (masks_[track] & bit) != 0u;
+ }
+
+ bool ClearTrack(std::size_t track) noexcept
+ {
+ if(track >= Tracks)
+ return false;
+ masks_[track] = 0u;
+ return true;
+ }
+
+ void Clear() noexcept { masks_.fill(0u); }
+
+ [[nodiscard]] std::uint16_t TrackMask(std::size_t track) const noexcept
+ {
+ return track < Tracks ? masks_[track] : 0u;
+ }
+
+ private:
+ [[nodiscard]] static constexpr bool Valid(std::size_t track, std::size_t step) noexcept
+ {
+ return track < Tracks && step < kSteps;
+ }
+
+ std::array masks_{};
+};
+
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/references/r0/src/core/SampleAccurateStepClock.h b/field/HydraPulse/references/r0/src/core/SampleAccurateStepClock.h
new file mode 100644
index 000000000..07cd10b03
--- /dev/null
+++ b/field/HydraPulse/references/r0/src/core/SampleAccurateStepClock.h
@@ -0,0 +1,100 @@
+#pragma once
+
+#include
+#include
+#include
+
+namespace hydrapulse::core
+{
+
+class SampleAccurateStepClock
+{
+ public:
+ static constexpr std::uint64_t kBpmScale = 1'000'000u;
+
+ explicit SampleAccurateStepClock(std::uint32_t sample_rate = 48'000u,
+ double bpm = 120.0,
+ std::uint32_t steps_per_beat = 4u) noexcept
+ : sample_rate_(sample_rate), steps_per_beat_(steps_per_beat)
+ {
+ (void)SetTempoBpm(bpm);
+ RecomputeThreshold();
+ }
+
+ bool SetSampleRate(std::uint32_t sample_rate) noexcept
+ {
+ if(sample_rate == 0u)
+ return false;
+ sample_rate_ = sample_rate;
+ RecomputeThreshold();
+ Reset();
+ return true;
+ }
+
+ bool SetTempoBpm(double bpm) noexcept
+ {
+ if(!std::isfinite(bpm) || bpm <= 0.0 || bpm > 1000.0)
+ return false;
+
+ const long double scaled = static_cast(bpm) * kBpmScale;
+ if(scaled > static_cast(std::numeric_limits::max()))
+ return false;
+
+ tempo_u_bpm_ = static_cast(std::llround(scaled));
+ RecomputeIncrement();
+ Reset();
+ return tempo_u_bpm_ != 0u;
+ }
+
+ void Reset() noexcept { phase_ = 0u; }
+
+ [[nodiscard]] bool ProcessSample() noexcept
+ {
+ phase_ += phase_increment_;
+ if(phase_ >= threshold_)
+ {
+ phase_ -= threshold_;
+ return true;
+ }
+ return false;
+ }
+
+ [[nodiscard]] std::uint64_t SamplesUntilNextStep() const noexcept
+ {
+ if(phase_increment_ == 0u || threshold_ <= phase_)
+ return 0u;
+
+ const auto remaining = threshold_ - phase_;
+ return (remaining + phase_increment_ - 1u) / phase_increment_;
+ }
+
+ [[nodiscard]] std::uint32_t SampleRate() const noexcept { return sample_rate_; }
+
+ [[nodiscard]] double TempoBpm() const noexcept
+ {
+ return static_cast(tempo_u_bpm_) / static_cast(kBpmScale);
+ }
+
+ [[nodiscard]] std::uint32_t StepsPerBeat() const noexcept { return steps_per_beat_; }
+
+ private:
+ void RecomputeThreshold() noexcept
+ {
+ threshold_ = static_cast(sample_rate_) * 60u * kBpmScale;
+ RecomputeIncrement();
+ }
+
+ void RecomputeIncrement() noexcept
+ {
+ phase_increment_ = tempo_u_bpm_ * static_cast(steps_per_beat_);
+ }
+
+ std::uint32_t sample_rate_{48'000u};
+ std::uint32_t steps_per_beat_{4u};
+ std::uint64_t tempo_u_bpm_{120u * kBpmScale};
+ std::uint64_t threshold_{48'000u * 60u * kBpmScale};
+ std::uint64_t phase_increment_{120u * kBpmScale * 4u};
+ std::uint64_t phase_{0u};
+};
+
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/references/r0/src/core/StepSequencer.h b/field/HydraPulse/references/r0/src/core/StepSequencer.h
new file mode 100644
index 000000000..69f03b0a5
--- /dev/null
+++ b/field/HydraPulse/references/r0/src/core/StepSequencer.h
@@ -0,0 +1,84 @@
+#pragma once
+
+#include "src/core/Pattern16.h"
+
+#include
+#include
+#include
+
+namespace hydrapulse::core
+{
+
+template
+class StepSequencer
+{
+ public:
+ using Pattern = Pattern16;
+
+ struct Tick
+ {
+ bool emitted{false};
+ std::uint8_t step{0};
+ std::uint32_t track_mask{0};
+ };
+
+ explicit StepSequencer(const Pattern& pattern) noexcept : pattern_(pattern) {}
+
+ void Start() noexcept
+ {
+ running_ = true;
+ current_step_ = 0u;
+ }
+
+ void Stop() noexcept { running_ = false; }
+
+ [[nodiscard]] bool Running() const noexcept { return running_; }
+
+ [[nodiscard]] std::uint8_t CurrentStep() const noexcept { return current_step_; }
+
+ [[nodiscard]] Tick EmitAndAdvance() noexcept
+ {
+ if(!running_)
+ return {};
+
+ Tick result;
+ result.emitted = true;
+ result.step = current_step_;
+ result.track_mask = ActiveTrackMask(current_step_);
+
+ current_step_ = static_cast((current_step_ + 1u) % Pattern::kSteps);
+ return result;
+ }
+
+ template
+ static void ForEachTriggeredTrack(const Tick& tick, Fn&& fn)
+ {
+ if(!tick.emitted)
+ return;
+
+ for(std::size_t track = 0; track < Tracks; ++track)
+ {
+ const auto bit = std::uint32_t{1u} << track;
+ if((tick.track_mask & bit) != 0u)
+ std::forward(fn)(track);
+ }
+ }
+
+ private:
+ [[nodiscard]] std::uint32_t ActiveTrackMask(std::uint8_t step) const noexcept
+ {
+ std::uint32_t mask = 0u;
+ for(std::size_t track = 0; track < Tracks; ++track)
+ {
+ if(pattern_.IsActive(track, step))
+ mask |= std::uint32_t{1u} << track;
+ }
+ return mask;
+ }
+
+ const Pattern& pattern_;
+ bool running_{false};
+ std::uint8_t current_step_{0};
+};
+
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/references/r0/tests/native/CMakeLists.txt b/field/HydraPulse/references/r0/tests/native/CMakeLists.txt
new file mode 100644
index 000000000..657829f52
--- /dev/null
+++ b/field/HydraPulse/references/r0/tests/native/CMakeLists.txt
@@ -0,0 +1,10 @@
+function(add_hpf_test test_name source_file)
+ add_executable(${test_name} ${source_file})
+ target_link_libraries(${test_name} PRIVATE hydrapulse_core)
+ add_test(NAME ${test_name} COMMAND ${test_name})
+endfunction()
+
+add_hpf_test(test_pattern16 test_pattern16.cpp)
+add_hpf_test(test_step_sequencer test_step_sequencer.cpp)
+add_hpf_test(test_sample_clock test_sample_clock.cpp)
+add_hpf_test(test_control_pickup test_control_pickup.cpp)
diff --git a/field/HydraPulse/references/r0/tests/native/test_control_pickup.cpp b/field/HydraPulse/references/r0/tests/native/test_control_pickup.cpp
new file mode 100644
index 000000000..0863d1259
--- /dev/null
+++ b/field/HydraPulse/references/r0/tests/native/test_control_pickup.cpp
@@ -0,0 +1,36 @@
+#include "src/core/ControlPickup.h"
+#include "tests/native/test_support.h"
+
+int main()
+{
+ using hydrapulse::core::ControlPickup;
+
+ ControlPickup pickup(0.01f);
+ pickup.Reset(0.70f, 0.20f);
+ HPF_CHECK(!pickup.Captured());
+ HPF_CHECK_NEAR(pickup.Value(), 0.70f, 1.0e-6f);
+
+ HPF_CHECK(!pickup.Update(0.40f));
+ HPF_CHECK(!pickup.Update(0.60f));
+ HPF_CHECK(!pickup.Captured());
+
+ HPF_CHECK(pickup.Update(0.695f));
+ HPF_CHECK(pickup.Captured());
+ HPF_CHECK_NEAR(pickup.Value(), 0.695f, 1.0e-6f);
+
+ HPF_CHECK(pickup.Update(0.30f));
+ HPF_CHECK_NEAR(pickup.Value(), 0.30f, 1.0e-6f);
+
+ // Crossing the target between samples captures even if neither sample lands inside tolerance.
+ pickup.Reset(0.50f, 0.20f);
+ HPF_CHECK(!pickup.Update(0.48f));
+ HPF_CHECK(pickup.Update(0.52f));
+ HPF_CHECK(pickup.Captured());
+
+ // Input and target are clamped to the normalized control domain.
+ pickup.Reset(1.5f, 2.0f);
+ HPF_CHECK(pickup.Captured());
+ HPF_CHECK_NEAR(pickup.Value(), 1.0f, 1.0e-6f);
+
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/references/r0/tests/native/test_pattern16.cpp b/field/HydraPulse/references/r0/tests/native/test_pattern16.cpp
new file mode 100644
index 000000000..a7113ee50
--- /dev/null
+++ b/field/HydraPulse/references/r0/tests/native/test_pattern16.cpp
@@ -0,0 +1,64 @@
+#include "src/core/Pattern16.h"
+#include "tests/native/test_support.h"
+
+#include
+#include
+
+int main()
+{
+ using hydrapulse::core::Pattern16;
+ Pattern16<8> pattern;
+
+ for(std::size_t track = 0; track < 8; ++track)
+ for(std::size_t step = 0; step < 16; ++step)
+ HPF_CHECK(!pattern.IsActive(track, step));
+
+ HPF_CHECK(pattern.Set(3, 7, true));
+ HPF_CHECK(pattern.IsActive(3, 7));
+ HPF_CHECK(pattern.Toggle(3, 7));
+ HPF_CHECK(!pattern.IsActive(3, 7));
+
+ HPF_CHECK(!pattern.Set(8, 0, true));
+ HPF_CHECK(!pattern.Set(0, 16, true));
+ HPF_CHECK(!pattern.Toggle(99, 99));
+ HPF_CHECK(!pattern.IsActive(99, 99));
+
+ for(std::size_t step = 0; step < 16; ++step)
+ HPF_CHECK(pattern.Set(2, step, true));
+ HPF_CHECK_EQ(pattern.TrackMask(2), static_cast(0xFFFFu));
+ HPF_CHECK(pattern.ClearTrack(2));
+ HPF_CHECK_EQ(pattern.TrackMask(2), static_cast(0u));
+
+ // Deterministic property stress: mirror the bit operations in a reference model.
+ std::mt19937 rng(0x48504631u);
+ std::uint16_t reference[8]{};
+ for(int i = 0; i < 20'000; ++i)
+ {
+ const std::size_t track = rng() % 8u;
+ const std::size_t step = rng() % 16u;
+ const bool active = (rng() & 1u) != 0u;
+ const auto bit = static_cast(std::uint16_t{1u} << step);
+
+ if((rng() & 1u) != 0u)
+ {
+ HPF_CHECK(pattern.Set(track, step, active));
+ if(active)
+ reference[track] = static_cast(reference[track] | bit);
+ else
+ reference[track] = static_cast(reference[track] & ~bit);
+ }
+ else
+ {
+ HPF_CHECK(pattern.Toggle(track, step));
+ reference[track] = static_cast(reference[track] ^ bit);
+ }
+
+ HPF_CHECK_EQ(pattern.TrackMask(track), reference[track]);
+ }
+
+ pattern.Clear();
+ for(std::size_t track = 0; track < 8; ++track)
+ HPF_CHECK_EQ(pattern.TrackMask(track), static_cast(0u));
+
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/references/r0/tests/native/test_sample_clock.cpp b/field/HydraPulse/references/r0/tests/native/test_sample_clock.cpp
new file mode 100644
index 000000000..77c44a1da
--- /dev/null
+++ b/field/HydraPulse/references/r0/tests/native/test_sample_clock.cpp
@@ -0,0 +1,47 @@
+#include "src/core/SampleAccurateStepClock.h"
+#include "tests/native/test_support.h"
+
+#include
+#include
+
+int main()
+{
+ using hydrapulse::core::SampleAccurateStepClock;
+
+ SampleAccurateStepClock clock(48'000u, 120.0, 4u);
+ HPF_CHECK_EQ(clock.SamplesUntilNextStep(), std::uint64_t{6'000u});
+
+ std::uint64_t samples = 0u;
+ while(!clock.ProcessSample())
+ ++samples;
+ ++samples; // include the triggering sample
+ HPF_CHECK_EQ(samples, std::uint64_t{6'000u});
+
+ HPF_CHECK(clock.SetTempoBpm(123.4567));
+ HPF_CHECK_NEAR(clock.TempoBpm(), 123.4567, 0.000001);
+
+ constexpr std::uint64_t kEvents = 1024u;
+ std::uint64_t event_count = 0u;
+ std::uint64_t sample_index = 0u;
+
+ const long double exact_interval =
+ (48'000.0L * 60.0L) / (123.4567L * 4.0L);
+
+ while(event_count < kEvents)
+ {
+ ++sample_index;
+ if(clock.ProcessSample())
+ {
+ ++event_count;
+ const long double ideal = exact_interval * static_cast(event_count);
+ const long double error = std::fabs(static_cast(sample_index) - ideal);
+ HPF_CHECK(error <= 1.0L);
+ }
+ }
+
+ HPF_CHECK(!clock.SetTempoBpm(0.0));
+ HPF_CHECK(!clock.SetTempoBpm(-120.0));
+ HPF_CHECK(!clock.SetSampleRate(0u));
+
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/references/r0/tests/native/test_step_sequencer.cpp b/field/HydraPulse/references/r0/tests/native/test_step_sequencer.cpp
new file mode 100644
index 000000000..49203ea1f
--- /dev/null
+++ b/field/HydraPulse/references/r0/tests/native/test_step_sequencer.cpp
@@ -0,0 +1,58 @@
+#include "src/core/StepSequencer.h"
+#include "tests/native/test_support.h"
+
+#include
+#include
+#include
+
+int main()
+{
+ using Pattern = hydrapulse::core::Pattern16<4>;
+ using Sequencer = hydrapulse::core::StepSequencer<4>;
+
+ Pattern pattern;
+ HPF_CHECK(pattern.Set(0, 0, true));
+ HPF_CHECK(pattern.Set(2, 0, true));
+ HPF_CHECK(pattern.Set(1, 15, true));
+
+ Sequencer sequencer(pattern);
+ HPF_CHECK(!sequencer.EmitAndAdvance().emitted);
+
+ sequencer.Start();
+ HPF_CHECK(sequencer.Running());
+
+ auto first = sequencer.EmitAndAdvance();
+ HPF_CHECK(first.emitted);
+ HPF_CHECK_EQ(static_cast(first.step), 0);
+ HPF_CHECK_EQ(first.track_mask, std::uint32_t{0b0101u});
+
+ std::array order{};
+ std::size_t count = 0;
+ Sequencer::ForEachTriggeredTrack(first, [&](std::size_t track) { order[count++] = track; });
+ HPF_CHECK_EQ(count, std::size_t{2});
+ HPF_CHECK_EQ(order[0], std::size_t{0});
+ HPF_CHECK_EQ(order[1], std::size_t{2});
+
+ // Steps 1..15; verify final step and wrap to zero.
+ Sequencer::Tick tick{};
+ for(int expected = 1; expected <= 15; ++expected)
+ {
+ tick = sequencer.EmitAndAdvance();
+ HPF_CHECK_EQ(static_cast(tick.step), expected);
+ }
+ HPF_CHECK_EQ(tick.track_mask, std::uint32_t{0b0010u});
+
+ auto wrapped = sequencer.EmitAndAdvance();
+ HPF_CHECK_EQ(static_cast(wrapped.step), 0);
+ HPF_CHECK_EQ(wrapped.track_mask, std::uint32_t{0b0101u});
+
+ sequencer.Stop();
+ HPF_CHECK(!sequencer.Running());
+ HPF_CHECK(!sequencer.EmitAndAdvance().emitted);
+
+ // Start is intentionally deterministic and restarts at step zero.
+ sequencer.Start();
+ HPF_CHECK_EQ(static_cast(sequencer.EmitAndAdvance().step), 0);
+
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/references/r0/tests/native/test_support.h b/field/HydraPulse/references/r0/tests/native/test_support.h
new file mode 100644
index 000000000..26953d842
--- /dev/null
+++ b/field/HydraPulse/references/r0/tests/native/test_support.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include
+#include
+#include
+
+#define HPF_CHECK(expr) \
+ do \
+ { \
+ if(!(expr)) \
+ { \
+ std::cerr << __FILE__ << ':' << __LINE__ << " CHECK failed: " #expr << '\n'; \
+ return EXIT_FAILURE; \
+ } \
+ } while(false)
+
+#define HPF_CHECK_EQ(lhs, rhs) \
+ do \
+ { \
+ const auto hpf_lhs = (lhs); \
+ const auto hpf_rhs = (rhs); \
+ if(!(hpf_lhs == hpf_rhs)) \
+ { \
+ std::cerr << __FILE__ << ':' << __LINE__ << " CHECK_EQ failed: " #lhs " != " #rhs \
+ << " (" << hpf_lhs << " vs " << hpf_rhs << ")\n"; \
+ return EXIT_FAILURE; \
+ } \
+ } while(false)
+
+#define HPF_CHECK_NEAR(lhs, rhs, tol) \
+ do \
+ { \
+ const auto hpf_lhs = static_cast(lhs); \
+ const auto hpf_rhs = static_cast(rhs); \
+ const auto hpf_tol = static_cast(tol); \
+ if(std::fabs(hpf_lhs - hpf_rhs) > hpf_tol) \
+ { \
+ std::cerr << __FILE__ << ':' << __LINE__ << " CHECK_NEAR failed: " #lhs " vs " #rhs \
+ << " (" << hpf_lhs << " vs " << hpf_rhs << ", tol=" << hpf_tol << ")\n"; \
+ return EXIT_FAILURE; \
+ } \
+ } while(false)
diff --git a/field/HydraPulse/references/r0/tests/python/test_validators.py b/field/HydraPulse/references/r0/tests/python/test_validators.py
new file mode 100644
index 000000000..e719a9f9c
--- /dev/null
+++ b/field/HydraPulse/references/r0/tests/python/test_validators.py
@@ -0,0 +1,32 @@
+import subprocess
+import sys
+import unittest
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parents[2]
+
+
+class ValidatorTests(unittest.TestCase):
+ def run_tool(self, relative_path: str) -> None:
+ result = subprocess.run(
+ [sys.executable, str(ROOT / relative_path)],
+ cwd=ROOT,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ self.assertEqual(
+ result.returncode,
+ 0,
+ msg=f"{relative_path} failed\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}",
+ )
+
+ def test_repository_structure(self):
+ self.run_tool("tools/validate_repo.py")
+
+ def test_provenance(self):
+ self.run_tool("tools/validate_provenance.py")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/field/HydraPulse/references/r0/third_party/manifest.json b/field/HydraPulse/references/r0/third_party/manifest.json
new file mode 100644
index 000000000..22806645c
--- /dev/null
+++ b/field/HydraPulse/references/r0/third_party/manifest.json
@@ -0,0 +1,90 @@
+{
+ "schema_version": 1,
+ "generated_utc_date": "2026-08-16",
+ "license_gate": "disabled_for_this_bootstrap_turn_by_user_instruction",
+ "sources": [
+ {
+ "name": "Denys/embedded-audio-mine",
+ "repository": "https://github.com/Denys/embedded-audio-mine",
+ "ref": "1113f0c6255c24a6655be968fb5aa115b8d5f7e9",
+ "role": "discovery/provenance source",
+ "verified_assets": [
+ "README discovery model",
+ "digests/2026-05-26-chat-backfill.md",
+ "digests/2026-07-16.md"
+ ],
+ "reuse": "research only"
+ },
+ {
+ "name": "electro-smith/libDaisy",
+ "repository": "https://github.com/electro-smith/libDaisy",
+ "ref": "cc146d5065dd8286078a662e2830bf820c37a612",
+ "role": "planned direct P0 dependency",
+ "verified_assets": [
+ "src/daisy_field.h",
+ "src/daisy_seed.h",
+ "src/hid/gatein.h",
+ "examples/SerialPrint/SerialPrint.cpp"
+ ],
+ "reuse": "P0 dependency after target integration"
+ },
+ {
+ "name": "electro-smith/DaisySP",
+ "repository": "https://github.com/electro-smith/DaisySP",
+ "ref": "599511b740f8f3a9b8db72a0642aa45b8a23c3a3",
+ "role": "planned P1 DSP dependency",
+ "verified_assets": [
+ "repository head and license file"
+ ],
+ "reuse": "deferred until P1"
+ },
+ {
+ "name": "electro-smith/DaisyExamples",
+ "repository": "https://github.com/electro-smith/DaisyExamples",
+ "ref": "259ed82c7d4c0d7699f695945e6e111b7dc7cb27",
+ "role": "official Field implementation reference",
+ "verified_assets": [
+ "field/KeyboardTest/KeyboardTest.cpp",
+ "field/KeyboardTest/Makefile",
+ "field/Midi/Midi.cpp"
+ ],
+ "reuse": "reference/scaffolding"
+ },
+ {
+ "name": "bkshepherd/DaisySeedProjects",
+ "repository": "https://github.com/bkshepherd/DaisySeedProjects",
+ "ref": "80feee11f26a401ae324de75d9266e63ed82deb2",
+ "role": "hardware abstraction architecture reference",
+ "verified_assets": [
+ "Software/GuitarPedal/Hardware-Modules/base_hardware_module.h",
+ "Software/GuitarPedal/Hardware-Modules/guitar_pedal_125b.h"
+ ],
+ "reuse": "concept: isolate hardware-facing layer behind stable interface"
+ },
+ {
+ "name": "hasanalpdoyduk/Teensy_Drum_Machine",
+ "repository": "https://github.com/hasanalpdoyduk/Teensy_Drum_Machine",
+ "ref": "187393d087a406134deee944edcd400aad7c7367",
+ "role": "drum-machine architecture reference",
+ "verified_assets": [
+ "Firmware/Sequencer.h",
+ "Firmware/ClockEngine.h",
+ "Firmware/InstrumentManager.h",
+ "Firmware/StorageManager.h"
+ ],
+ "reuse": "concept: manager boundaries and direct 16-step sequencing"
+ },
+ {
+ "name": "kencul/STM32F4-AudioSynthesis",
+ "repository": "https://github.com/kencul/STM32F4-AudioSynthesis",
+ "ref": "b1de88d6a3967327cc8a0eeccba1ebad524ccbd2",
+ "role": "test/benchmark architecture reference",
+ "verified_assets": [
+ ".github/workflows/tests.yml",
+ "tests/CMakeLists.txt",
+ "App/Src/app.cpp"
+ ],
+ "reuse": "concept: compile hardware-independent DSP under host CMake/CTest and keep target timing instrumentation separate"
+ }
+ ]
+}
diff --git a/field/HydraPulse/references/r0/tools/validate_provenance.py b/field/HydraPulse/references/r0/tools/validate_provenance.py
new file mode 100644
index 000000000..256b74055
--- /dev/null
+++ b/field/HydraPulse/references/r0/tools/validate_provenance.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+from pathlib import Path
+import json
+import re
+import sys
+
+ROOT = Path(__file__).resolve().parents[1]
+SHA40 = re.compile(r"^[0-9a-f]{40}$")
+
+
+def load(path: str):
+ with (ROOT / path).open("r", encoding="utf-8") as handle:
+ return json.load(handle)
+
+
+def validate_entries(entries, container_name: str) -> list[str]:
+ errors = []
+ for index, entry in enumerate(entries):
+ prefix = f"{container_name}[{index}]"
+ for field in ("name", "repository", "ref"):
+ if not entry.get(field):
+ errors.append(f"{prefix}: missing {field}")
+ ref = entry.get("ref", "")
+ if ref and not SHA40.fullmatch(ref):
+ errors.append(f"{prefix}: ref is not a full 40-hex commit SHA: {ref}")
+ repo = entry.get("repository", "")
+ if repo and not repo.startswith("https://github.com/"):
+ errors.append(f"{prefix}: repository is not a GitHub HTTPS URL: {repo}")
+ return errors
+
+
+def main() -> int:
+ lock = load("deps.lock.json")
+ manifest = load("third_party/manifest.json")
+
+ errors = []
+ errors += validate_entries(lock.get("dependencies", []), "dependencies")
+ errors += validate_entries(manifest.get("sources", []), "sources")
+
+ direct = {entry["repository"]: entry["ref"] for entry in lock.get("dependencies", [])}
+ sources = {entry["repository"]: entry["ref"] for entry in manifest.get("sources", [])}
+ for repository, ref in direct.items():
+ if repository not in sources:
+ errors.append(f"direct dependency {repository} missing from third_party/manifest.json")
+ elif sources[repository] != ref:
+ errors.append(f"direct dependency {repository} ref mismatch between lock and manifest")
+
+ if errors:
+ print("provenance validation: FAIL")
+ for error in errors:
+ print(f" - {error}")
+ return 1
+
+ print("provenance validation: PASS")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/field/HydraPulse/references/r0/tools/validate_repo.py b/field/HydraPulse/references/r0/tools/validate_repo.py
new file mode 100644
index 000000000..11e30c814
--- /dev/null
+++ b/field/HydraPulse/references/r0/tools/validate_repo.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+from pathlib import Path
+import sys
+
+ROOT = Path(__file__).resolve().parents[1]
+
+REQUIRED = [
+ "README.md",
+ "CMakeLists.txt",
+ "deps.lock.json",
+ "third_party/manifest.json",
+ "src/core/Pattern16.h",
+ "src/core/StepSequencer.h",
+ "src/core/SampleAccurateStepClock.h",
+ "src/core/ControlPickup.h",
+ "tests/native/CMakeLists.txt",
+ "docs/ARCHITECTURE.md",
+ "docs/POC_SEQUENCE.md",
+ "docs/TEST_STRATEGY.md",
+ "docs/SOURCE_REVIEW.md",
+ "docs/STATUS.md",
+ "docs/LOCAL_VALIDATION.md",
+ "firmware/field_truth/README.md",
+ "evidence/README.md",
+]
+
+FORBIDDEN_PUBLIC_EXTENSIONS = {".pdf"}
+
+
+def main() -> int:
+ missing = [path for path in REQUIRED if not (ROOT / path).is_file()]
+ if missing:
+ print("missing required files:")
+ for path in missing:
+ print(f" - {path}")
+ return 1
+
+ forbidden = []
+ for path in ROOT.rglob("*"):
+ if path.is_file() and path.suffix.lower() in FORBIDDEN_PUBLIC_EXTENSIONS:
+ forbidden.append(path.relative_to(ROOT).as_posix())
+
+ if forbidden:
+ print("unexpected binary/manual-like files in bootstrap:")
+ for path in forbidden:
+ print(f" - {path}")
+ return 1
+
+ print("repository structure: PASS")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/field/HydraPulse/references/r0_manifest.json b/field/HydraPulse/references/r0_manifest.json
new file mode 100644
index 000000000..2496e9a4c
--- /dev/null
+++ b/field/HydraPulse/references/r0_manifest.json
@@ -0,0 +1,35 @@
+{
+ "archive": "HydraPulse-Field_R0_bootstrap_2026-08-16.zip",
+ "archive_sha256": "721f806e9bdc7a76c2c27bb532a2c9db05f5e487e33d382c0a96112e391e5468",
+ "files": {
+ ".clang-format": "d87cc519b7567560cbfd6def04b86ccb168ee64154ad0472982d152441ef2c62",
+ ".github/workflows/ci.yml": "50c290ad468f10d8c6a1c4ce1e6cb13d77c63ddfdf01d8dbf9f0d35f40c4d95b",
+ ".gitignore": "dfd352f6bed34db1459fc7f9b9f29ba5e054ed844ed4f4c42107395decaade6b",
+ "CMakeLists.txt": "96c67cc212784b6be76e4b8a4a5f2b0fe705646df47334437cd06f33521763ce",
+ "README.md": "b5f45227bf5e589beaf6515ae4024b26f39b41b7b393b151cd102d6e89534fe8",
+ "deps.lock.json": "c8c410d73ecfc9d36defcfd94119eaedd008a00c032e4b7d4b52eb47c7a18ff7",
+ "docs/ARCHITECTURE.md": "066da0f7ad512c5a5810cf3b11bc7513f88fad59fcf3015ac180a66ec58882a7",
+ "docs/LOCAL_VALIDATION.md": "ae6d8c77902681245c4e4e8347f27e75ab7fef26c6e43aae5663c2c437b572da",
+ "docs/POC_SEQUENCE.md": "d822e4f7faff5a4771cf0992612d74cb90cda73b72d2c0c2a086cffb0d8d5769",
+ "docs/SOURCE_REVIEW.md": "a6d73983040559104770d2f002aec951b31e67158e1d8fcf45f3d61ca44bbd17",
+ "docs/STATUS.md": "6e3a9c3dd9b22299a7dc3f9a8fff24ae7a20b028fc54a3f05494780118c6d7a3",
+ "docs/TEST_STRATEGY.md": "fe7c63c7e66fd3c088ce8f59036e35c184c408f5729897e66309df642eb562d2",
+ "evidence/README.md": "4593e28a37d679fecc26e11b287084d36f021adc327e9f0dd17b2b6f240b6535",
+ "firmware/field_truth/README.md": "c2f6e40ad2d254720d852a5387640a10ee345d5791c5f93d534de52ee331d5dd",
+ "hardware/README.md": "3898d4a726df36973ac4928d0634778380839d1072e8fe8b7881db0bafbb3132",
+ "src/core/ControlPickup.h": "807950b052488b66d531bc64bb58acac3b321d87f139de5befa8b866f3819903",
+ "src/core/Pattern16.h": "1e18985219ba1ea6c5b4d66eaf223098483b7457af945217b3a07145c8ea70a6",
+ "src/core/SampleAccurateStepClock.h": "3728d537a685f547f7f5537a8ceedb4f7f6311a311f2f171c73d4262a329204f",
+ "src/core/StepSequencer.h": "ee6dd997505023870b534f3320df8b50a2fbc3770c28d4b1758a6942bd81b55f",
+ "tests/native/CMakeLists.txt": "ca1f7ae459de3506ca25d26ef9b6947383537a1ade9e6f1635f06aae1b9ed1b0",
+ "tests/native/test_control_pickup.cpp": "6355a247d778aba2c3cac7fbe177540a0dee03e097bd2aa1853ea7f610ae359b",
+ "tests/native/test_pattern16.cpp": "6d98ffccbd3a2bbc851a4796a935c58bbc9ab4298aaddc545a724395e39189f9",
+ "tests/native/test_sample_clock.cpp": "fd7e9b823fb0dbbade0c54ef702896b1da5b5ba963f45757a2f9c395d76781ec",
+ "tests/native/test_step_sequencer.cpp": "0574a40d3c9773cada1f7453fcd834b46e2fec4635eeb27d5ead224283b90bfd",
+ "tests/native/test_support.h": "72045eda7d406fb675629284c4886dbb5b9e6487bcbc9d04fbaa52b8dbcb49e0",
+ "tests/python/test_validators.py": "c1aace70635229dd62916b6be9a5b4d643121e4207df1e007965fe8656ba5401",
+ "third_party/manifest.json": "00ed13b47d1ebf5a53421c9db220cdf66b4c29f221357ac65033558f4ff68bbc",
+ "tools/validate_provenance.py": "b5cea9dac8e18fa7fb4abbaa22634a6900f152e3df9e254945c548606d76b099",
+ "tools/validate_repo.py": "b30de859914e5ffb9175d1eeeab5d987054fa5885e7c29b23aea765d1baa15ce"
+ }
+}
diff --git a/field/HydraPulse/src/app/Controller.cpp b/field/HydraPulse/src/app/Controller.cpp
new file mode 100644
index 000000000..88b920f33
--- /dev/null
+++ b/field/HydraPulse/src/app/Controller.cpp
@@ -0,0 +1,250 @@
+#include "src/app/Controller.h"
+#include
+#include
+
+namespace hydrapulse
+{
+void Controller::CaptureTargets(const Engine &engine, const InputFrame &frame, bool globals) noexcept
+{
+ const auto p = engine.Voice(selected_);
+ const float values[8] = {
+ p.tune, p.decay, p.character, p.level, float((engine.Tempo() - 40.0) / 200.0),
+ engine.Swing(), engine.Drive(), engine.Master()};
+ for (std::size_t i = 0; i < (globals ? 8u : 4u); ++i)
+ pickup_[i].Reset(values[i], frame.knobs[i]);
+}
+void Controller::Init(Engine &engine, const InputFrame &frame) noexcept
+{
+ selected_ = 0;
+ edit_ = Bank::A;
+ previous_ = frame;
+ revision_ = engine.PresetRevision();
+ pending_preset_ = engine.PresetId();
+ consumed_ = frame.keys;
+ long_done_ = 0;
+ held_ms_.fill(0);
+ panic_ms_ = clear_ms_ = preset_age_ = 0;
+ initialized_ = true;
+ play_armed_ = panic_latched_ = clear_done_ = preset_armed_ = false;
+ CaptureTargets(engine, frame, true);
+}
+void Controller::ShiftPress(Engine &engine, std::size_t key, const InputFrame &frame) noexcept
+{
+ if (key < 4)
+ {
+ selected_ = static_cast(key);
+ CaptureTargets(engine, frame, false);
+ preset_armed_ = false;
+ if (!engine.Running())
+ engine.Trigger(key);
+ }
+ else if (key == 4 || key == 5)
+ {
+ edit_ = (key == 4) ? Bank::A : Bank::B;
+ engine.QueueBank(edit_);
+ preset_armed_ = false;
+ }
+ else if (key == 6)
+ {
+ edit_ = Bank::Fill;
+ preset_armed_ = false;
+ }
+ else if (key >= 8 && key <= 11)
+ engine.ToggleMute(key - 8);
+ else if ((key == 13 || key == 14) && !engine.Running() && !engine.Loading())
+ {
+ if (!preset_armed_)
+ pending_preset_ = engine.PresetId();
+ pending_preset_ = static_cast((pending_preset_ + (key == 13 ? 7u : 1u)) % 8u);
+ preset_armed_ = true;
+ preset_age_ = 0;
+ }
+ else if (key == 15 && preset_armed_ && !engine.Running())
+ {
+ (void)engine.RequestPreset(pending_preset_);
+ preset_armed_ = false;
+ }
+}
+void Controller::Tick(Engine &engine, const InputFrame &frame) noexcept
+{
+ if (!initialized_)
+ {
+ Init(engine, frame);
+ return;
+ }
+ if (revision_ != engine.PresetRevision())
+ {
+ revision_ = engine.PresetRevision();
+ edit_ = Bank::A;
+ pending_preset_ = engine.PresetId();
+ consumed_ |= frame.keys;
+ long_done_ = 0;
+ CaptureTargets(engine, frame, true);
+ }
+ const auto pressed = std::uint16_t(frame.keys & ~previous_.keys);
+ const auto released = std::uint16_t(previous_.keys & ~frame.keys);
+ if (frame.shift)
+ {
+ consumed_ |= frame.keys;
+ play_armed_ = false;
+ }
+ if (frame.play && frame.shift)
+ {
+ if (panic_ms_ < 500)
+ ++panic_ms_;
+ if (panic_ms_ == 500 && !panic_latched_)
+ {
+ engine.Panic();
+ panic_latched_ = true;
+ preset_armed_ = false;
+ }
+ }
+ else
+ panic_ms_ = 0;
+ if (panic_latched_)
+ {
+ consumed_ |= frame.keys;
+ play_armed_ = false;
+ engine.SetFill(false);
+ previous_ = frame;
+ if (!frame.play && !frame.shift)
+ panic_latched_ = false;
+ return;
+ }
+ if (frame.play && !previous_.play && !frame.shift)
+ play_armed_ = true;
+ if (frame.shift || previous_.shift)
+ play_armed_ = false;
+ if (!frame.play && previous_.play && play_armed_)
+ {
+ if (engine.Running())
+ engine.Stop();
+ else
+ engine.Start();
+ play_armed_ = false;
+ preset_armed_ = false;
+ }
+ if (!(frame.play && frame.shift))
+ {
+ for (std::size_t key = 0; key < 16; ++key)
+ {
+ const auto bit = std::uint16_t(1u << key);
+ if (pressed & bit)
+ {
+ held_ms_[key] = 0;
+ long_done_ &= std::uint16_t(~bit);
+ if (frame.shift)
+ ShiftPress(engine, key, frame);
+ }
+ if ((frame.keys & bit) && !frame.shift && !(consumed_ & bit))
+ {
+ if (held_ms_[key] < 400)
+ ++held_ms_[key];
+ if (held_ms_[key] == 400 && !(long_done_ & bit))
+ {
+ engine.ToggleAccent(edit_, selected_, key);
+ long_done_ |= bit;
+ }
+ }
+ if (released & bit)
+ {
+ if (!(consumed_ & bit) && !(long_done_ & bit) && !frame.shift)
+ engine.ToggleStep(edit_, selected_, key);
+ consumed_ &= std::uint16_t(~bit);
+ long_done_ &= std::uint16_t(~bit);
+ }
+ }
+ }
+ engine.SetFill(frame.shift && ((frame.keys & (1u << 7u)) != 0));
+ const bool clear = frame.shift && ((frame.keys & (1u << 12u)) != 0) && !engine.Running();
+ if (clear)
+ {
+ if (clear_ms_ < 1000)
+ ++clear_ms_;
+ if (clear_ms_ == 1000 && !clear_done_)
+ {
+ engine.ClearTrack(edit_, selected_);
+ clear_done_ = true;
+ }
+ }
+ else
+ {
+ clear_ms_ = 0;
+ clear_done_ = false;
+ }
+ if (preset_armed_ && ++preset_age_ >= 5000)
+ preset_armed_ = false;
+
+ auto params = engine.Voice(selected_);
+ float *fields[4] = {¶ms.tune, ¶ms.decay, ¶ms.character, ¶ms.level};
+ bool changed = false;
+ for (std::size_t i = 0; i < 8; ++i)
+ {
+ if (!pickup_[i].Update(frame.knobs[i]))
+ continue;
+ const float value = pickup_[i].Value();
+ if (i < 4)
+ {
+ if (std::fabs(*fields[i] - value) > 0.002f)
+ {
+ *fields[i] = value;
+ changed = true;
+ }
+ }
+ else if (i == 4)
+ {
+ const double bpm = std::round((40.0 + 200.0 * double(value)) * 10.0) / 10.0;
+ if (std::fabs(engine.Tempo() - bpm) >= 0.099)
+ (void)engine.SetTempo(bpm);
+ }
+ else if (i == 5)
+ {
+ if (std::fabs(value - engine.Swing()) > 0.002f)
+ (void)engine.SetSwing(value);
+ }
+ else if (i == 6)
+ {
+ if (std::fabs(value - engine.Drive()) > 0.002f)
+ engine.SetDrive(value);
+ }
+ else if (i == 7)
+ {
+ if (std::fabs(value - engine.Master()) > 0.002f)
+ engine.SetMaster(value);
+ }
+ }
+ if (changed)
+ engine.SetVoice(selected_, params);
+ previous_ = frame;
+}
+UiSnapshot Controller::Snapshot(const Engine &engine) const noexcept
+{
+ UiSnapshot s{};
+ const auto p = engine.Voice(selected_);
+ s.params = {p.tune, p.decay, p.character, p.level};
+ s.notes = engine.Notes(edit_, selected_);
+ s.accents = engine.Accents(edit_, selected_);
+ for (std::size_t i = 0; i < 8; ++i)
+ if (!pickup_[i].Captured())
+ s.pickup_mask |= std::uint16_t(1u << i);
+ s.selected = selected_;
+ s.step = engine.CurrentStep();
+ s.edit_bank = static_cast(edit_);
+ s.play_bank = static_cast(engine.PlayingBank());
+ s.queued_bank = static_cast(engine.QueuedBank());
+ s.preset = engine.PresetId();
+ s.pending_preset = pending_preset_;
+ s.mutes = engine.Mutes();
+ s.running = engine.Running();
+ s.shift = previous_.shift;
+ s.fill = engine.Fill();
+ s.preset_armed = preset_armed_;
+ s.loading = engine.Loading();
+ s.bpm = float(engine.Tempo());
+ s.swing = engine.Swing();
+ s.drive = engine.Drive();
+ s.master = engine.Master();
+ s.faults = engine.Faults();
+ return s;
+}
+} // namespace hydrapulse
diff --git a/field/HydraPulse/src/app/Controller.h b/field/HydraPulse/src/app/Controller.h
new file mode 100644
index 000000000..ee514298d
--- /dev/null
+++ b/field/HydraPulse/src/app/Controller.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "src/app/Engine.h"
+#include "src/core/ControlPickup.h"
+#include
+#include
+
+namespace hydrapulse
+{
+// Tick at 1 kHz with debounced states. The adapter and engine share the audio owner.
+class Controller
+{
+ public:
+ void Init(Engine &engine, const InputFrame &frame) noexcept;
+ void Tick(Engine &engine, const InputFrame &frame) noexcept;
+ UiSnapshot Snapshot(const Engine &engine) const noexcept;
+ bool SuppressMidi() const noexcept
+ {
+ return panic_latched_ || (previous_.play && previous_.shift);
+ }
+
+ private:
+ void CaptureTargets(const Engine &engine, const InputFrame &frame, bool globals) noexcept;
+ void ShiftPress(Engine &engine, std::size_t key, const InputFrame &frame) noexcept;
+ std::array pickup_{};
+ std::array held_ms_{};
+ InputFrame previous_{};
+ std::uint32_t revision_{0};
+ std::uint16_t consumed_{0}, long_done_{0}, panic_ms_{0}, clear_ms_{0}, preset_age_{0};
+ std::uint8_t selected_{0}, pending_preset_{0};
+ Bank edit_{Bank::A};
+ bool initialized_{false}, play_armed_{false}, panic_latched_{false}, clear_done_{false},
+ preset_armed_{false};
+};
+} // namespace hydrapulse
diff --git a/field/HydraPulse/src/app/Engine.cpp b/field/HydraPulse/src/app/Engine.cpp
new file mode 100644
index 000000000..8638498ae
--- /dev/null
+++ b/field/HydraPulse/src/app/Engine.cpp
@@ -0,0 +1,244 @@
+#include "src/app/Engine.h"
+#include "src/app/Presets.generated.h"
+#include
+#include
+
+namespace hydrapulse
+{
+void Engine::Init(std::uint32_t sample_rate) noexcept
+{
+ sample_rate_ = (sample_rate >= 8000 && sample_rate <= 192000) ? sample_rate : 48000;
+ ramp_samples_ = std::max(1u, sample_rate_ / 200u);
+ table_.Init();
+ clock_.Init(sample_rate_);
+ master_.Reset(0.25f);
+ drive_.Reset(0);
+ load_gain_.Reset(1);
+ for (std::size_t i = 0; i < kTracks; ++i)
+ {
+ voices_[i].Init(static_cast(i), float(sample_rate_), table_);
+ mute_gain_[i].Reset(1);
+ }
+ for (auto &dc : dc_)
+ dc.Init(float(sample_rate_));
+ pending_preset_ = -1;
+ faults_ = 0;
+ step_events_ = 0;
+ ApplyPreset(0);
+}
+const char *Engine::PresetName() const noexcept
+{
+ return kTutorialPresets[preset_].name;
+}
+void Engine::ApplyPreset(std::uint8_t id) noexcept
+{
+ const auto &preset = kTutorialPresets[id];
+ preset_ = id;
+ ++preset_revision_;
+ running_ = false;
+ first_ = true;
+ fill_ = false;
+ active_bank_ = queued_bank_ = play_bank_ = Bank::A;
+ current_step_ = next_step_ = mute_mask_ = 0;
+ (void)clock_.SetTempo(preset.bpm);
+ (void)clock_.SetSwing(preset.swing);
+ clock_.Reset();
+ drive_.Reset(preset.drive);
+ for (std::size_t t = 0; t < kTracks; ++t)
+ {
+ voices_[t].Reset();
+ voices_[t].SetParameters(preset.voices[t], true);
+ mute_gain_[t].Reset(1);
+ }
+ for (std::size_t b = 0; b < kBanks; ++b)
+ for (std::size_t t = 0; t < kTracks; ++t)
+ for (std::size_t s = 0; s < kSteps; ++s)
+ {
+ const auto bit = std::uint16_t(1u << s);
+ banks_[b].notes.Set(t, s, (preset.notes[b][t] & bit) != 0);
+ banks_[b].accents.Set(t, s, (preset.accents[b][t] & bit) != 0);
+ }
+ for (auto &dc : dc_)
+ dc.Reset();
+}
+bool Engine::RequestPreset(std::uint8_t id) noexcept
+{
+ if (id >= kPresets || running_ || Loading())
+ return false;
+ pending_preset_ = int(id);
+ load_gain_.Set(0, ramp_samples_);
+ return true;
+}
+void Engine::Start() noexcept
+{
+ if (Loading() || running_)
+ return;
+ current_step_ = next_step_ = 0;
+ first_ = true;
+ running_ = true;
+ clock_.Reset();
+}
+void Engine::Stop() noexcept
+{
+ running_ = false;
+ fill_ = false;
+ for (auto &voice : voices_)
+ voice.Release();
+}
+void Engine::Panic() noexcept
+{
+ Stop();
+ // Existing finite releases reach exact zero. No phase/state reset while audible.
+ pending_preset_ = -1;
+ load_gain_.Set(1, ramp_samples_);
+}
+void Engine::Trigger(std::size_t track, float velocity) noexcept
+{
+ if (track >= kTracks || Loading() || (mute_mask_ & (1u << track)))
+ return;
+ voices_[track].Trigger(velocity);
+}
+void Engine::HandleMidi(const MidiCommand &command) noexcept
+{
+ switch (command.type)
+ {
+ case MidiCommandType::Trigger:
+ Trigger(command.track, float(command.velocity) / 127.0f);
+ break;
+ case MidiCommandType::Start:
+ Start();
+ break;
+ case MidiCommandType::Stop:
+ Stop();
+ break;
+ case MidiCommandType::Panic:
+ Panic();
+ break;
+ }
+}
+void Engine::SetMaster(float level) noexcept
+{
+ master_.Set(dsp::Unit(level, master_.Target()), ramp_samples_);
+}
+void Engine::SetDrive(float drive) noexcept
+{
+ drive_.Set(dsp::Unit(drive, drive_.Target()), ramp_samples_);
+}
+void Engine::SetVoice(std::size_t track, const dsp::VoiceParameters ¶meters) noexcept
+{
+ if (track < kTracks)
+ voices_[track].SetParameters(parameters);
+}
+dsp::VoiceParameters Engine::Voice(std::size_t track) const noexcept
+{
+ return track < kTracks ? voices_[track].Parameters() : dsp::VoiceParameters{};
+}
+bool Engine::ToggleStep(Bank bank, std::size_t track, std::size_t step) noexcept
+{
+ if (!ValidBank(bank) || track >= kTracks || step >= kSteps)
+ return false;
+ auto &b = banks_[static_cast(bank)];
+ b.notes.Toggle(track, step);
+ if (!b.notes.IsActive(track, step))
+ b.accents.Set(track, step, false);
+ return true;
+}
+bool Engine::ToggleAccent(Bank bank, std::size_t track, std::size_t step) noexcept
+{
+ if (!ValidBank(bank) || track >= kTracks || step >= kSteps)
+ return false;
+ auto &b = banks_[static_cast(bank)];
+ b.notes.Set(track, step, true);
+ b.accents.Toggle(track, step);
+ return true;
+}
+bool Engine::ClearTrack(Bank bank, std::size_t track) noexcept
+{
+ if (!ValidBank(bank) || track >= kTracks || running_)
+ return false;
+ auto &b = banks_[static_cast(bank)];
+ b.notes.ClearTrack(track);
+ b.accents.ClearTrack(track);
+ return true;
+}
+std::uint16_t Engine::Notes(Bank bank, std::size_t track) const noexcept
+{
+ return ValidBank(bank) ? banks_[static_cast(bank)].notes.TrackMask(track) : 0;
+}
+std::uint16_t Engine::Accents(Bank bank, std::size_t track) const noexcept
+{
+ return ValidBank(bank) ? banks_[static_cast(bank)].accents.TrackMask(track) : 0;
+}
+void Engine::QueueBank(Bank bank) noexcept
+{
+ if (bank != Bank::A && bank != Bank::B)
+ return;
+ queued_bank_ = bank;
+ if (!running_)
+ active_bank_ = play_bank_ = bank;
+}
+void Engine::ToggleMute(std::size_t track) noexcept
+{
+ if (track >= kTracks)
+ return;
+ mute_mask_ ^= static_cast(1u << track);
+ mute_gain_[track].Set((mute_mask_ & (1u << track)) ? 0.0f : 1.0f, ramp_samples_);
+}
+void Engine::EmitStep() noexcept
+{
+ if (next_step_ == 0)
+ active_bank_ = queued_bank_;
+ play_bank_ = fill_ ? Bank::Fill : active_bank_;
+ current_step_ = next_step_;
+ const auto &bank = banks_[static_cast(play_bank_)];
+ for (std::size_t t = 0; t < kTracks; ++t)
+ if (bank.notes.IsActive(t, current_step_))
+ Trigger(t, bank.accents.IsActive(t, current_step_) ? 1.0f : 0.72f);
+ next_step_ = static_cast((next_step_ + 1u) % 16u);
+ ++step_events_;
+}
+StereoFrame Engine::Process() noexcept
+{
+ float loading_gain = load_gain_.Process();
+ if (Loading() && load_gain_.Settled() && loading_gain == 0)
+ {
+ const auto id = static_cast(pending_preset_);
+ ApplyPreset(id);
+ pending_preset_ = -1;
+ load_gain_.Set(1, ramp_samples_);
+ }
+ if (running_)
+ {
+ if (first_)
+ {
+ first_ = false;
+ EmitStep();
+ }
+ else if (clock_.ProcessSample())
+ EmitStep();
+ }
+ // Fixed linear pan law: each voice's L+R contribution is constant.
+ constexpr float pans[4] = {-0.08f, 0.1f, 0.4f, -0.35f};
+ float left = 0, right = 0;
+ for (std::size_t t = 0; t < kTracks; ++t)
+ {
+ const float sample = voices_[t].Process() * mute_gain_[t].Process();
+ left += sample * (1.0f - pans[t]) * 0.5f;
+ right += sample * (1.0f + pans[t]) * 0.5f;
+ }
+ const float master = master_.Process() * 0.8f * loading_gain;
+ const float drive = drive_.Process();
+ const float gain = 0.32f * (1.0f + 5.0f * drive);
+ left = dc_[0].Process(left * gain);
+ right = dc_[1].Process(right * gain);
+ if (!std::isfinite(left) || !std::isfinite(right))
+ {
+ ++faults_;
+ Panic();
+ for (auto &dc : dc_)
+ dc.Reset();
+ return {};
+ }
+ return {0.92f * dsp::Saturate(left) * master, 0.92f * dsp::Saturate(right) * master};
+}
+} // namespace hydrapulse
diff --git a/field/HydraPulse/src/app/Engine.h b/field/HydraPulse/src/app/Engine.h
new file mode 100644
index 000000000..ca1ef6b5b
--- /dev/null
+++ b/field/HydraPulse/src/app/Engine.h
@@ -0,0 +1,135 @@
+#pragma once
+#include "src/app/Types.h"
+#include "src/core/GrooveClock.h"
+#include "src/dsp/Primitives.h"
+#include
+#include
+
+namespace hydrapulse
+{
+// Single-owner object: only the audio callback (or the serial host renderer) may mutate it.
+class Engine
+{
+ public:
+ void Init(std::uint32_t sample_rate = 48000) noexcept;
+ StereoFrame Process() noexcept;
+ void Start() noexcept;
+ void Stop() noexcept;
+ void Panic() noexcept;
+ bool Running() const noexcept
+ {
+ return running_;
+ }
+ bool Loading() const noexcept
+ {
+ return pending_preset_ >= 0;
+ }
+ bool RequestPreset(std::uint8_t id) noexcept;
+ std::uint8_t PresetId() const noexcept
+ {
+ return preset_;
+ }
+ std::uint32_t PresetRevision() const noexcept
+ {
+ return preset_revision_;
+ }
+ const char *PresetName() const noexcept;
+ void Trigger(std::size_t track, float velocity = 0.75f) noexcept;
+ void HandleMidi(const MidiCommand &command) noexcept;
+ bool SetTempo(double bpm) noexcept
+ {
+ return clock_.SetTempo(bpm);
+ }
+ bool SetSwing(float amount) noexcept
+ {
+ return clock_.SetSwing(amount);
+ }
+ void SetDrive(float drive) noexcept;
+ void SetMaster(float level) noexcept;
+ float Master() const noexcept
+ {
+ return master_.Target();
+ }
+ float Drive() const noexcept
+ {
+ return drive_.Target();
+ }
+ double Tempo() const noexcept
+ {
+ return clock_.Tempo();
+ }
+ float Swing() const noexcept
+ {
+ return clock_.Swing();
+ }
+ void SetVoice(std::size_t track, const dsp::VoiceParameters ¶meters) noexcept;
+ dsp::VoiceParameters Voice(std::size_t track) const noexcept;
+ bool ToggleStep(Bank bank, std::size_t track, std::size_t step) noexcept;
+ bool ToggleAccent(Bank bank, std::size_t track, std::size_t step) noexcept;
+ bool ClearTrack(Bank bank, std::size_t track) noexcept;
+ std::uint16_t Notes(Bank bank, std::size_t track) const noexcept;
+ std::uint16_t Accents(Bank bank, std::size_t track) const noexcept;
+ void QueueBank(Bank bank) noexcept;
+ void SetFill(bool held) noexcept
+ {
+ fill_ = held;
+ }
+ bool Fill() const noexcept
+ {
+ return fill_;
+ }
+ Bank ActiveBank() const noexcept
+ {
+ return active_bank_;
+ }
+ Bank QueuedBank() const noexcept
+ {
+ return queued_bank_;
+ }
+ Bank PlayingBank() const noexcept
+ {
+ return play_bank_;
+ }
+ std::uint8_t CurrentStep() const noexcept
+ {
+ return current_step_;
+ }
+ void ToggleMute(std::size_t track) noexcept;
+ std::uint8_t Mutes() const noexcept
+ {
+ return mute_mask_;
+ }
+ std::uint32_t Faults() const noexcept
+ {
+ std::uint32_t total = faults_;
+ for (const auto &voice : voices_)
+ total += voice.Faults();
+ return total;
+ }
+ std::uint64_t StepEvents() const noexcept
+ {
+ return step_events_;
+ }
+
+ private:
+ void ApplyPreset(std::uint8_t id) noexcept;
+ void EmitStep() noexcept;
+ static bool ValidBank(Bank bank) noexcept
+ {
+ return static_cast(bank) < 3u;
+ }
+ std::uint32_t sample_rate_{48000}, ramp_samples_{240}, preset_revision_{0}, faults_{0};
+ dsp::SineTable table_;
+ std::array voices_;
+ std::array banks_{};
+ std::array mute_gain_;
+ dsp::Ramp master_, drive_, load_gain_;
+ std::array dc_;
+ core::GrooveClock clock_;
+ std::uint64_t step_events_{0};
+ Bank active_bank_{Bank::A}, queued_bank_{Bank::A}, play_bank_{Bank::A};
+ std::uint8_t preset_{0}, current_step_{0}, next_step_{0}, mute_mask_{0};
+ int pending_preset_{-1};
+ bool running_{false}, first_{true}, fill_{false};
+};
+} // namespace hydrapulse
diff --git a/field/HydraPulse/src/app/Presets.generated.h b/field/HydraPulse/src/app/Presets.generated.h
new file mode 100644
index 000000000..81b924a8c
--- /dev/null
+++ b/field/HydraPulse/src/app/Presets.generated.h
@@ -0,0 +1,17 @@
+// Generated by tools/generate_presets.py; edit presets/tutorials.json instead.
+// clang-format off
+#pragma once
+#include "src/app/Types.h"
+namespace hydrapulse {
+inline const std::array kTutorialPresets{{
+ {"Blank Canvas",110.0000000f,0.0000000f,0.0000000f,{{{0.3600000f,0.3200000f,0.2800000f,0.8000000f},{0.4500000f,0.2800000f,0.5000000f,0.7200000f},{0.4500000f,0.2500000f,0.4000000f,0.5500000f},{0.4000000f,0.4000000f,0.3000000f,0.6500000f}}},{{{{0x0000u,0x0000u,0x0000u,0x0000u}},{{0x0000u,0x0000u,0x0000u,0x0000u}},{{0x0000u,0x0000u,0x0000u,0x0000u}}}},{{{{0x0000u,0x0000u,0x0000u,0x0000u}},{{0x0000u,0x0000u,0x0000u,0x0000u}},{{0x0000u,0x0000u,0x0000u,0x0000u}}}}},
+ {"First Pulse",110.0000000f,0.0000000f,0.0500000f,{{{0.3200000f,0.3500000f,0.3500000f,0.8500000f},{0.4000000f,0.2800000f,0.5000000f,0.7000000f},{0.4000000f,0.2000000f,0.4000000f,0.5500000f},{0.3500000f,0.4000000f,0.2500000f,0.6000000f}}},{{{{0x1111u,0x0000u,0x0000u,0x0000u}},{{0x1191u,0x0000u,0x0000u,0x0000u}},{{0x1111u,0xe800u,0xff00u,0x0000u}}}},{{{{0x1111u,0x0000u,0x0000u,0x0000u}},{{0x1111u,0x0000u,0x0000u,0x0000u}},{{0x1111u,0x0000u,0x1100u,0x0000u}}}}},
+ {"Backbeat",120.0000000f,0.0000000f,0.1000000f,{{{0.4000000f,0.2800000f,0.2500000f,0.8000000f},{0.3500000f,0.2800000f,0.5500000f,0.7400000f},{0.4800000f,0.2000000f,0.3500000f,0.5500000f},{0.4000000f,0.3000000f,0.3500000f,0.6500000f}}},{{{{0x0101u,0x1010u,0x4444u,0x0000u}},{{0x4141u,0x1010u,0x4444u,0x0000u}},{{0x0101u,0xf810u,0xff44u,0x0000u}}}},{{{{0x0101u,0x1010u,0x0000u,0x0000u}},{{0x0101u,0x1010u,0x0000u,0x0000u}},{{0x0101u,0x1010u,0x1100u,0x0000u}}}}},
+ {"Broken Grid",126.0000000f,0.0000000f,0.1600000f,{{{0.4500000f,0.2400000f,0.4800000f,0.8200000f},{0.4000000f,0.2400000f,0.6500000f,0.7500000f},{0.5800000f,0.1800000f,0.6000000f,0.5000000f},{0.4500000f,0.2500000f,0.3500000f,0.6200000f}}},{{{{0x0941u,0x1010u,0x6565u,0x0808u}},{{0x4901u,0x1010u,0x6565u,0x8888u}},{{0x0941u,0xf810u,0xff65u,0x0808u}}}},{{{{0x0101u,0x1010u,0x0101u,0x0000u}},{{0x0101u,0x1010u,0x0101u,0x0000u}},{{0x0101u,0x1010u,0x1101u,0x0000u}}}}},
+ {"Alloy Swing",124.0000000f,0.5500000f,0.1800000f,{{{0.3000000f,0.3500000f,0.3800000f,0.8200000f},{0.4600000f,0.3400000f,0.7000000f,0.7200000f},{0.5500000f,0.4200000f,0.5500000f,0.5600000f},{0.5500000f,0.3500000f,0.4500000f,0.6000000f}}},{{{{0x0181u,0x1010u,0x5555u,0x8208u}},{{0x41c1u,0x1010u,0x5555u,0x8288u}},{{0x0181u,0xf810u,0xff55u,0x8208u}}}},{{{{0x0101u,0x1010u,0x1111u,0x0000u}},{{0x0101u,0x1010u,0x1111u,0x0000u}},{{0x0101u,0x1010u,0x1111u,0x0000u}}}}},
+ {"Arc Study",105.0000000f,0.0000000f,0.1200000f,{{{0.4000000f,0.2800000f,0.3500000f,0.8000000f},{0.5000000f,0.3000000f,0.4500000f,0.7000000f},{0.4000000f,0.2000000f,0.4000000f,0.5000000f},{0.6200000f,0.5500000f,0.6000000f,0.6200000f}}},{{{{0x0101u,0x1000u,0x4444u,0x4a49u}},{{0x4141u,0x1000u,0x4444u,0xcac9u}},{{0x0101u,0xf800u,0xff44u,0xea49u}}}},{{{{0x0101u,0x1000u,0x0000u,0x0001u}},{{0x0101u,0x1000u,0x0000u,0x0001u}},{{0x0101u,0x1000u,0x1100u,0x0001u}}}}},
+ {"Night Shift",132.0000000f,0.2000000f,0.3800000f,{{{0.2500000f,0.5000000f,0.7000000f,0.8600000f},{0.3000000f,0.4000000f,0.8000000f,0.7400000f},{0.5000000f,0.5000000f,0.6000000f,0.5200000f},{0.3000000f,0.6000000f,0.6800000f,0.6000000f}}},{{{{0x1191u,0x1810u,0x5555u,0x8488u}},{{0x51d1u,0x1810u,0x5555u,0x8488u}},{{0x1191u,0xf810u,0xff55u,0xae88u}}}},{{{{0x1111u,0x1010u,0x1111u,0x0000u}},{{0x1111u,0x1010u,0x1111u,0x0000u}},{{0x1111u,0x1010u,0x1111u,0x0000u}}}}},
+ {"Fracture Lab",144.0000000f,0.3500000f,0.5200000f,{{{0.4500000f,0.4000000f,0.9000000f,0.8400000f},{0.6500000f,0.3800000f,0.9000000f,0.6800000f},{0.6800000f,0.5000000f,0.7000000f,0.4600000f},{0.7200000f,0.5500000f,0.8500000f,0.5600000f}}},{{{{0x4949u,0x9090u,0xffffu,0x2a22u}},{{0x0909u,0x9090u,0xffffu,0xaaa2u}},{{0x4949u,0xf890u,0xffffu,0xaa22u}}}},{{{{0x0101u,0x1010u,0x1111u,0x0000u}},{{0x0101u,0x1010u,0x1111u,0x0000u}},{{0x0101u,0x1010u,0x1111u,0x0000u}}}}},
+}};
+} // namespace hydrapulse
+// clang-format on
diff --git a/field/HydraPulse/src/app/Types.h b/field/HydraPulse/src/app/Types.h
new file mode 100644
index 000000000..7e4ef3d75
--- /dev/null
+++ b/field/HydraPulse/src/app/Types.h
@@ -0,0 +1,60 @@
+#pragma once
+#include "src/core/Pattern16.h"
+#include "src/dsp/Voices.h"
+#include
+#include
+namespace hydrapulse
+{
+constexpr std::size_t kTracks = 4, kSteps = 16, kBanks = 3, kPresets = 8;
+enum class Bank : std::uint8_t
+{
+ A = 0,
+ B = 1,
+ Fill = 2
+};
+struct BankPattern
+{
+ core::Pattern16 notes, accents;
+};
+struct Preset
+{
+ const char *name;
+ float bpm, swing, drive;
+ std::array voices;
+ // masks[bank][track], least significant bit = step 1.
+ std::array, kBanks> notes, accents;
+};
+struct StereoFrame
+{
+ float left{0}, right{0};
+};
+struct InputFrame
+{
+ std::array knobs{};
+ std::uint16_t keys{0}; // stable logical held-key states
+ bool play{false}, shift{false};
+};
+enum class MidiCommandType : std::uint8_t
+{
+ Trigger,
+ Start,
+ Stop,
+ Panic
+};
+struct MidiCommand
+{
+ MidiCommandType type{};
+ std::uint8_t track{0}, velocity{0};
+};
+struct UiSnapshot
+{
+ std::array params{};
+ std::uint16_t notes{0}, accents{0}, pickup_mask{0};
+ std::uint8_t selected{0}, step{0}, edit_bank{0}, play_bank{0}, queued_bank{0}, preset{0};
+ std::uint8_t pending_preset{0}, mutes{0};
+ bool running{false}, shift{false}, fill{false}, preset_armed{false}, loading{false};
+ float bpm{120}, swing{0}, drive{0}, master{0};
+ std::uint32_t callback_count{0}, snapshot_drops{0}, midi_drops{0}, faults{0};
+ std::uint32_t load_max_per_mille{0}, overruns{0}, late_starts{0};
+};
+} // namespace hydrapulse
diff --git a/field/HydraPulse/src/core/ControlPickup.h b/field/HydraPulse/src/core/ControlPickup.h
new file mode 100644
index 000000000..8721bb0af
--- /dev/null
+++ b/field/HydraPulse/src/core/ControlPickup.h
@@ -0,0 +1,60 @@
+#pragma once
+#include
+#include
+
+namespace hydrapulse::core
+{
+class ControlPickup
+{
+ public:
+ ControlPickup() noexcept : ControlPickup(0.015f) {}
+ explicit ControlPickup(float tolerance) noexcept
+ : tolerance_(std::isfinite(tolerance) ? std::clamp(tolerance, 0.0f, 0.1f) : 0.015f)
+ {
+ }
+ void Reset(float target, float physical) noexcept
+ {
+ target_ = std::isfinite(target) ? std::clamp(target, 0.0f, 1.0f) : 0.0f;
+ value_ = target_;
+ has_previous_ = std::isfinite(physical);
+ previous_ = has_previous_ ? std::clamp(physical, 0.0f, 1.0f) : target_;
+ captured_ = has_previous_ && std::fabs(previous_ - target_) <= tolerance_;
+ if (captured_)
+ value_ = previous_;
+ }
+ bool Update(float physical) noexcept
+ {
+ if (!std::isfinite(physical))
+ return false;
+ const float current = std::clamp(physical, 0.0f, 1.0f);
+ if (!has_previous_)
+ {
+ previous_ = current;
+ has_previous_ = true;
+ }
+ const bool crossed =
+ (previous_ <= target_ && current >= target_) || (previous_ >= target_ && current <= target_);
+ captured_ = captured_ || crossed || std::fabs(current - target_) <= tolerance_;
+ previous_ = current;
+ if (captured_)
+ value_ = current;
+ return captured_;
+ }
+ bool Captured() const noexcept
+ {
+ return captured_;
+ }
+ float Value() const noexcept
+ {
+ return value_;
+ }
+ float Target() const noexcept
+ {
+ return target_;
+ }
+
+ private:
+ float tolerance_, target_{0}, value_{0}, previous_{0};
+ bool has_previous_{false}, captured_{false};
+};
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/src/core/GrooveClock.h b/field/HydraPulse/src/core/GrooveClock.h
new file mode 100644
index 000000000..50df116d2
--- /dev/null
+++ b/field/HydraPulse/src/core/GrooveClock.h
@@ -0,0 +1,70 @@
+#pragma once
+#include
+#include
+#include
+
+namespace hydrapulse::core
+{
+// Sixteenth clock. A swing pair remains exactly two nominal intervals in rational arithmetic.
+// ProcessSample is called once for each sample AFTER the initial step-zero sample.
+class GrooveClock
+{
+ public:
+ void Init(std::uint32_t rate) noexcept
+ {
+ rate_ = (rate >= 8'000 && rate <= 192'000) ? rate : 48'000;
+ base_ = std::uint64_t{rate_} * 60ull * 1'000'000ull;
+ Reset();
+ }
+ bool SetTempo(double bpm) noexcept
+ {
+ if (!std::isfinite(bpm) || bpm < 40.0 || bpm > 240.0)
+ return false;
+ tempo_ = static_cast(std::llround(bpm * 1'000'000.0));
+ increment_ = tempo_ * 4ull * 1000ull;
+ return true;
+ }
+ bool SetSwing(float amount) noexcept
+ {
+ if (!std::isfinite(amount) || amount < 0 || amount > 1)
+ return false;
+ pending_swing_ = static_cast(std::lround(amount * 400.0f));
+ return true;
+ }
+ void Reset() noexcept
+ {
+ phase_ = 0;
+ long_interval_ = true;
+ swing_ = pending_swing_;
+ threshold_ = base_ * (1000ull + swing_);
+ }
+ bool ProcessSample() noexcept
+ {
+ phase_ += increment_;
+ if (phase_ < threshold_)
+ return false;
+ phase_ -= threshold_;
+ long_interval_ = !long_interval_;
+ if (long_interval_)
+ swing_ = pending_swing_; // commit swing only at pair boundary
+ threshold_ = base_ * (long_interval_ ? 1000ull + swing_ : 1000ull - swing_);
+ return true;
+ }
+ double Tempo() const noexcept
+ {
+ return double(tempo_) / 1'000'000.0;
+ }
+ float Swing() const noexcept
+ {
+ return float(pending_swing_) / 400.0f;
+ }
+
+ private:
+ std::uint32_t rate_{48'000};
+ std::uint64_t base_{48'000ull * 60ull * 1'000'000ull};
+ std::uint64_t threshold_{48'000ull * 60ull * 1'000'000ull * 1000ull};
+ std::uint64_t tempo_{120'000'000ull}, increment_{480'000'000'000ull}, phase_{0};
+ std::uint16_t swing_{0}, pending_swing_{0};
+ bool long_interval_{true};
+};
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/src/core/OutputTestArm.h b/field/HydraPulse/src/core/OutputTestArm.h
new file mode 100644
index 000000000..43e509a53
--- /dev/null
+++ b/field/HydraPulse/src/core/OutputTestArm.h
@@ -0,0 +1,18 @@
+#pragma once
+namespace hydrapulse::core
+{
+// A control held during boot must first be released before outputs can be tested.
+class OutputTestArm
+{
+ public:
+ bool Update(bool hold, bool other_switch) noexcept
+ {
+ if (!hold)
+ released_ = true;
+ return released_ && hold && !other_switch;
+ }
+
+ private:
+ bool released_{false};
+};
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/src/core/Pattern16.h b/field/HydraPulse/src/core/Pattern16.h
new file mode 100644
index 000000000..8ac5688b5
--- /dev/null
+++ b/field/HydraPulse/src/core/Pattern16.h
@@ -0,0 +1,77 @@
+#pragma once
+
+#include
+#include
+#include
+
+namespace hydrapulse::core
+{
+
+template class Pattern16
+{
+ public:
+ static_assert(Tracks > 0, "Pattern16 requires at least one track");
+ static_assert(Tracks <= 32, "Pattern16 track masks are limited to 32 tracks");
+
+ static constexpr std::size_t kSteps = 16;
+
+ bool Set(std::size_t track, std::size_t step, bool active) noexcept
+ {
+ if (!Valid(track, step))
+ return false;
+
+ const auto bit = static_cast(std::uint16_t{1u} << step);
+ if (active)
+ masks_[track] = static_cast(masks_[track] | bit);
+ else
+ masks_[track] = static_cast(masks_[track] & ~bit);
+ return true;
+ }
+
+ bool Toggle(std::size_t track, std::size_t step) noexcept
+ {
+ if (!Valid(track, step))
+ return false;
+
+ const auto bit = static_cast(std::uint16_t{1u} << step);
+ masks_[track] = static_cast(masks_[track] ^ bit);
+ return true;
+ }
+
+ [[nodiscard]] bool IsActive(std::size_t track, std::size_t step) const noexcept
+ {
+ if (!Valid(track, step))
+ return false;
+
+ const auto bit = static_cast(std::uint16_t{1u} << step);
+ return (masks_[track] & bit) != 0u;
+ }
+
+ bool ClearTrack(std::size_t track) noexcept
+ {
+ if (track >= Tracks)
+ return false;
+ masks_[track] = 0u;
+ return true;
+ }
+
+ void Clear() noexcept
+ {
+ masks_.fill(0u);
+ }
+
+ [[nodiscard]] std::uint16_t TrackMask(std::size_t track) const noexcept
+ {
+ return track < Tracks ? masks_[track] : 0u;
+ }
+
+ private:
+ [[nodiscard]] static constexpr bool Valid(std::size_t track, std::size_t step) noexcept
+ {
+ return track < Tracks && step < kSteps;
+ }
+
+ std::array masks_{};
+};
+
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/src/core/SampleAccurateStepClock.h b/field/HydraPulse/src/core/SampleAccurateStepClock.h
new file mode 100644
index 000000000..d8a8ad89d
--- /dev/null
+++ b/field/HydraPulse/src/core/SampleAccurateStepClock.h
@@ -0,0 +1,77 @@
+#pragma once
+#include
+#include
+
+namespace hydrapulse::core
+{
+// One event per sample maximum. Supported domain ensures this bound.
+class SampleAccurateStepClock
+{
+ public:
+ static constexpr std::uint64_t kBpmScale = 1'000'000u;
+ explicit SampleAccurateStepClock(std::uint32_t sample_rate = 48'000u, double bpm = 120.0,
+ std::uint32_t steps_per_beat = 4u) noexcept
+ {
+ if (sample_rate >= 8'000u && sample_rate <= 192'000u)
+ sample_rate_ = sample_rate;
+ if (steps_per_beat >= 1u && steps_per_beat <= 64u)
+ steps_per_beat_ = steps_per_beat;
+ threshold_ = std::uint64_t{sample_rate_} * 60u * kBpmScale;
+ increment_ = tempo_ * steps_per_beat_;
+ (void)SetTempoBpm(bpm);
+ }
+ bool SetSampleRate(std::uint32_t rate) noexcept
+ {
+ if (rate < 8'000u || rate > 192'000u)
+ return false;
+ sample_rate_ = rate;
+ threshold_ = std::uint64_t{rate} * 60u * kBpmScale;
+ Reset();
+ return true;
+ }
+ // Tempo changes preserve beat phase. Only an explicit transport reset restarts it.
+ bool SetTempoBpm(double bpm) noexcept
+ {
+ if (!std::isfinite(bpm) || bpm < 1.0 || bpm > 1000.0)
+ return false;
+ const auto proposed = static_cast(std::llround(bpm * double(kBpmScale)));
+ tempo_ = proposed;
+ increment_ = tempo_ * steps_per_beat_;
+ return true;
+ }
+ void Reset() noexcept
+ {
+ phase_ = 0;
+ }
+ bool ProcessSample() noexcept
+ {
+ phase_ += increment_;
+ if (phase_ < threshold_)
+ return false;
+ phase_ -= threshold_;
+ return true;
+ }
+ std::uint64_t SamplesUntilNextStep() const noexcept
+ {
+ return (threshold_ - phase_ + increment_ - 1u) / increment_;
+ }
+ std::uint32_t SampleRate() const noexcept
+ {
+ return sample_rate_;
+ }
+ double TempoBpm() const noexcept
+ {
+ return double(tempo_) / double(kBpmScale);
+ }
+ std::uint32_t StepsPerBeat() const noexcept
+ {
+ return steps_per_beat_;
+ }
+
+ private:
+ std::uint32_t sample_rate_{48'000u}, steps_per_beat_{4u};
+ std::uint64_t tempo_{120u * kBpmScale};
+ std::uint64_t threshold_{48'000ull * 60u * kBpmScale};
+ std::uint64_t increment_{120u * kBpmScale * 4u}, phase_{0};
+};
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/src/core/SpscQueue.h b/field/HydraPulse/src/core/SpscQueue.h
new file mode 100644
index 000000000..d3a56e664
--- /dev/null
+++ b/field/HydraPulse/src/core/SpscQueue.h
@@ -0,0 +1,43 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+
+namespace hydrapulse::core
+{
+// Exactly ONE producer and ONE consumer. No overwrite, no spin, no blocking.
+// Usable capacity is N-1. Do not Reset while either context is active.
+template class SpscQueue
+{
+ static_assert(N >= 2 && (N & (N - 1u)) == 0, "capacity must be a power of two");
+ static_assert(std::is_trivially_copyable::value, "queue payload must be trivially copyable");
+ static_assert(std::atomic::is_always_lock_free, "32-bit atomics must be lock-free");
+
+ public:
+ bool Push(const T &value) noexcept
+ {
+ const auto w = write_.load(std::memory_order_relaxed);
+ const auto next = (w + 1u) & (N - 1u);
+ if (next == read_.load(std::memory_order_acquire))
+ return false;
+ slots_[w] = value;
+ write_.store(next, std::memory_order_release);
+ return true;
+ }
+ bool Pop(T &value) noexcept
+ {
+ const auto r = read_.load(std::memory_order_relaxed);
+ if (r == write_.load(std::memory_order_acquire))
+ return false;
+ value = slots_[r];
+ read_.store((r + 1u) & (N - 1u), std::memory_order_release);
+ return true;
+ }
+
+ private:
+ std::array slots_{};
+ std::atomic write_{0}, read_{0};
+};
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/src/core/StepSequencer.h b/field/HydraPulse/src/core/StepSequencer.h
new file mode 100644
index 000000000..bf0fb8365
--- /dev/null
+++ b/field/HydraPulse/src/core/StepSequencer.h
@@ -0,0 +1,91 @@
+#pragma once
+
+#include "src/core/Pattern16.h"
+
+#include
+#include
+#include
+
+namespace hydrapulse::core
+{
+
+template class StepSequencer
+{
+ public:
+ using Pattern = Pattern16;
+
+ struct Tick
+ {
+ bool emitted{false};
+ std::uint8_t step{0};
+ std::uint32_t track_mask{0};
+ };
+
+ explicit StepSequencer(const Pattern &pattern) noexcept : pattern_(pattern) {}
+
+ void Start() noexcept
+ {
+ running_ = true;
+ current_step_ = 0u;
+ }
+
+ void Stop() noexcept
+ {
+ running_ = false;
+ }
+
+ [[nodiscard]] bool Running() const noexcept
+ {
+ return running_;
+ }
+
+ [[nodiscard]] std::uint8_t CurrentStep() const noexcept
+ {
+ return current_step_;
+ }
+
+ [[nodiscard]] Tick EmitAndAdvance() noexcept
+ {
+ if (!running_)
+ return {};
+
+ Tick result;
+ result.emitted = true;
+ result.step = current_step_;
+ result.track_mask = ActiveTrackMask(current_step_);
+
+ current_step_ = static_cast((current_step_ + 1u) % Pattern::kSteps);
+ return result;
+ }
+
+ template static void ForEachTriggeredTrack(const Tick &tick, Fn &&fn)
+ {
+ if (!tick.emitted)
+ return;
+
+ for (std::size_t track = 0; track < Tracks; ++track)
+ {
+ const auto bit = std::uint32_t{1u} << track;
+ if ((tick.track_mask & bit) != 0u)
+ std::forward(fn)(track);
+ }
+ }
+
+ private:
+ [[nodiscard]] std::uint32_t ActiveTrackMask(std::uint8_t step) const noexcept
+ {
+ std::uint32_t mask = 0u;
+ for (std::size_t track = 0; track < Tracks; ++track)
+ {
+ if (pattern_.IsActive(track, step))
+ mask |= std::uint32_t{1u} << track;
+ }
+ return mask;
+ }
+
+ const Pattern &pattern_;
+ bool running_{false};
+ std::uint8_t current_step_{0};
+};
+
+} // namespace hydrapulse::core
diff --git a/field/HydraPulse/src/dsp/Primitives.h b/field/HydraPulse/src/dsp/Primitives.h
new file mode 100644
index 000000000..e34356596
--- /dev/null
+++ b/field/HydraPulse/src/dsp/Primitives.h
@@ -0,0 +1,214 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+
+namespace hydrapulse::dsp
+{
+constexpr float kPi = 3.14159265358979323846f;
+inline float Unit(float x, float fallback = 0.0f) noexcept
+{
+ return std::isfinite(x) ? std::clamp(x, 0.0f, 1.0f) : fallback;
+}
+
+inline float ExpMap(float x, float lo, float hi) noexcept
+{
+ return lo * std::exp(std::log(hi / lo) * Unit(x));
+}
+
+class Ramp
+{
+ public:
+ void Reset(float value) noexcept
+ {
+ value_ = target_ = std::isfinite(value) ? value : 0;
+ left_ = 0;
+ delta_ = 0;
+ }
+ void Set(float target, std::uint32_t samples) noexcept
+ {
+ if (!std::isfinite(target) || target == target_)
+ return;
+ target_ = target;
+ left_ = std::max(samples, 1u);
+ delta_ = (target_ - value_) / float(left_);
+ }
+ float Process() noexcept
+ {
+ if (left_ && --left_ == 0)
+ value_ = target_;
+ else if (left_)
+ value_ += delta_;
+ return value_;
+ }
+ float Value() const noexcept
+ {
+ return value_;
+ }
+ float Target() const noexcept
+ {
+ return target_;
+ }
+ bool Settled() const noexcept
+ {
+ return left_ == 0;
+ }
+
+ private:
+ float value_{0}, target_{0}, delta_{0};
+ std::uint32_t left_{0};
+};
+
+class Noise
+{
+ public:
+ void Seed(std::uint32_t x) noexcept
+ {
+ state_ = x ? x : 1;
+ }
+ float Process() noexcept
+ {
+ state_ ^= state_ << 13;
+ state_ ^= state_ >> 17;
+ state_ ^= state_ << 5;
+ return float(state_ >> 8) * (1.0f / 8388608.0f) - 1.0f;
+ }
+
+ private:
+ std::uint32_t state_{1};
+};
+
+class SineTable
+{
+ public:
+ void Init() noexcept
+ {
+ for (std::size_t i = 0; i < kSize; ++i)
+ table_[i] = std::sin(2.0f * kPi * float(i) / float(kSize));
+ table_[kSize] = table_[0];
+ }
+ // Caller supplies finite cycles in a bounded range. Voices enforce this.
+ float Read(float cycles) const noexcept
+ {
+ if (!std::isfinite(cycles) || std::fabs(cycles) > 32767.0f)
+ return 0;
+ cycles -= float(static_cast(cycles));
+ if (cycles < 0)
+ cycles += 1;
+ const float position = cycles * float(kSize);
+ const auto index = static_cast(position);
+ if (index >= kSize)
+ return table_[0];
+ const float fraction = position - float(index);
+ return table_[index] + fraction * (table_[index + 1] - table_[index]);
+ }
+
+ private:
+ static constexpr std::size_t kSize = 1024;
+ std::array table_{};
+};
+
+class Envelope
+{
+ public:
+ void Init(float sr) noexcept
+ {
+ sr_ = sr;
+ level_ = 0;
+ remaining_ = 0;
+ releasing_ = false;
+ SetDecay(0.2f);
+ }
+ void SetDecay(float t60) noexcept
+ {
+ coefficient_ = std::exp(-6.90775527898f / (std::clamp(t60, 0.015f, 3.0f) * sr_));
+ }
+ void Trigger(float velocity, float attack_seconds = 0.001f) noexcept
+ {
+ target_ = Unit(velocity);
+ if (target_ <= 0)
+ return;
+ remaining_ = std::max(1u, static_cast(sr_ * attack_seconds));
+ delta_ = (target_ - level_) / float(remaining_);
+ releasing_ = false;
+ }
+ void Release(float seconds = 0.005f) noexcept
+ {
+ if (releasing_ && target_ == 0)
+ return;
+ target_ = 0;
+ remaining_ = std::max(1u, static_cast(seconds * sr_));
+ delta_ = -level_ / float(remaining_);
+ releasing_ = true;
+ }
+ float Process() noexcept
+ {
+ if (remaining_)
+ {
+ if (--remaining_ == 0)
+ level_ = target_;
+ else
+ level_ += delta_;
+ }
+ else if (!releasing_)
+ {
+ level_ *= coefficient_;
+ if (level_ < 1.0e-6f)
+ level_ = 0;
+ }
+ return level_;
+ }
+ float Value() const noexcept
+ {
+ return level_;
+ }
+ void Reset() noexcept
+ {
+ level_ = 0;
+ remaining_ = 0;
+ releasing_ = false;
+ }
+
+ private:
+ float sr_{48000}, level_{0}, coefficient_{0.999f}, target_{0}, delta_{0};
+ std::uint32_t remaining_{0};
+ bool releasing_{false};
+};
+
+class DcBlock
+{
+ public:
+ void Init(float sr) noexcept
+ {
+ r_ = std::exp(-2.0f * kPi * 10.0f / sr);
+ Reset();
+ }
+ void Reset() noexcept
+ {
+ x_ = y_ = 0;
+ }
+ float Process(float x) noexcept
+ {
+ const float y = x - x_ + r_ * y_;
+ x_ = x;
+ y_ = y;
+ if (std::fabs(y_) < 1.0e-12f)
+ y_ = 0;
+ return y_;
+ }
+
+ private:
+ float r_{0.9987f}, x_{0}, y_{0};
+};
+
+inline float Saturate(float x) noexcept
+{
+ if (!std::isfinite(x))
+ return 0;
+ x = std::clamp(x, -3.0f, 3.0f);
+ const float xx = x * x;
+ return x * (27.0f + xx) / (27.0f + 9.0f * xx);
+}
+} // namespace hydrapulse::dsp
diff --git a/field/HydraPulse/src/dsp/Voices.cpp b/field/HydraPulse/src/dsp/Voices.cpp
new file mode 100644
index 000000000..8202fcb11
--- /dev/null
+++ b/field/HydraPulse/src/dsp/Voices.cpp
@@ -0,0 +1,164 @@
+#include "src/dsp/Voices.h"
+#include
+#include
+
+namespace hydrapulse::dsp
+{
+void PercussionVoice::Init(VoiceId id, float sample_rate, const SineTable &table) noexcept
+{
+ id_ = id;
+ sr_ = std::isfinite(sample_rate) ? std::clamp(sample_rate, 8000.0f, 192000.0f) : 48000.0f;
+ inverse_sr_ = 1.0f / sr_;
+ table_ = &table;
+ amplitude_.Init(sr_);
+ pitch_.Init(sr_);
+ faults_ = 0;
+ Reset();
+ SetParameters({}, true);
+}
+
+void PercussionVoice::Reset() noexcept
+{
+ phase_.fill(0);
+ noise_low_ = 0;
+ noise_.Seed(0x48504631u + static_cast(id_) * 0x19A3u);
+ amplitude_.Reset();
+ pitch_.Reset();
+}
+
+void PercussionVoice::SetParameters(const VoiceParameters &p, bool immediate) noexcept
+{
+ parameters_ = {Unit(p.tune, parameters_.tune), Unit(p.decay, parameters_.decay),
+ Unit(p.character, parameters_.character), Unit(p.level, parameters_.level)};
+ float low = 32, high = 110, min_decay = 0.06f, max_decay = 1.4f;
+ switch (id_)
+ {
+ case VoiceId::Hammer:
+ break;
+ case VoiceId::Crack:
+ low = 120;
+ high = 330;
+ min_decay = 0.04f;
+ max_decay = 0.8f;
+ break;
+ case VoiceId::Steel:
+ low = 700;
+ high = 2300;
+ min_decay = 0.02f;
+ max_decay = 0.3f;
+ break;
+ case VoiceId::Arc:
+ low = 55;
+ high = 900;
+ min_decay = 0.05f;
+ max_decay = 2.4f;
+ break;
+ }
+ const float frequency = ExpMap(parameters_.tune, low, high);
+ decay_seconds_ = ExpMap(parameters_.decay, min_decay, max_decay);
+ amplitude_.SetDecay(decay_seconds_);
+ pitch_.SetDecay(0.02f + 0.12f * parameters_.decay);
+ // Convex one-pole noise filter; coefficient cannot create a resonant feedback loop.
+ const float cutoff = 900.0f + 4500.0f * parameters_.character;
+ const float noise_alpha = 1.0f - std::exp(-2.0f * kPi * cutoff / sr_);
+ const auto ramp_samples = std::max(1u, static_cast(sr_ * 0.005f));
+ if (immediate)
+ {
+ frequency_.Reset(frequency);
+ noise_alpha_.Reset(noise_alpha);
+ character_.Reset(parameters_.character);
+ level_.Reset(parameters_.level);
+ }
+ else
+ {
+ frequency_.Set(frequency, ramp_samples);
+ noise_alpha_.Set(noise_alpha, ramp_samples);
+ character_.Set(parameters_.character, ramp_samples);
+ level_.Set(parameters_.level, ramp_samples);
+ }
+}
+
+void PercussionVoice::Trigger(float velocity) noexcept
+{
+ if (!std::isfinite(velocity) || velocity <= 0 || !table_)
+ return;
+ // Keep oscillator phases and the current envelope value on retrigger.
+ amplitude_.Trigger(velocity, id_ == VoiceId::Steel ? 0.0005f : 0.001f);
+ // Smooth the transient depth as well as amplitude: PM index must not jump on retrigger.
+ pitch_.Trigger(1.0f);
+}
+
+float PercussionVoice::Osc(std::size_t oscillator, float frequency, float phase_mod) noexcept
+{
+ const float value = table_->Read(phase_[oscillator] + phase_mod);
+ const float increment = std::clamp(frequency * inverse_sr_, 0.0f, 0.42f);
+ phase_[oscillator] += increment;
+ if (phase_[oscillator] >= 1)
+ phase_[oscillator] -= 1;
+ return value;
+}
+
+float PercussionVoice::Process() noexcept
+{
+ const float f = frequency_.Process();
+ const float c = character_.Process();
+ const float level = level_.Process();
+ const float envelope = amplitude_.Process();
+ const float pitch_env = pitch_.Process();
+ const float noise_alpha = noise_alpha_.Process();
+ if (!table_ || envelope == 0)
+ return 0;
+
+ float signal = 0;
+ switch (id_)
+ {
+ case VoiceId::Hammer:
+ {
+ const float pitched = std::min(f * (1.0f + (2.0f + 10.0f * c) * pitch_env), 1800.0f);
+ const float body = Osc(0, pitched);
+ const float overtone = Osc(1, pitched * 2.0f);
+ signal = (body + 0.18f * c * overtone) / (1.0f + 0.18f * c);
+ break;
+ }
+ case VoiceId::Crack:
+ {
+ const float n = noise_.Process();
+ noise_low_ += noise_alpha * (n - noise_low_);
+ const float highpass = (n - noise_low_) * 0.65f;
+ const float body = 0.65f * Osc(0, f * (1.0f + 0.35f * pitch_env)) + 0.35f * Osc(1, f * 1.47f);
+ signal = body * (0.55f - 0.25f * c) + highpass * (0.45f + 0.25f * c);
+ break;
+ }
+ case VoiceId::Steel:
+ {
+ constexpr float ratios[6] = {1.0f, 1.342f, 1.893f, 2.511f, 3.117f, 4.017f};
+ float partials[6]{};
+ for (std::size_t i = 0; i < 6; ++i)
+ partials[i] = Osc(i, f * ratios[i]);
+ const float metal =
+ (partials[0] * partials[1] + partials[2] * partials[3] + partials[4] * partials[5]) *
+ (1.0f / 3.0f);
+ const float n = noise_.Process();
+ noise_low_ += noise_alpha * (n - noise_low_);
+ signal = metal * (0.8f - 0.35f * c) + (n - noise_low_) * (0.15f + 0.2f * c);
+ break;
+ }
+ case VoiceId::Arc:
+ {
+ const float modulator = Osc(1, f * (2.0f + 4.5f * c));
+ // Finite, feed-forward phase modulation. This is not feedback FM.
+ const float index = (0.5f + 4.5f * c) * (0.15f + 0.85f * pitch_env);
+ signal = Osc(0, f, modulator * index / (2.0f * kPi));
+ break;
+ }
+ }
+ const float result = signal * envelope * level;
+ if (!std::isfinite(result))
+ {
+ ++faults_;
+ Reset();
+ return 0;
+ }
+ return std::clamp(result, -1.0f, 1.0f);
+}
+} // namespace hydrapulse::dsp
diff --git a/field/HydraPulse/src/dsp/Voices.h b/field/HydraPulse/src/dsp/Voices.h
new file mode 100644
index 000000000..d22d3c37a
--- /dev/null
+++ b/field/HydraPulse/src/dsp/Voices.h
@@ -0,0 +1,58 @@
+#pragma once
+#include "src/dsp/Primitives.h"
+#include
+#include
+
+namespace hydrapulse::dsp
+{
+enum class VoiceId : std::uint8_t
+{
+ Hammer = 0,
+ Crack = 1,
+ Steel = 2,
+ Arc = 3
+};
+struct VoiceParameters
+{
+ float tune{0.4f}, decay{0.3f}, character{0.35f}, level{0.75f};
+};
+// Fixed work and no ownership of memory outside a read-only initialized sine table.
+class PercussionVoice
+{
+ public:
+ void Init(VoiceId id, float sample_rate, const SineTable &table) noexcept;
+ void SetParameters(const VoiceParameters &p, bool immediate = false) noexcept;
+ void Trigger(float velocity) noexcept;
+ void Release() noexcept
+ {
+ amplitude_.Release();
+ }
+ void Reset() noexcept;
+ float Process() noexcept;
+ VoiceParameters Parameters() const noexcept
+ {
+ return parameters_;
+ }
+ float DecaySeconds() const noexcept
+ {
+ return decay_seconds_;
+ }
+ std::uint32_t Faults() const noexcept
+ {
+ return faults_;
+ }
+
+ private:
+ float Osc(std::size_t oscillator, float frequency, float phase_mod = 0) noexcept;
+ VoiceId id_{VoiceId::Hammer};
+ float sr_{48000}, inverse_sr_{1.0f / 48000};
+ float noise_low_{0}, decay_seconds_{0.2f};
+ std::uint32_t faults_{0};
+ std::array phase_{};
+ const SineTable *table_{nullptr};
+ Noise noise_;
+ Ramp frequency_, character_, level_, noise_alpha_;
+ Envelope amplitude_, pitch_;
+ VoiceParameters parameters_{};
+};
+} // namespace hydrapulse::dsp
diff --git a/field/HydraPulse/tests/adapter_stub/daisy_field.h b/field/HydraPulse/tests/adapter_stub/daisy_field.h
new file mode 100644
index 000000000..025d5c9ec
--- /dev/null
+++ b/field/HydraPulse/tests/adapter_stub/daisy_field.h
@@ -0,0 +1,203 @@
+#pragma once
+// This stub checks only our source syntax and selected API shapes. Never flash this build.
+// It does not emulate libDaisy, DMA, analog hardware, or ISR execution.
+#include
+#include
+struct dsy_gpio
+{
+};
+inline void dsy_gpio_write(dsy_gpio *, std::uint8_t) {}
+struct FontDef
+{
+};
+inline FontDef Font_6x8;
+namespace daisy
+{
+struct SaiHandle
+{
+ struct Config
+ {
+ enum class SampleRate
+ {
+ SAI_48KHZ
+ };
+ };
+};
+struct AudioHandle
+{
+ using InputBuffer = const float *const *;
+ using OutputBuffer = float **;
+};
+struct System
+{
+ static std::uint32_t GetNow()
+ {
+ return 0;
+ }
+ static std::uint32_t GetTick()
+ {
+ return 0;
+ }
+ static std::uint32_t GetTickFreq()
+ {
+ return 200000000;
+ }
+ static std::uint32_t GetSysClkFreq()
+ {
+ return 400000000;
+ }
+ static unsigned GetProgramMemoryRegion()
+ {
+ return 0;
+ }
+ static unsigned GetBootloaderVersion()
+ {
+ return 0;
+ }
+ static void Delay(std::uint32_t) {}
+};
+enum MidiMessageType
+{
+ NoteOn,
+ NoteOff,
+ SystemRealTime,
+ ChannelMode,
+ ControlChange
+};
+enum SystemRealTimeType
+{
+ Start,
+ Stop,
+ Reset,
+ TimingClock
+};
+struct MidiEvent
+{
+ MidiMessageType type{};
+ SystemRealTimeType srt_type{};
+ int channel{0};
+ std::uint8_t data[2]{};
+};
+struct MidiHandler
+{
+ void StartReceive() {}
+ void Listen() {}
+ bool HasEvents() const
+ {
+ return false;
+ }
+ MidiEvent PopEvent()
+ {
+ return {};
+ }
+};
+struct Switch
+{
+ bool Pressed() const
+ {
+ return false;
+ }
+};
+struct GPIO
+{
+ void Write(bool) {}
+};
+struct GateIn
+{
+ bool State()
+ {
+ return false;
+ }
+};
+struct Display
+{
+ void SetCursor(std::uint8_t, std::uint8_t) {}
+ void WriteString(const char *, FontDef, bool) {}
+ void Fill(bool) {}
+ void Update() {}
+};
+struct LedDriver
+{
+ void SetLed(std::size_t, std::uint8_t) {}
+ void SetLed(std::size_t, float) {}
+ void SwapBuffersAndTransmit() {}
+};
+struct DaisySeed
+{
+ void SetTestPoint(bool) {}
+ void StartLog(bool) {}
+ unsigned CheckBoardVersion()
+ {
+ return 0;
+ }
+ template void PrintLine(const char *, T...) {}
+};
+struct DaisyField
+{
+ enum
+ {
+ SW_1,
+ SW_2,
+ SW_LAST
+ };
+ enum
+ {
+ LED_KEY_B1,
+ LED_KEY_B2,
+ LED_KEY_B3,
+ LED_KEY_B4,
+ LED_KEY_B5,
+ LED_KEY_B6,
+ LED_KEY_B7,
+ LED_KEY_B8,
+ LED_KEY_A8,
+ LED_KEY_A7,
+ LED_KEY_A6,
+ LED_KEY_A5,
+ LED_KEY_A4,
+ LED_KEY_A3,
+ LED_KEY_A2,
+ LED_KEY_A1,
+ LED_KNOB_1,
+ LED_KNOB_2,
+ LED_KNOB_3,
+ LED_KNOB_4,
+ LED_KNOB_5,
+ LED_KNOB_6,
+ LED_KNOB_7,
+ LED_KNOB_8,
+ LED_SW_1,
+ LED_SW_2,
+ LED_LAST
+ };
+ DaisySeed seed;
+ Switch sw[2];
+ dsy_gpio gate_out;
+ GateIn gate_in;
+ Display display;
+ LedDriver led_driver;
+ MidiHandler midi;
+ void Init(bool) {}
+ void SetCvOut1(std::uint16_t) {}
+ void SetCvOut2(std::uint16_t) {}
+ void SetAudioSampleRate(SaiHandle::Config::SampleRate) {}
+ void SetAudioBlockSize(std::size_t) {}
+ void StartAdc() {}
+ void ProcessAnalogControls() {}
+ void ProcessDigitalControls() {}
+ float GetKnobValue(std::size_t)
+ {
+ return 0;
+ }
+ float GetCvValue(std::size_t)
+ {
+ return 0;
+ }
+ bool KeyboardState(std::size_t) const
+ {
+ return false;
+ }
+ void VegasMode() {}
+ void StartAudio(void (*)(AudioHandle::InputBuffer, AudioHandle::OutputBuffer, std::size_t)) {}
+};
+} // namespace daisy
diff --git a/field/HydraPulse/tests/adapter_stub/util/CpuLoadMeter.h b/field/HydraPulse/tests/adapter_stub/util/CpuLoadMeter.h
new file mode 100644
index 000000000..74de52de7
--- /dev/null
+++ b/field/HydraPulse/tests/adapter_stub/util/CpuLoadMeter.h
@@ -0,0 +1,15 @@
+#pragma once
+namespace daisy
+{
+class CpuLoadMeter
+{
+ public:
+ void Init(float, int) {}
+ void OnBlockStart() {}
+ void OnBlockEnd() {}
+ float GetMaxCpuLoad() const
+ {
+ return 0;
+ }
+};
+} // namespace daisy
diff --git a/field/HydraPulse/tests/native/CMakeLists.txt b/field/HydraPulse/tests/native/CMakeLists.txt
new file mode 100644
index 000000000..680575352
--- /dev/null
+++ b/field/HydraPulse/tests/native/CMakeLists.txt
@@ -0,0 +1,26 @@
+function(add_hpf_test test_name source_file)
+ add_executable(${test_name} ${source_file})
+ target_link_libraries(${test_name} PRIVATE hydrapulse_engine Threads::Threads)
+ add_test(NAME ${test_name} COMMAND ${test_name})
+ set_tests_properties(${test_name} PROPERTIES TIMEOUT 90)
+endfunction()
+add_hpf_test(test_pattern16 test_pattern16.cpp)
+add_hpf_test(test_step_sequencer test_step_sequencer.cpp)
+add_hpf_test(test_sample_clock test_sample_clock.cpp)
+add_hpf_test(test_control_pickup test_control_pickup.cpp)
+add_hpf_test(test_regressions test_regressions.cpp)
+add_hpf_test(test_groove_clock test_groove_clock.cpp)
+add_hpf_test(test_spsc test_spsc.cpp)
+add_hpf_test(test_engine test_engine.cpp)
+add_hpf_test(test_controller test_controller.cpp)
+add_hpf_test(test_dsp test_dsp.cpp)
+add_hpf_test(test_no_allocation test_no_allocation.cpp)
+add_hpf_test(test_near_rejects_nan test_near_rejects_nan.cpp)
+set_tests_properties(test_near_rejects_nan PROPERTIES WILL_FAIL TRUE)
+
+add_hpf_test(test_transitions test_transitions.cpp)
+
+if(HPF_ADAPTER_CONTRACT_CHECK)
+ add_hpf_test(test_adapter_midi test_adapter_midi.cpp)
+ target_include_directories(test_adapter_midi BEFORE PRIVATE ../adapter_stub)
+endif()
diff --git a/field/HydraPulse/tests/native/test_adapter_midi.cpp b/field/HydraPulse/tests/native/test_adapter_midi.cpp
new file mode 100644
index 000000000..668d28ab0
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_adapter_midi.cpp
@@ -0,0 +1,67 @@
+// Exercise the actual application router/callback with an explicit fake BSP.
+// This is not a UART parser, HAL, ARM, or hardware test.
+#define main hydrapulse_firmware_entry_not_run
+#include "firmware/HydraPulse.cpp"
+#undef main
+#include "tests/native/test_support.h"
+#include
+
+int main()
+{
+ engine.Init();
+ controller.Init(engine, hydrapulse::field::ReadControls(hw));
+ meter.Init();
+ float l[48]{}, r[48]{};
+ float *output[2] = {l, r};
+ daisy::MidiEvent event{};
+ event.type = daisy::NoteOn;
+ event.channel = 0;
+ event.data[0] = 36;
+ event.data[1] = 100;
+ Midi(event);
+ AudioCallback(nullptr, output, 48);
+ for (float x : l)
+ HPF_CHECK_EQ(x, 0.0f);
+ event.channel = 9;
+ event.data[1] = 0;
+ Midi(event);
+ AudioCallback(nullptr, output, 48);
+ for (float x : l)
+ HPF_CHECK_EQ(x, 0.0f);
+ event.data[1] = 100;
+ Midi(event);
+ AudioCallback(nullptr, output, 48);
+ float peak = 0;
+ for (float x : l)
+ peak = std::max(peak, std::fabs(x));
+ HPF_CHECK(peak > 1e-6f);
+ for (int i = 0; i < 80; ++i)
+ Midi(event);
+ HPF_CHECK(midi_drops.load() > 0);
+ event.type = daisy::SystemRealTime;
+ event.srt_type = daisy::Stop;
+ Midi(event);
+ AudioCallback(nullptr, output, 48);
+ HPF_CHECK(!engine.Running());
+ hydrapulse::MidiCommand pending{};
+ HPF_CHECK(!midi_commands.Pop(pending));
+ for (int i = 0; i < 100; ++i)
+ AudioCallback(nullptr, output, 48);
+ for (float x : l)
+ HPF_CHECK(std::fabs(x) < 1e-6f);
+ event.srt_type = daisy::TimingClock;
+ Midi(event);
+ AudioCallback(nullptr, output, 48);
+ HPF_CHECK(!engine.Running());
+ event.srt_type = daisy::Start;
+ Midi(event);
+ AudioCallback(nullptr, output, 48);
+ HPF_CHECK(engine.Running());
+ event.type = daisy::ChannelMode;
+ event.channel = 9;
+ event.data[0] = 123;
+ Midi(event);
+ AudioCallback(nullptr, output, 48);
+ HPF_CHECK(!engine.Running());
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_control_pickup.cpp b/field/HydraPulse/tests/native/test_control_pickup.cpp
new file mode 100644
index 000000000..0863d1259
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_control_pickup.cpp
@@ -0,0 +1,36 @@
+#include "src/core/ControlPickup.h"
+#include "tests/native/test_support.h"
+
+int main()
+{
+ using hydrapulse::core::ControlPickup;
+
+ ControlPickup pickup(0.01f);
+ pickup.Reset(0.70f, 0.20f);
+ HPF_CHECK(!pickup.Captured());
+ HPF_CHECK_NEAR(pickup.Value(), 0.70f, 1.0e-6f);
+
+ HPF_CHECK(!pickup.Update(0.40f));
+ HPF_CHECK(!pickup.Update(0.60f));
+ HPF_CHECK(!pickup.Captured());
+
+ HPF_CHECK(pickup.Update(0.695f));
+ HPF_CHECK(pickup.Captured());
+ HPF_CHECK_NEAR(pickup.Value(), 0.695f, 1.0e-6f);
+
+ HPF_CHECK(pickup.Update(0.30f));
+ HPF_CHECK_NEAR(pickup.Value(), 0.30f, 1.0e-6f);
+
+ // Crossing the target between samples captures even if neither sample lands inside tolerance.
+ pickup.Reset(0.50f, 0.20f);
+ HPF_CHECK(!pickup.Update(0.48f));
+ HPF_CHECK(pickup.Update(0.52f));
+ HPF_CHECK(pickup.Captured());
+
+ // Input and target are clamped to the normalized control domain.
+ pickup.Reset(1.5f, 2.0f);
+ HPF_CHECK(pickup.Captured());
+ HPF_CHECK_NEAR(pickup.Value(), 1.0f, 1.0e-6f);
+
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_controller.cpp b/field/HydraPulse/tests/native/test_controller.cpp
new file mode 100644
index 000000000..cb75addec
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_controller.cpp
@@ -0,0 +1,82 @@
+#include "src/app/Controller.h"
+#include "tests/native/test_support.h"
+int main()
+{
+ using namespace hydrapulse;
+ Engine e;
+ e.Init();
+ Controller c;
+ InputFrame f{};
+ c.Init(e, f);
+ // Tap a step: action happens on release.
+ f.keys = 1;
+ c.Tick(e, f);
+ HPF_CHECK_EQ(e.Notes(Bank::A, 0), 0u);
+ f.keys = 0;
+ c.Tick(e, f);
+ HPF_CHECK_EQ(e.Notes(Bank::A, 0), 1u);
+ // Long press changes accent once and never toggles the step on release.
+ f.keys = 1;
+ for (unsigned i = 0; i < 450; ++i)
+ c.Tick(e, f);
+ f.keys = 0;
+ c.Tick(e, f);
+ HPF_CHECK_EQ(e.Accents(Bank::A, 0), 1u);
+ HPF_CHECK_EQ(e.Notes(Bank::A, 0), 1u);
+ // Modifier key held across Shift release must not leak a step.
+ f.shift = true;
+ c.Tick(e, f);
+ f.keys = 2;
+ c.Tick(e, f);
+ HPF_CHECK_EQ(unsigned(c.Snapshot(e).selected), 1u);
+ f.shift = false;
+ c.Tick(e, f);
+ f.keys = 0;
+ c.Tick(e, f);
+ HPF_CHECK_EQ(e.Notes(Bank::A, 1), 0u);
+ // Play is a tap-release; panic chord must not become a Play release.
+ f.play = true;
+ c.Tick(e, f);
+ f.play = false;
+ c.Tick(e, f);
+ HPF_CHECK(e.Running());
+ f.play = true;
+ f.shift = true;
+ for (int i = 0; i < 510; ++i)
+ c.Tick(e, f);
+ HPF_CHECK(!e.Running());
+ f.shift = false;
+ c.Tick(e, f);
+ f.play = false;
+ c.Tick(e, f);
+ HPF_CHECK(!e.Running());
+ // Preview does not load. Confirm while stopped performs a fade transaction.
+ f.shift = true;
+ f.keys = std::uint16_t(1u << 14);
+ c.Tick(e, f);
+ HPF_CHECK(c.Snapshot(e).preset_armed);
+ HPF_CHECK_EQ(unsigned(e.PresetId()), 0u);
+ f.keys = 0;
+ c.Tick(e, f);
+ f.keys = std::uint16_t(1u << 15);
+ c.Tick(e, f);
+ HPF_CHECK(e.Loading());
+ f.keys = 0;
+ f.shift = false;
+ for (int i = 0; i < 1000; ++i)
+ (void)e.Process();
+ c.Tick(e, f);
+ HPF_CHECK_EQ(unsigned(e.PresetId()), 1u);
+ // Selecting a voice with knobs at zero does not erase its stored parameters.
+ auto before = e.Voice(2);
+ f.shift = true;
+ f.keys = 4;
+ c.Tick(e, f);
+ f.shift = false;
+ c.Tick(e, f);
+ f.keys = 0;
+ c.Tick(e, f);
+ HPF_CHECK_NEAR(e.Voice(2).tune, before.tune, 1e-6);
+ HPF_CHECK_EQ(e.Notes(Bank::A, 2), 0u);
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_dsp.cpp b/field/HydraPulse/tests/native/test_dsp.cpp
new file mode 100644
index 000000000..72ce2ff8c
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_dsp.cpp
@@ -0,0 +1,85 @@
+#include "src/app/Engine.h"
+#include "src/dsp/Voices.h"
+#include "tests/native/test_support.h"
+#include
+#include
+#include
+int main()
+{
+ using namespace hydrapulse;
+ dsp::SineTable table;
+ table.Init();
+ for (unsigned id = 0; id < 4; ++id)
+ {
+ for (unsigned corner = 0; corner < 8; ++corner)
+ {
+ dsp::PercussionVoice v;
+ v.Init(static_cast(id), 48000, table);
+ v.SetParameters({float(corner & 1u), float((corner >> 1u) & 1u), float((corner >> 2u) & 1u), 1},
+ true);
+ v.Trigger(1);
+ float peak = 0, tail = 0;
+ for (int n = 0; n < 48000 * 6; ++n)
+ {
+ const float s = v.Process();
+ HPF_CHECK(std::isfinite(s));
+ HPF_CHECK(std::fabs(s) <= 1.00001f);
+ peak = std::max(peak, std::fabs(s));
+ if (n > 48000 * 5)
+ tail = std::max(tail, std::fabs(s));
+ }
+ HPF_CHECK(peak > 0.0001f);
+ HPF_CHECK(tail < 1e-5f);
+ }
+ dsp::PercussionVoice v;
+ v.Init(static_cast(id), 48000, table);
+ v.Trigger(1);
+ for (int n = 0; n < 500; ++n)
+ (void)v.Process();
+ v.Release();
+ for (int n = 0; n < 300; ++n)
+ (void)v.Process();
+ HPF_CHECK_EQ(v.Process(), 0.0f);
+ }
+ for (unsigned preset = 0; preset < 8; ++preset)
+ {
+ Engine a, b;
+ a.Init();
+ b.Init();
+ a.RequestPreset(static_cast(preset));
+ b.RequestPreset(static_cast(preset));
+ for (int n = 0; n < 600; ++n)
+ {
+ (void)a.Process();
+ (void)b.Process();
+ }
+ a.Start();
+ b.Start();
+ for (int n = 0; n < 48000 * 3; ++n)
+ {
+ if (n % 1103 == 0)
+ {
+ a.Trigger(unsigned(n) % 4u);
+ b.Trigger(unsigned(n) % 4u);
+ }
+ auto x = a.Process(), y = b.Process();
+ HPF_CHECK(std::isfinite(x.left) && std::isfinite(x.right));
+ HPF_CHECK(std::fabs(x.left) < 0.737f && std::fabs(x.right) < 0.737f);
+ HPF_CHECK_EQ(x.left, y.left);
+ HPF_CHECK_EQ(x.right, y.right);
+ }
+ HPF_CHECK_EQ(a.Faults(), 0u);
+ }
+ dsp::Ramp ramp;
+ ramp.Reset(1);
+ ramp.Set(0, 240);
+ float previous = 1;
+ for (int n = 0; n < 240; ++n)
+ {
+ const float x = ramp.Process();
+ HPF_CHECK(std::fabs(x - previous) <= 1.0f / 240.0f + 1e-6f);
+ previous = x;
+ }
+ HPF_CHECK_EQ(ramp.Value(), 0.0f);
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_engine.cpp b/field/HydraPulse/tests/native/test_engine.cpp
new file mode 100644
index 000000000..8a81101f2
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_engine.cpp
@@ -0,0 +1,61 @@
+#include "src/app/Engine.h"
+#include "tests/native/test_support.h"
+#include
+int main()
+{
+ using namespace hydrapulse;
+ Engine e;
+ e.Init();
+ HPF_CHECK(!e.Running());
+ for (int i = 0; i < 100; ++i)
+ {
+ auto a = e.Process();
+ HPF_CHECK(a.left == 0 && a.right == 0);
+ }
+ HPF_CHECK(e.RequestPreset(2));
+ HPF_CHECK(!e.RequestPreset(3));
+ for (int i = 0; i < 600; ++i)
+ (void)e.Process();
+ HPF_CHECK_EQ(unsigned(e.PresetId()), 2u);
+ e.SetTempo(120);
+ e.Start();
+ (void)e.Process();
+ HPF_CHECK_EQ(e.StepEvents(), 1u);
+ HPF_CHECK_EQ(unsigned(e.CurrentStep()), 0u);
+ e.QueueBank(Bank::B);
+ HPF_CHECK(e.ActiveBank() == Bank::A);
+ for (unsigned i = 1; i <= 16u * 6000u; ++i)
+ (void)e.Process();
+ HPF_CHECK(e.ActiveBank() == Bank::B);
+ HPF_CHECK_EQ(unsigned(e.CurrentStep()), 0u);
+ e.SetFill(true);
+ for (int i = 0; i < 6000; ++i)
+ (void)e.Process();
+ HPF_CHECK(e.PlayingBank() == Bank::Fill);
+ e.SetFill(false);
+ for (int i = 0; i < 6000; ++i)
+ (void)e.Process();
+ HPF_CHECK(e.PlayingBank() == Bank::B);
+ HPF_CHECK(!e.RequestPreset(0));
+ HPF_CHECK(!e.ClearTrack(Bank::A, 0));
+ e.Panic();
+ HPF_CHECK(!e.Running());
+ float peak = 0;
+ for (int i = 0; i < 48000; ++i)
+ {
+ auto a = e.Process();
+ if (i > 40000)
+ peak = std::max(peak, std::fabs(a.left) + std::fabs(a.right));
+ }
+ HPF_CHECK(peak < 1e-7f);
+ HPF_CHECK(!e.ToggleStep(static_cast(9), 0, 0));
+ HPF_CHECK(!e.ToggleStep(Bank::A, 4, 0));
+ HPF_CHECK(!e.ToggleAccent(Bank::A, 0, 16));
+ HPF_CHECK(!e.RequestPreset(8));
+ e.SetVoice(0, {NAN, INFINITY, -INFINITY, NAN});
+ e.Trigger(0, NAN);
+ for (int i = 0; i < 100; ++i)
+ HPF_CHECK(std::isfinite(e.Process().left));
+ HPF_CHECK_EQ(e.Faults(), 0u);
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_groove_clock.cpp b/field/HydraPulse/tests/native/test_groove_clock.cpp
new file mode 100644
index 000000000..540066326
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_groove_clock.cpp
@@ -0,0 +1,37 @@
+#include "src/core/GrooveClock.h"
+#include "tests/native/test_support.h"
+#include
+#include
+int main()
+{
+ using hydrapulse::core::GrooveClock;
+ for (const auto rate : {44100u, 48000u, 96000u})
+ {
+ for (const float swing : {0.0f, 0.5f, 1.0f})
+ {
+ GrooveClock c;
+ c.Init(rate);
+ HPF_CHECK(c.SetTempo(123.4567));
+ HPF_CHECK(c.SetSwing(swing));
+ c.Reset();
+ std::uint64_t n = 0, event = 0;
+ while (event < 512)
+ {
+ ++n;
+ HPF_CHECK(c.SetTempo(123.4567)); // emulate frequent control updates
+ if (!c.ProcessSample())
+ continue;
+ ++event;
+ const long double intervals = static_cast(event) +
+ ((event & 1u) ? static_cast(swing) * 0.4L : 0.0L);
+ const long double expected =
+ static_cast(rate) * 60.0L * intervals / (123.4567L * 4.0L);
+ HPF_CHECK(std::fabs(static_cast(n) - expected) < 1.00001L);
+ }
+ }
+ }
+ GrooveClock c;
+ HPF_CHECK(!c.SetTempo(0));
+ HPF_CHECK(!c.SetSwing(-1));
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_near_rejects_nan.cpp b/field/HydraPulse/tests/native/test_near_rejects_nan.cpp
new file mode 100644
index 000000000..1e4d1e82e
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_near_rejects_nan.cpp
@@ -0,0 +1,7 @@
+#include "tests/native/test_support.h"
+#include
+int main()
+{
+ HPF_CHECK_NEAR(std::numeric_limits::quiet_NaN(), 0.0f, 0.1f);
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_no_allocation.cpp b/field/HydraPulse/tests/native/test_no_allocation.cpp
new file mode 100644
index 000000000..beebe75bd
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_no_allocation.cpp
@@ -0,0 +1,59 @@
+#include "src/app/Controller.h"
+#include "tests/native/test_support.h"
+#include
+#include
+static bool monitoring = false;
+static unsigned allocations = 0;
+void *operator new(std::size_t n)
+{
+ if (monitoring)
+ ++allocations;
+ if (void *p = std::malloc(n ? n : 1))
+ return p;
+ throw std::bad_alloc();
+}
+void operator delete(void *p) noexcept
+{
+ std::free(p);
+}
+void operator delete(void *p, std::size_t) noexcept
+{
+ std::free(p);
+}
+void *operator new[](std::size_t n)
+{
+ return ::operator new(n);
+}
+void operator delete[](void *p) noexcept
+{
+ ::operator delete(p);
+}
+void operator delete[](void *p, std::size_t) noexcept
+{
+ ::operator delete(p);
+}
+int main()
+{
+ hydrapulse::Engine e;
+ e.Init();
+ hydrapulse::Controller c;
+ hydrapulse::InputFrame f{};
+ c.Init(e, f);
+ monitoring = true;
+ e.RequestPreset(7);
+ for (int n = 0; n < 1000; ++n)
+ (void)e.Process();
+ e.Start();
+ for (unsigned n = 0; n < 48000 * 2; ++n)
+ {
+ if (n % 48u == 0)
+ c.Tick(e, f);
+ if (n % 1201u == 0)
+ e.Trigger(n % 4u);
+ (void)e.Process();
+ }
+ e.Panic();
+ monitoring = false;
+ HPF_CHECK_EQ(allocations, 0u);
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_pattern16.cpp b/field/HydraPulse/tests/native/test_pattern16.cpp
new file mode 100644
index 000000000..23cc7e29b
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_pattern16.cpp
@@ -0,0 +1,64 @@
+#include "src/core/Pattern16.h"
+#include "tests/native/test_support.h"
+
+#include
+#include
+
+int main()
+{
+ using hydrapulse::core::Pattern16;
+ Pattern16<8> pattern;
+
+ for (std::size_t track = 0; track < 8; ++track)
+ for (std::size_t step = 0; step < 16; ++step)
+ HPF_CHECK(!pattern.IsActive(track, step));
+
+ HPF_CHECK(pattern.Set(3, 7, true));
+ HPF_CHECK(pattern.IsActive(3, 7));
+ HPF_CHECK(pattern.Toggle(3, 7));
+ HPF_CHECK(!pattern.IsActive(3, 7));
+
+ HPF_CHECK(!pattern.Set(8, 0, true));
+ HPF_CHECK(!pattern.Set(0, 16, true));
+ HPF_CHECK(!pattern.Toggle(99, 99));
+ HPF_CHECK(!pattern.IsActive(99, 99));
+
+ for (std::size_t step = 0; step < 16; ++step)
+ HPF_CHECK(pattern.Set(2, step, true));
+ HPF_CHECK_EQ(pattern.TrackMask(2), static_cast(0xFFFFu));
+ HPF_CHECK(pattern.ClearTrack(2));
+ HPF_CHECK_EQ(pattern.TrackMask(2), static_cast(0u));
+
+ // Deterministic property stress: mirror the bit operations in a reference model.
+ std::mt19937 rng(0x48504631u);
+ std::uint16_t reference[8]{};
+ for (int i = 0; i < 20'000; ++i)
+ {
+ const std::size_t track = rng() % 8u;
+ const std::size_t step = rng() % 16u;
+ const bool active = (rng() & 1u) != 0u;
+ const auto bit = static_cast(std::uint16_t{1u} << step);
+
+ if ((rng() & 1u) != 0u)
+ {
+ HPF_CHECK(pattern.Set(track, step, active));
+ if (active)
+ reference[track] = static_cast(reference[track] | bit);
+ else
+ reference[track] = static_cast(reference[track] & ~bit);
+ }
+ else
+ {
+ HPF_CHECK(pattern.Toggle(track, step));
+ reference[track] = static_cast(reference[track] ^ bit);
+ }
+
+ HPF_CHECK_EQ(pattern.TrackMask(track), reference[track]);
+ }
+
+ pattern.Clear();
+ for (std::size_t track = 0; track < 8; ++track)
+ HPF_CHECK_EQ(pattern.TrackMask(track), static_cast(0u));
+
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_regressions.cpp b/field/HydraPulse/tests/native/test_regressions.cpp
new file mode 100644
index 000000000..bfb1ebaa9
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_regressions.cpp
@@ -0,0 +1,40 @@
+#include "src/core/ControlPickup.h"
+#include "src/core/OutputTestArm.h"
+#include "src/core/SampleAccurateStepClock.h"
+#include "tests/native/test_support.h"
+#include
+int main()
+{
+ using namespace hydrapulse::core;
+ SampleAccurateStepClock c;
+ for (unsigned i = 0; i < 5999; ++i)
+ {
+ HPF_CHECK(c.SetTempoBpm(120));
+ HPF_CHECK(!c.ProcessSample());
+ }
+ HPF_CHECK(c.SetTempoBpm(120));
+ HPF_CHECK(c.ProcessSample());
+ SampleAccurateStepClock invalid(0, 0, 0);
+ HPF_CHECK_EQ(invalid.SampleRate(), 48000u);
+ HPF_CHECK_EQ(invalid.StepsPerBeat(), 4u);
+ HPF_CHECK_EQ(invalid.SamplesUntilNextStep(), 6000u);
+ HPF_CHECK(!c.SetTempoBpm(0.0000001));
+ HPF_CHECK(!c.SetTempoBpm(std::numeric_limits::infinity()));
+ HPF_CHECK(!c.SetTempoBpm(std::numeric_limits::quiet_NaN()));
+ HPF_CHECK_NEAR(c.TempoBpm(), 120, 1e-6);
+ ControlPickup p;
+ p.Reset(.6f, .1f);
+ HPF_CHECK(!p.Update(std::numeric_limits::quiet_NaN()));
+ HPF_CHECK_NEAR(p.Value(), .6f, 1e-6);
+ HPF_CHECK(p.Update(.7f));
+ HPF_CHECK(!p.Update(std::numeric_limits::infinity()));
+ HPF_CHECK_NEAR(p.Value(), .7f, 1e-6);
+ OutputTestArm arm;
+ HPF_CHECK(!arm.Update(true, false));
+ HPF_CHECK(!arm.Update(true, false));
+ HPF_CHECK(!arm.Update(false, false));
+ HPF_CHECK(arm.Update(true, false));
+ HPF_CHECK(!arm.Update(true, true));
+ HPF_CHECK(!arm.Update(false, false));
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_sample_clock.cpp b/field/HydraPulse/tests/native/test_sample_clock.cpp
new file mode 100644
index 000000000..09953cf46
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_sample_clock.cpp
@@ -0,0 +1,46 @@
+#include "src/core/SampleAccurateStepClock.h"
+#include "tests/native/test_support.h"
+
+#include
+#include
+
+int main()
+{
+ using hydrapulse::core::SampleAccurateStepClock;
+
+ SampleAccurateStepClock clock(48'000u, 120.0, 4u);
+ HPF_CHECK_EQ(clock.SamplesUntilNextStep(), std::uint64_t{6'000u});
+
+ std::uint64_t samples = 0u;
+ while (!clock.ProcessSample())
+ ++samples;
+ ++samples; // include the triggering sample
+ HPF_CHECK_EQ(samples, std::uint64_t{6'000u});
+
+ HPF_CHECK(clock.SetTempoBpm(123.4567));
+ HPF_CHECK_NEAR(clock.TempoBpm(), 123.4567, 0.000001);
+
+ constexpr std::uint64_t kEvents = 1024u;
+ std::uint64_t event_count = 0u;
+ std::uint64_t sample_index = 0u;
+
+ const long double exact_interval = (48'000.0L * 60.0L) / (123.4567L * 4.0L);
+
+ while (event_count < kEvents)
+ {
+ ++sample_index;
+ if (clock.ProcessSample())
+ {
+ ++event_count;
+ const long double ideal = exact_interval * static_cast(event_count);
+ const long double error = std::fabs(static_cast(sample_index) - ideal);
+ HPF_CHECK(error <= 1.0L);
+ }
+ }
+
+ HPF_CHECK(!clock.SetTempoBpm(0.0));
+ HPF_CHECK(!clock.SetTempoBpm(-120.0));
+ HPF_CHECK(!clock.SetSampleRate(0u));
+
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_spsc.cpp b/field/HydraPulse/tests/native/test_spsc.cpp
new file mode 100644
index 000000000..3af49e958
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_spsc.cpp
@@ -0,0 +1,39 @@
+#include "src/core/SpscQueue.h"
+#include "tests/native/test_support.h"
+#include
+#include
+#include
+int main()
+{
+ using hydrapulse::core::SpscQueue;
+ SpscQueue q;
+ HPF_CHECK(q.Push(1));
+ HPF_CHECK(q.Push(2));
+ HPF_CHECK(q.Push(3));
+ HPF_CHECK(!q.Push(4));
+ std::uint32_t x = 0;
+ for (unsigned i = 1; i <= 3; ++i)
+ {
+ HPF_CHECK(q.Pop(x));
+ HPF_CHECK_EQ(x, i);
+ }
+ HPF_CHECK(!q.Pop(x));
+ SpscQueue threaded;
+ std::atomic bad{false};
+ constexpr std::uint32_t count = 200000;
+ std::thread producer([&]() {
+ for (std::uint32_t i = 0; i < count; ++i)
+ while (!threaded.Push(i))
+ std::this_thread::yield();
+ });
+ for (std::uint32_t i = 0; i < count; ++i)
+ {
+ while (!threaded.Pop(x))
+ std::this_thread::yield();
+ if (x != i)
+ bad.store(true);
+ }
+ producer.join();
+ HPF_CHECK(!bad.load());
+ return EXIT_SUCCESS;
+}
diff --git a/field/HydraPulse/tests/native/test_step_sequencer.cpp b/field/HydraPulse/tests/native/test_step_sequencer.cpp
new file mode 100644
index 000000000..a0fbc5c20
--- /dev/null
+++ b/field/HydraPulse/tests/native/test_step_sequencer.cpp
@@ -0,0 +1,58 @@
+#include "src/core/StepSequencer.h"
+#include "tests/native/test_support.h"
+
+#include
+#include
+#include