Commit graph

16 commits

Author SHA1 Message Date
Me Here
dbc9fa7fdc PM_K-1 0.1.0: on-device lane editor - edit/add/remove lanes
Tap the instrument name -> a modal to change Sound (cycle the GM voices), Beats (1-12),
Subdivision (1-8), Swing, and Mute, plus + Lane / Remove (1..MAXLANES). Beats/sub changes
regenerate the lane's default accents; sound/swing/mute keep the pattern. Reuses the
existing dirty + Save/Revert + .mpy machinery (edits to a built-in save a copy to
"My edits"). The modal redraws live as you adjust; tap Done or outside to close.

Verified in harness: editor opens (13 hit-zones), sound cycles, beats/sub regen steps,
swing/mute toggle, add/remove lanes, the edited track serializes + round-trips, Done
closes; modal renders cleanly. app.mpy builds (C/v6).

This completes the Phase-2 editing set (beats + lanes + Continue + built-in/user split).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 14:44:04 -05:00
Me Here
7481f91935 PM_K-1 0.0.10: ship precompiled app.mpy (fixes boot OOM) + push .mpy over the air
The single-file app grew to ~57KB; CircuitPython compiling it at boot fragments the
RP2040 heap so badly that the fonts can't get a contiguous block (161KB free, yet a
~16KB alloc fails). Fix: precompile to app.mpy (Adafruit mpy-cross for CP 10.2.1, emits
CircuitPython mpy v6) so the device loads bytecode without compiling -> no fragmentation.

- build.sh precompiles pico-cp/app.py -> dist/app.mpy via tools/mpy-cross (gitignored
  binary); the bundle ships app.mpy (NOT app.py); serves pico-cp-app.mpy + pico-cp-app.py
  (the .py only for the editor's version regex + as readable reference).
- Loader (code.py) imports app.mpy and rolls back app.bak as .mpy.
- One-click updater now pushes the .mpy: editor base64-encodes it and sends it over the
  existing flow-controlled chunked transport (512-char = mult-of-4 chunks); the device
  base64-decodes each chunk to /app.new and verifies the CircuitPython .mpy header
  (magic 'C', v6, >=4KB) before the A/B install. Version still read from the served .py.

Verified: mpy-cross emits magic 'C'/v6; build produces a 21.8KB app.mpy; editing-logic
harness + scene render still pass; and a simulated push (base64 -> 57 chunks -> a2b_base64)
reassembles the .mpy byte-exact and passes the device's header check.

One-time recovery: delete app.py from the drive, copy app.mpy + code.py from the new zip.
After that, updates are one-click again (and can't brick: header check + A/B rollback).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 14:01:57 -05:00
Me Here
7dd567fb44 PM_K-1 0.0.9: on-device editing (tap beats, save/revert) + Continue auto-advance
- Tap a beat to cycle it (off->normal->accent->ghost); the title turns red (unsaved).
  Tap the title -> SAVE / REVERT modal. Editing a built-in saves a COPY into a "My edits"
  user playlist (built-ins stay read-only); editing a user item updates it in place.
  Saves persist to programs.json (NAKs gracefully in editor mode / read-only).
- New round-trippable serializer (lane_to_str/_prog_str): parser now keeps groups + @db
  gain + ramp start; verified parse->serialize->parse on all 23 built-ins (0 mismatches).
- Continue (CONT) toggle, top-right of the tab line: when on, a playlist auto-advances to
  the next item at the end of each item's b<n> segment (no log spam, keeps the stopwatch).
- Touch routing consolidated: tab=switch playlist / CONT, title=save-revert, pads=cycle,
  log=delete; modal overlay drawn on top.

Verified in the harness: beat cycle+dirty, built-in edit -> My edits persisted (built-ins
untouched), revert, Continue arming at segment end, overlay SAVE-tap, and both renders.

Next (0.1.0): tap the instrument name -> lane-parameter table (reuses this save machinery).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 13:23:27 -05:00
Me Here
88104e3d5c PM_K-1 0.0.7: perform tempo ramps + gap trainer, show their indicators, log bars
Device parser now reads the rmp<start>/<amt>/<every> and tr<play>/<mute> tokens it
previously ignored, and the firmware performs them:
- Tempo ramp: steps bpm by <amt> every <every> bars (resets to the start at each b<n>
  segment boundary). Shows an amber ramp arrow + "+amt/everyb" (up/down by sign; no
  starting bpm, per request).
- Gap trainer: cycles <play> audible bars then <mute> silent bars (no click/MIDI/LED;
  playheads keep moving). Shows a play|rest symbol + "play/muteb".
- Practice log entries now record + show bars played.

Verified in the CPython harness: ramp 92->96->100->104->108 (+4 every 2 bars), gap
mute cycle play,play,mute,mute, and the on-screen ramp indicator renders.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 11:43:54 -05:00
Me Here
ca44aa833d PM_K-1 firmware: screen refinements (run-state bg tint, time/bar totals, per-track log, square/circle pads, separated track #)
App.py-only (ships over the one-click updater). APP_VERSION -> 0.0.3.

- Run/stop is now a background tint (gray running / near-black stopped) instead of
  STOP text, reclaiming the space.
- Running time + bar counter show "of total" when the track has a b<n> length:
  "1:23 of 2:00" and "bar N of 16" (bar cycles 1..N); total time derived from
  bars x master-beats-per-bar x 60/bpm. Parser now reads the b<n> token.
- Practice log is filtered to the current track (drops the redundant track column).
- Pads: squares for the main pulse, circles for subdivisions (was square + hollow
  outline); fewer vectorio shapes too.
- Track number set apart from the title (small + dim, right) so it no longer reads
  as part of the title.

On-device editing (tap instrument -> lane table; tap beat -> cycle state; dirty-name
-> confirm save/revert) is deferred to Phase 2, where "save" has a correct destination
(an edited built-in saves as a user copy).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 09:41:57 -05:00
Me Here
dec6c61fce PM_K-1 firmware Phase 1: VARASYS logo, MIDI/USB status icons, square/outline beats, beat gridlines, stopwatch + bar counter
Device screen redesign (CircuitPython app.py), built proportional to WIDTH/HEIGHT
so it scales to other panels (one adaptive firmware, per-panel config — not a fork):

- gen_assets.py bakes logo.bin (VARASYS wordmark, no tagline), midi.bin (DIN-5),
  usb.bin (trident) as 4-bit-alpha bitmaps (same packing as the fonts).
- Header: VARASYS logo (brand cyan) replaces the "PM_K-1 KIT" text; MIDI icon goes
  green when a host is listening, USB icon lights when supervisor.runtime.usb_connected.
  load_alpha/make_glyph are non-fatal — a missing .bin falls back to text, never a
  black screen (addresses the corrupt-file failure mode we just hit).
- Pad grid: filled squares on main beats, hollow outline squares (outer+inner rect) on
  off-beats; playhead fills the lit pad. Vertical gridlines at the master lane's beats
  (full height) so beats line up across lanes.
- Stopwatch (m:ss) + bar counter (master-lane cycles), refreshed ~4x/s only on change.

The .bin assets ship in the drive bundle (the A/B updater only pushes app.py), so a
one-time re-copy is needed to pick them up. APP_VERSION -> 0.0.2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 08:56:45 -05:00
Me Here
e8945ee1d1 PM_K-1: one-click A/B firmware updates over USB-MIDI (+ version check)
Split the CircuitPython firmware into a tiny stable loader (code.py) + the application (app.py,
carries APP_VERSION). The editor's ⋯ → "⬆ Update firmware" queries the device version (SysEx 0x02
-> 0x03 reply), fetches the latest app from the site (/pico-cp-app.py), shows device-vs-latest, and
pushes the new app.py over USB-MIDI (SysEx 0x20). The device installs it to a trial slot (old build
kept as app.bak), reboots, and the loader AUTO-ROLLS-BACK to app.bak if the new build fails to start;
a build that runs cleanly ~5s is confirmed (clears /trial). No BOOTSEL, no dragging; Chromium/Firefox.
app.py forced to pure ASCII so it pushes raw (no base64); SysEx buffer raised to 60KB.

build.sh/deploy.sh: bundle code.py+app.py and serve /pico-cp-app.py. Docs updated.

Verified in CPython: version reply, update install+reboot+ACK, rollback file dance; editor loads clean
with the updater wired.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 06:55:58 -05:00
Me Here
7d743c18a1 PM_K-1: appliance model — push-programming over USB-MIDI, on-device practice log, swing fix
Firmware (pico-cp/): the Pico now owns its filesystem by default (boot.py), so it can save the
practice log and write editor-pushed set lists; the drive is read-only to the computer, which also
protects the firmware. Hold button A at power-on for editor mode (drive writable; universal drag).
  - Replaced the on-screen touch buttons with an on-device PRACTICE LOG (time · BPM · duration ·
    track), newest-first, persisted to /history.json next to programs.json. Plays < 5s aren't logged;
    tap a row twice to delete it. Real timestamps once the editor syncs the clock.
  - USB-MIDI SysEx receiver: clock-set (0x01 -> RTC) and program-push (0x10 -> write programs.json,
    reload, ACK/NAK). disable autoreload so our own writes never self-restart.
  - Fixed swing: the parser was discarding the 's' flag, so /2s never swung. Now the scheduler uses a
    per-step duration with long-short (2:1, SWING_RATIO 2/3) pairs on even subdivisions, matching the
    web engine. Verified: ride:4/2s -> 266/133ms vs straight 200/200.

Editor (editor.html): requestMIDIAccess({sysex:true}); Save to device now pushes programs.json as
SysEx to the device (+ clock sync), waits for ACK, shows "Saved ✓", and falls back to downloading the
file (drag onto the drive in editor mode) when no device answers. Heartbeat also keeps the clock synced.
Web MIDI works in Chromium AND Firefox; the drag fallback covers any browser/OS incl. Safari.

Docs (pico-cp/README, info-kit, README) updated for the two modes, push programming, and the log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 00:38:08 -05:00
Me Here
09b20a9e69 PM_K-1: add firmware-protect helper (hide files so users only see editor + programs)
protect-firmware.sh sets the FAT hidden attribute on the firmware files (code.py, boot.py,
font_*.bin, README) on a mounted CIRCUITPY drive, so an end user sees only editor.html +
programs.json and can't accidentally delete the program — the hidden files keep running and
Save to device still works. Documented in pico-cp/README (incl. the read-only boot.py
hard-lock alternative) and bundled into pm_k1_circuitpy.zip. README.md verified accurate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:58:12 -05:00
Me Here
b5584454b3 PM_K-1 CircuitPython: add boot.py to guarantee the USB-MIDI port appears
If the device isn't seen as a MIDI input (USB endpoint pressure from drive+serial+HID+MIDI),
boot.py disables the unused HID interface and enables usb_midi — copy it on and power-cycle.
Bundled into pm_k1_circuitpy.zip; documented in the README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:57:23 -05:00
Me Here
7ccc75e399 Phase 3: USB-MIDI audio — play the device through the computer's speakers
Firmware (pico-cp/code.py): on every click, send a USB-MIDI note-on per firing lane —
GM drum note by voice (SOUND_GM), velocity by level (accent/normal/ghost) — via the
default-enabled usb_midi.ports[1]. Polyphonic, so the computer plays the full groove.
New CONFIG: MIDI_ENABLED (default on), MUTE_BUZZER (silence the buzzer when using
computer audio).

Editor (editor.html): a '🎹 Device audio' toggle uses the Web MIDI API
(requestMIDIAccess) to voice incoming notes through the existing synth — Note-On ->
GM_NUM[note] / velocity-to-gain -> playInstrument(). The device is the clock; the
browser is the sound module, locked in sync. Chrome/Edge.

Verified: firmware emits the right notes (kick+hat on beat 1 of four-on-the-floor,
snare's rest skipped); editor loads clean with the toggle + handlers present. Docs
(info-kit, both READMEs) updated. The on-device buzzer/screen still work standalone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:40:08 -05:00
Me Here
d558dccbde Phase 2: editor 'Save/Load to device' + bundle the editor on the drive
Add to the editor's set-list ⋯ menu:
  - 📟 Save to device — writes the active set list as programs.json (the same file the
    PM_K-1 firmware reads). Uses the File System Access API to write straight onto the
    CIRCUITPY drive (Chrome/Edge); falls back to a download to drag on. Reuses
    setupToPatch() per item -> {title, programs:[{name, prog}]}.
  - 📥 Load from device — reads a programs.json back into a new set list (patchToSetup
    per item; reuses the existing import path).
Bundle the built editor.html into pm_k1_circuitpy.zip so the drive carries its own
offline programmer. info-kit + pico-cp/README document the workflow.

Verified: editor loads with no console errors; both menu buttons + all four functions
present; zip contains editor.html. (FSA save needs a real user gesture to test on-device.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:20:35 -05:00
Me Here
ec43c694a1 PM_K-1 CircuitPython: circle pad grid, small labels, dimmer LED, faster SPI
From on-board feedback (works well; minor tweaks):
  - Pad grid uses circles now: big circle on each beat (division), small on the
    subdivisions (vectorio.Circle — native, no extra cost), coloured/lit as before.
  - Lane labels use a new small font (font_s.bin, ~12px via gen_font.py) so they're
    half-size and show more of the voice name (e.g. 'hatClos').
  - LED was blinding -> LED_BRIGHTNESS scale (default 0.15) applied on every write.
  - Residual tearing -> SPI back to 62.5 MHz (vendor speed; smaller tear window on a
    panel with no tearing-effect pin). Both are CONFIG flags.
Verified by rendering the full scene headless. font_s.bin added to gen_font.py + bundle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:07:00 -05:00
Me Here
ffededd05b PM_K-1 CircuitPython: self-contained RGB LED + fix screen tearing
From on-board feedback (memory + colours now good):
  - LED: drive the WS2812 via the core neopixel_write module (no neopixel library to
    install) — a tiny RGB class. Self-contained: it works straight from the bundle.
  - Tearing: switch displayio to auto_refresh=False and push a complete frame only when
    the scene changed (dirty flag, capped at the panel's refresh rate) so updates are
    never shown mid-paint. Beat dots now recolour in place (vectorio color_index) instead
    of being rebuilt every beat, shrinking the dirty region.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 21:43:48 -05:00
Me Here
111da49b5a PM_K-1 CircuitPython: fix MemoryError + red/blue swap (from on-board test)
On hardware the app rendered, beeped and took input, then died with MemoryError at
the text Bitmap alloc — the two ~37KB base64 font strings stayed pinned in RAM. Move
the fonts to small binary files read at boot (font_m.bin / font_l.bin), drop the
base64 + binascii, and gc.collect() before each text bitmap. code.py 56KB -> 20KB and
RAM use drops ~37KB+. Also: cyan rendered as yellow (R/B swapped) -> MADCTL 0x40 -> 0x48.
Bundle + README updated to include the font blobs. (LED still needs the neopixel lib.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 21:30:03 -05:00
Me Here
b419ad0daa Phase 1: CircuitPython firmware (USB-drive edition) for the Kit
New pico-cp/ — a CircuitPython port of the PM_K-1 firmware so the Pico mounts as a
CIRCUITPY drive carrying its code + tracks (the MicroPython pico/main.py stays the
simple fallback):
  - pico-cp/code.py: displayio BusDisplay driving ST7796 via a custom init_sequence;
    smooth anti-aliased text via displayio Bitmap+Palette (reuses the baked font blobs);
    vectorio rects for dots/buttons; DIY GT911 touch (16-bit regs, edge-detected);
    pwmio buzzer, analogio joystick, digitalio buttons, optional neopixel RGB; the
    polymeter engine on a time.monotonic_ns scheduler. Reads /programs.json (falls back
    to baked defaults); CircuitPython auto-reloads on file change.
  - pico-cp/programs.json: the 23 default grooves. pico-cp/README.md: flash + calibrate.
  - build.sh/deploy.sh: bundle + serve /pm_k1_circuitpy.zip. info-kit.html: experimental
    'CircuitPython edition — USB drive' section.

Verified in CPython (stubbed displayio): init sequence well-formed, parser handles the
grooves incl. (3,8) euclid + @-4 gain, and code.py's actual make_text renders identical
smooth AA text. Hardware bits (panel/touch/MIDI) await on-board testing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 21:10:34 -05:00