feat: add Grooves app - #604
Draft
kosmar wants to merge 29 commits into
Draft
Conversation
Multi-genre MIDI drum helper with swing, density fills, and jack modes (app ID 29), plus configurator manual with gesture tables.
This was referenced Jul 22, 2026
kosmar
marked this pull request as draft
July 24, 2026 14:17
Authored by an AI coding agent on behalf of kosmar.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
3 tasks
…grooves Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # faderpunk/src/app.rs
…t/grooves Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # faderpunk/src/storage.rs
…t+Hold Shift+Tap plays a genre-specific fill that resolves on the next downbeat, turning subtractive into a break above roughly 60 % density. Holding Shift escalates into a four-bar solo phrase (build, busier, peak, turnaround) with its own accent mask instead of looping the fill; releasing drops back into the end-weighted fill so it always lands on the one. Each genre carries three fill, break and solo figures, with Feel rolling between them. Jack Mode and Swing Dir move to the Configurator to free the Shift+Long gesture. Authored by an AI coding agent on behalf of kosmar. Co-authored-by: Cursor <cursoragent@cursor.com>
The simulator catalog generator turned every path expression into an enum tag,
so `variants: GENRE_NAMES` came out as `{ tag: "GENRE_NAMES" }` where the
TypeScript side expects `string[]`, breaking the configurator build. Top-level
consts an app references now resolve to their values — both integer bounds and
string-array variant lists — and a signed bound like `min: -100` parses instead
of panicking.
Const names are not unique across app modules (Bassment and Chord Vamp each
declare their own `SCALE_NAMES`), so resolution goes module-local first and
only then falls back to consts declared in another app module.
Authored by an AI coding agent on behalf of kosmar.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds Open Hat, Low Tom, High Tom and Clap. Each is derived from a hit the groove already plays, so no genre needs extra mask data: Clap layers under the accented snare, Open Hat takes over an accented offbeat hat (muting the closed one), and the toms lift the snare figure into a descending run in the back half of a fill, break or solo. Note 0 switches a voice off, which is the default — with all four off the app behaves exactly as before. To fit seven voices in the 16-param budget, the per-voice channels move into one packed i32: seven nibbles, 0 meaning "follow the base MIDI Ch". Jack, Jack Mode and CV Dest collapse into a single enum (a CV In destination and a CV Out level shape were never live at once), and Swing Dir folds into the sign of Swing max %. Stacked CV keeps its binary-weighted family voltages (hats 1, snare 2, kick 4 of 10) by folding the new voices into the family they belong to, so existing patches stay calibrated. The jack now follows the groove rather than the note assignment, so a CV-only patch still pulses. Authored by an AI coding agent on behalf of kosmar. Co-authored-by: Cursor <cursoragent@cursor.com>
Microtiming moves from clock ticks to milliseconds, so the pocket no longer snaps to the 20.8 ms tick grid. Each drum family carries its own push and jitter width scaled by a per-genre tightness, gate lengths are per voice so closed hats choke inside their step while open hats and toms ring past it, and velocity gains jitter, offbeat hat ducking and a phrase arc. At Groove (Feel) = 0 everything collapses onto the grid, so Grooves still locks against the other apps. Authored by an AI coding agent on behalf of kosmar. Co-authored-by: Cursor <cursoragent@cursor.com>
The branch still used the ID it was first drafted with. ID 29 now belongs to another app in docs/wip-app-ids.md, and 28 is reserved for Sift, so two of these app branches would have collided on merge. Authored by an AI coding agent on behalf of kosmar.
Review feedback on ATOVproject#639: a dropped NoteOff hangs the note with no later message to correct it, and APP_MIDI_CHANNEL is shared by all 16 channels, so a busy neighbour can cause the drop. All three call sites run in the async voice task, so awaiting queue space costs at most a late NoteOff — and with per-voice drum channels a leaked voice would drone until the next hit on that same voice. Drops the now-unused try_send_note_off from the branch copy of app.rs, matching ATOVproject#639. Authored by an AI coding agent on behalf of kosmar. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # gen-bindings/src/catalog.rs
Same mutate+save API as ATOVproject#640 so WIP apps can call params.update now. Host AppState push stays out until the real writeback lands. Authored by an AI coding agent on behalf of kosmar. Co-authored-by: Cursor <cursoragent@cursor.com>
ParamStore::update polyfill (mutate+FRAM, no host push). Authored by an AI coding agent on behalf of kosmar. Co-authored-by: Cursor <cursoragent@cursor.com>
…branch Brings grooves.rs, groove.rs and genre_palette.rs up to the state the playground integration branch has been flashing, and adds the shared ornament module they now depend on. The platform work that landed alongside it there (perf-mute, non-blocking notes, panic beacon) stays on its own feature branches and is deliberately not pulled in. Bassment and Contura are the other consumers of the shared modules and live on their own branches, so their helpers read as dead code here. The lint is silenced at the module declaration to keep the shared files byte-identical to the integration branch. Authored by an AI coding agent on behalf of kosmar. Co-authored-by: Cursor <cursoragent@cursor.com>
Adds eight drummer personas that transform the genre pattern after it is read, the way Bassment's bassist voices do: Elvin, Purdie, Clyde, Jaki, Sly, Allen, Pretty Tony and Dilla. Each profile carries shuffle, ghost density, backbeat lock, tom dispersal, clave rotation, roll rate, push or pull, limb drift and a machine amount, and every one of them is Feel scaled, so a flat Feel still plays the plain genre grid. Long press cycles the drummer, matching Bassment. The reset it used to carry moves onto the unmute, so coming back in still lands on the downbeat, and CV In Reset is unchanged. The app was already at the sixteen param ceiling, so the separate Range param folds into the Jack enum, which now carries duty, stacked mode and range together. Stored blobs migrate on the value type at index 14, which keeps both the duty and the range the user picked. Authored by an AI coding agent on behalf of kosmar. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Note on the param ceiling
The app was already using all sixteen slots
APP_MAX_PARAMSallows, so the separate Range param folds into the Jack enum, which now carries duty, stacked mode and range together. Stored blobs migrate on the value type at index 14, which preserves both the duty and the range the user had picked.Gestures
Test plan
Prerequisites
The platform pieces this app needs are under review as their own PRs, so that each app PR stays small enough to review on its own.
Required, since these APIs do not exist on current
main:App::clock_tickertry_send_*)ParamStore::update, so an app can write its own params back to the hostgenre_palette/groove/led_fxmodules, plus the sharedornamentmodule the personas build onThis branch currently carries its own inline copy of the code in #638, #639, #640 and #641. Once those land it will be rebased to drop the duplicates, which shrinks this diff to the app file plus its registry line.
Building any of this on a current Rust nightly also needs the toolchain fix in #632.
Note: ID 28 is reserved for Sift; Heat Pump holds 29 (#603). These PRs are independent from
main.