Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/fix_style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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(
[
Expand Down
105 changes: 105 additions & 0 deletions .github/workflows/hydrapulse.yml
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions field/HydraPulse/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
BasedOnStyle: LLVM
IndentWidth: 4
ColumnLimit: 110
BreakBeforeBraces: Allman
AllowShortFunctionsOnASingleLine: Empty
SortIncludes: true
3 changes: 3 additions & 0 deletions field/HydraPulse/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Preserve archived source and raw acquisition bytes across checkouts.
/references/r0/** -text
/evidence/**/*.log -text -diff
14 changes: 14 additions & 0 deletions field/HydraPulse/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/build/
/build-*/
/.deps/
/artifacts/
/renders/
__pycache__/
*.pyc
*.o
*.elf
*.bin
*.hex
*.map
*.lst
.DS_Store
32 changes: 32 additions & 0 deletions field/HydraPulse/AGENTS.md
Original file line number Diff line number Diff line change
@@ -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.
44 changes: 44 additions & 0 deletions field/HydraPulse/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
8 changes: 8 additions & 0 deletions field/HydraPulse/LICENSE_STATUS.md
Original file line number Diff line number Diff line change
@@ -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.
39 changes: 39 additions & 0 deletions field/HydraPulse/Makefile
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading