diff --git a/configurator/src/components/ManualTab.tsx b/configurator/src/components/ManualTab.tsx index 375715377..f46321cf0 100644 --- a/configurator/src/components/ManualTab.tsx +++ b/configurator/src/components/ManualTab.tsx @@ -1595,6 +1595,102 @@ On load, both registers are restored at the next phrase boundary so the recalled }, ], }, + { + appId: 29, + title: "Grooves", + description: "Multi-genre MIDI drum grooves with feel", + color: "Orange", + icon: "die", + params: [ + "MIDI Note Kick", + "MIDI Channel Kick", + "MIDI Note Snare", + "MIDI Channel Snare", + "MIDI Note Hats", + "MIDI Channel Hats", + "Groove", + "Swing max %", + "GATE %", + "Color", + "MIDI Out", + "Jack", + "Range", + "CV Dest", + "CV Att", + ], + storage: ["Feel", "Density", "Jack mode", "Reversed", "Muted"], + text: `Grooves is a one-channel multi-genre drum helper. It always sends three MIDI voices — **Kick**, **Snare**, and **Hats** — clock-synced to a 4/4 bar. Genre DNA includes accents, microtiming, and swing bias; **Feel** (Button + Fader) attenuates that character from flat/quantized to full groove. + +#### Groove (genre) + +Configurator **Groove** or **Shift + Fader** on device selects the pattern family (Shift+Fader scrub persists the param). + +What changes by genre (core hits before Density fills): +- **Dub** — sparse kick on 1 & 3, snare 2 & 4, thin offbeat hats; heavy 1 accent +- **Disco** — four-on-the-floor, clap/snare 2 & 4, offbeat hats +- **House** — classic four-on-the-floor + offbeat hats +- **Techno** — four-on-the-floor, denser hats (Feel mainly via hat dynamics), snare mostly on 4 +- **Trip-Hop** — laid-back sparse kick/hats, late snare pocket +- **Hip-Hop** — syncopated boom-bap kicks with strong 1+3 accents, snare 2 & 4 +- **Jungle** — amen-ish breakbeat; busy kick, snare 2 & 4, rapid hats +- **UK Garage** — skippy kick/hat syncopation, snare 2 & 4 +- **Dubstep** — half-time: kick on 1, snare on 3 + +#### Density, Feel, jack + +**Density** reveals extra kick/snare/hat fills as quiet ghosts that get louder past their reveal point. + +**Feel** (0 = flat equal velocities + straight grid; max = full genre accents, microtiming, and swing bias). Swing amount is genre bias × Feel, capped by **Swing max %**. Ghost drag also scales with Feel. + +**CV Dest** can modulate Density, Feel, or Reset. Jack **Any** / **Stacked** toggled with Shift + long. + +#### Gestures + +| Control | Action | +| --- | --- | +| **Fader** | Density — reveal / loudness of fill hits | +| **Shift + Fader** | Genre scrub (LED shows genre color) | +| **Button + Fader** | Feel — attenuate groove character (accents, timing, swing) | +| **Short press** | Reset to downbeat | +| **Long press** | Mute all MIDI and jack output (no fader move) | +| **Shift + short** | Reverse swing (delays onbeats instead) | +| **Shift + long** | Toggle jack Any ↔ Stacked | + +#### Faders & LEDs + +| Control | Edits | Visual feedback | +| --- | --- | --- | +| **Fader** | Density | **Top LED** = bar progress in app color. **Bottom LED** flashes on each hit | +| **Shift + Fader** | Genre | **Top/Bottom LEDs** = genre color split | +| **Button + Fader** | Feel | **Top LED** = Feel amount in **red** (brighter = more) | +| **Button LED** | — | app color while playing; white↔off on reverse; brief yellow/violet on jack-mode toggle; off when muted |`, + channels: [ + { + jackTitle: "Jack output", + jackDescription: + "Any mode: one velocity-weighted pulse per hit. Stacked mode: 1 V = kick, 2 V = snare, 4 V = hats.", + faderTitle: "Density", + faderDescription: + "Groove macro — raises the fader to reveal extra kick, snare and hat hits as quiet ghosts that get louder the further you go.", + faderPlusShiftTitle: "Genre", + faderPlusShiftDescription: + "Scrub across Dub → … → Dubstep pattern families (same order as the Groove param).", + faderPlusFnTitle: "Feel", + faderPlusFnDescription: + "Attenuates groove character: bottom = flat/quantized; top = full accents, microtiming, and genre swing (capped by Swing max %).", + fnTitle: "Reset / Mute", + fnDescription: + "Short press: jump to the downbeat. Long press: mute MIDI and jack (do not move the fader).", + fnPlusShiftTitle: "Reverse / Jack mode", + fnPlusShiftDescription: + "Short: reverse swing (delay onbeats). Long: toggle jack Any ↔ Stacked.", + ledTop: "Bar position (app color)", + ledTopPlusShift: "Genre color", + ledTopPlusFn: "Feel amount (red, brighter = more)", + ledBottom: "Flash on each hit", + }, + ], + }, ]; export const ManualTab = () => { diff --git a/configurator/src/components/manual/Apps.tsx b/configurator/src/components/manual/Apps.tsx index 655f11d05..7fec120dd 100644 --- a/configurator/src/components/manual/Apps.tsx +++ b/configurator/src/components/manual/Apps.tsx @@ -68,7 +68,8 @@ export const Apps = ({ apps }: Props) => ( button 2 mutes Output B)
+
{children}
+ ), + h4: ({ children }: { children?: ReactNode }) => ( +{children}
+ ),
+ ul: ({ children }: { children?: ReactNode }) => (
+ |
+ |
+ ))}
+
|---|
|
+ |
+ ))}
+
- {children}
-
+ Manual. Genre MIDI drums on the global clock (9 shared genres with Chord Vamp, including Jungle). Density reveals hits; Shift+Fader scrubs genre on the shared red→blue spectrum — button hue follows the latched genre (meters show live fader until pickup) while neighboring genres morph continuously; Button+Fader = Swing (capped by Swing max %). Shift+Tap plays a dedicated genre fill (or break at high density) that resolves on the next downbeat — Techno machine-guns, Hip-Hop and Jungle roll the snare, Dub and Dubstep answer sparsely — with velocity crescendoing toward the bar line, independent of the Density fader. Shift+Hold escalates into a drum solo that plays a four-bar phrase rather than looping the fill: an open build, a busier bar, a peak, then a turnaround that drops back to the sparse end-weighted figure and breathes. Each solo bar carries its own accent mask — the weight sits on the one and the backbeat, Feel only adds human jitter around it instead of randomising the dynamics — and every figure keeps rests and trades kick against snare rather than stacking them. Feel also opens ghost drags on the step right after a hit, so the fill-ins read as flams off the figure. Genre character stays intact: Techno rolls 16th kick bursts against clap stabs, Jungle chops the amen skeleton (kick on 1 and 3e, snare answering on the e), Dubstep keeps its half-time anchor, Dub and Trip-Hop stay wide open. Letting go drops straight into the end-weighted fill for the rest of the bar, so the solo always lands on the one. Releasing keeps either gesture running to the next downbeat so it always resolves. Above roughly 60 % density the same gesture reads subtractively and plays a break instead — the floor drops out (Techno keeps the kick, House the hats, Dub goes near-silent) and a late snare pickup hands the groove back on the one. Each genre carries three fill, three break, and three solo figures; low Feel locks to one, higher Feel rolls between them (same Die mix as Bassment). The button shows the gesture: bright white while filling, dim white while breaking. Watch the button: Main density ramps genre hue from half→full saturation with bar lightness; hits dim by voice (kick −50%, snare −30%, hats −10%) — not a clock blink. MIDI fires outside the clock subscriber (pending_* isolate). Third swing blends White→app→Red. Long resets to the downbeat. Four optional voices sit on top of the three written ones and cost no pattern data — each is derived from a hit the groove already plays, and each stays silent until you give it a note (note 0 = off, which is how they ship). Clap layers under the accented snare the way house and disco stack the two. Open Hat takes over an accented hat off the beat, muting the closed hat on that step rather than smearing both transients. The toms only appear inside a fill, break or solo, and only in the back half of the bar, where they lift the snare figure into the descending run that makes a fill read as a fill — High Tom through the third beat, Low Tom to land it. With all four off, Grooves plays exactly as it did before they existed. All seven voices share the base MIDI Ch unless Ch Map is non-zero; Ch Map packs one channel per voice into seven nibbles (voice order Kick, Snare, Hats, Open Hat, Low Tom, High Tom, Clap; nibble + 1 = channel), so drum machines that expect a channel per voice can be addressed without spending a param each. Swing max % carries the direction in its sign — negative swings the offbeats early. Jack folds the old Jack, Jack Mode and CV Dest into one enum, since a CV In destination and a CV Out level shape were never live at the same time. CV In modulates Density/Feel or triggers Reset; CV Att scales input.
+ {
accessor(&*guard)
}
+ /// Change a param from inside the app and persist it to FRAM.
+ ///
+ /// Same call shape as the full AppParams writeback (#640), but without the
+ /// host push: the configurator stays stale until that lands. Apps can call
+ /// this now; swapping in the real #640 implementation later is a drop-in.
+ #[allow(dead_code)]
+ pub async fn update