diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..4d0ed3537 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: CI + +on: + push: + branches: [ ps-1.13, feat/** ] + pull_request: + +# Every step below calls a Makefile target. No test file is named here or in the +# Makefile: `unittest discover` finds test/test_*.py and `node --test` finds +# test/js/**/*.test.js. A new test runs without a change to either file. +# `make test` runs all of it at once and is what you run before you push. + +jobs: + python: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Check that the server files parse + run: make compile + - name: Run the Python tests + run: make test-py + + frontend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Run the JavaScript tests + run: make test-js + - name: Check the JSDoc types + run: make typecheck diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 000000000..fbf31d295 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +GUIDE.md \ No newline at end of file diff --git a/GEMINI.md b/GEMINI.md new file mode 120000 index 000000000..fbf31d295 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1 @@ +GUIDE.md \ No newline at end of file diff --git a/GUIDE.md b/GUIDE.md new file mode 100644 index 000000000..253969c4e --- /dev/null +++ b/GUIDE.md @@ -0,0 +1,219 @@ +# mod-ui — the pi-stomp "ultimate" fork + +> This document is the single source of truth for *this* version of `mod-ui` — the one sitting at `/Users/cam/dev/mod-ui`, on branch `more-fixes`, with the PatchStorage integration. It is meant for Claude, Gemini, and any future coding agent that has to touch this code. + +## What this repo is + +This is a fork of [mod-audio/mod-ui](https://github.com/moddevices/mod-ui), the web UI + control layer for the MOD audio ecosystem. It runs as a Python/Tornado webserver and talks to `mod-host` over a socket. It discovers LV2 plugins and pedalboards via `lilv`, serves an HTML5 pedalboard editor, and pushes real-time state to connected browsers and hardware peers over WebSocket. + +This fork is customized for the **pi-stomp** guitar-pedal hardware platform. It lives as a sibling to `pi-stomp`, `pi-gen-pistomp`, `pistomp-recovery`, etc. + +### Why "ultimate" + +This branch is the accumulation of several feature/experimental branches that were merged together to make a single "do everything" target: + +- PatchStorage-based plugin/pedalboard store in the web UI +- nested user pedalboard directories (`~/.pedalboards//.pedalboard`) +- output clipping indicator (WebSocket push) with mod-host meter flow-control fixes +- WebSocket BPM/BBT rebroadcast between browser clients +- local WebSocket clients (i.e. pi-stomp) skip meter flow-control and self-acknowledge `output_data_ready` +- hardening around `param_set` / bypass when parameters do not exist + +The user describes this as the **"ultimate version"** — the richest feature set — but some features were discussed and never implemented. Those are documented below under [Features we talked about but did not build](#features-we-talked-about-but-did-not-build). + +## How to build / run locally + +This is a Linux project, but it has been built/tested on macOS for development. The C++ utils library is the only compiled component. + +```bash +# Python deps +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt + +# Native utils (lilv + jack + optional alsa) +make -C utils +``` + +To run locally against a MOD Desktop / mod-host instance, set the expected environment variables. A typical local run: + +```bash +export MOD_DEV_ENVIRONMENT=0 +export MOD_DEVICE_WEBSERVER_PORT=18181 +export MOD_HOST_PORT=5555 +export MOD_HOST_FEEDBACK_PORT=5556 +export MOD_DATA_DIR=$HOME/.mod-data +export MOD_USER_PEDALBOARDS_DIR=$MOD_DATA_DIR/.pedalboards +export MOD_USER_FILES_DIR=$MOD_DATA_DIR/user-files +export MOD_KEYS_PATH=$MOD_DATA_DIR/keys/ +export MOD_HTML_DIR=$PWD/html +export MOD_DEFAULT_PEDALBOARD=$PWD/default.pedalboard +export MOD_CLOUD_API=https://api.moddevices.com/v2 +export PATCHSTORAGE_API_URL=https://patchstorage.com/api/beta/patches +export PATCHSTORAGE_PLATFORM_ID=8046 +export PATCHSTORAGE_TARGET_ID=8280 +export JACK_PROMISCUOUS_SERVER=jack +python3 server.py +``` + +On the device these values are injected by the `mod-ui.service` in `pi-gen-pistomp` (see `debpkgs/mod-ui/debian/mod-ui.mod-ui.service`). + +## Testing a branch on a pi-Stomp + +`./deploy.sh` copies this working tree over the packaged mod-ui on the device and restarts it; `./deploy.sh restore` puts the packaged version back. See [`docs/deploying-to-pistomp.md`](docs/deploying-to-pistomp.md) for the recipe and its limits. + +## Repo layout + +| Path | Purpose | +|------|---------| +| `mod/` | Core Python server: webserver, session, host logic, settings | +| `mod/host.py` | Plugin/pedalboard host controller; most WS dispatch lives here | +| `mod/session.py` | WebSocket session management, local-vs-remote client handling | +| `mod/webserver.py` | HTTP endpoints and pedalboard/config API | +| `mod/settings.py` | Environment-based defaults (PatchStorage IDs live here) | +| `utils/` | C/C++ helpers compiled into `libmod_utils.so` | +| `utils/utils_lilv.cpp` | Plugin/pedalboard discovery via lilv; nested pedalboards added here | +| `utils/patchstorage.cpp` | C++ helpers for PatchStorage metadata | +| `html/js/patchstorage.js` | Browser-side PatchStorage store UI and API client | +| `docs/output-data-flow.md` | How `output_data_ready` / `data_finish` meter flow-control works | + +## Key features implemented in this fork + +### 1. PatchStorage integration + +The browser can open a **PatchStorage** tab to browse, install, update, and remove LV2 plugins and pedalboards from the cloud. + +- Default endpoint: `https://patchstorage.com/api/beta/patches` +- Default platform: **8046** ("LV2 Plugins") +- Default target: **8280** (`rpi-aarch64`) — the pi-stomp image is 64-bit ARM +- These values are configured through environment variables (`PATCHSTORAGE_*`) and exposed to the frontend as globals in `html/index.html`. +- Each installed patch writes a `patchstorage.json` into the bundle so the UI can detect "outdated" content and offer updates. + +> Historical note: the original hardcoded IDs `5027`/`5037` were deleted by PatchStorage. This fork already moved to the current IDs (`8046`/`8280`) via the systemd environment. + +### 2. Nested pedalboard directories + +Upstream mod-ui / lilv only loads pedalboards that are **immediate children** of each LV2_PATH entry. We added a bounded directory walk in `utils/utils_lilv.cpp` so boards can live one level deeper, e.g. + +``` +~/.pedalboards/ + Live/ + FuzzSong.pedalboard/ + Studio/ + CleanPlate.pedalboard/ +``` + +The walk is intentionally defensive: +- max 2 levels deep +- skips hidden dirs, `.git`, `node_modules`, `__pycache__` +- only appends a sub-folder to `LV2_PATH` if it directly contains at least one `*.pedalboard` child +- caps each directory scan at 1024 entries and warns once + +### 3. Output clipping indicator + +A WebSocket message `clip_status