Skip to content

(feat): add Bassment multi-genre bassline app - #633

Draft
kosmar wants to merge 24 commits into
ATOVproject:mainfrom
kosmar:feat/bassment
Draft

(feat): add Bassment multi-genre bassline app#633
kosmar wants to merge 24 commits into
ATOVproject:mainfrom
kosmar:feat/bassment

Conversation

@kosmar

@kosmar kosmar commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add Bassment (app ID 41): multi-genre monophonic basslines with bassist Voice personas
  • Bring minimal shared helpers needed to compile on main (genre_palette, groove, led_fx) and register the app

Test plan

  • Flash build from this branch on hardware
  • Load Bassment on a channel; confirm it appears in the app library as “Bassment”
  • With clock running: Density / Feel / Groove produce bass notes (MIDI + CV Out)
  • Shift+fader scrubs genre axis (LED spectrum); button Voice cycle / mute behave as expected
  • Scene save/load restores Feel, Density, Voice, mute

Made with Cursor

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 — these APIs do not exist on current main:

This branch currently carries its own inline copy of the code in #638, #639, #640, #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.

Monophonic basslines on the shared genre axis with bassist Voice
personas; includes minimal shared genre/groove/LED helpers for main.

Authored by an AI coding agent on behalf of kosmar.

Co-authored-by: Cursor <cursoragent@cursor.com>
@kosmar
kosmar marked this pull request as draft August 8, 2026 21:00
kosmar and others added 5 commits August 9, 2026 00:39
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>
kosmar and others added 3 commits August 10, 2026 14:06
kosmar and others added 15 commits August 10, 2026 14:15
Co-authored-by: Cursor <cursoragent@cursor.com>
Authored by an AI coding agent on behalf of kosmar.

Co-authored-by: Cursor <cursoragent@cursor.com>
Grooves, Chord Vamp and Bassment all render the genre axis with the
continuous red→blue spectrum from led_fx, so the discrete per-genre chrome
had no callers left.

Authored by an AI coding agent on behalf of kosmar.

Co-authored-by: Cursor <cursoragent@cursor.com>
Shift+Tap walks a bass fill into the next bar's chord with a leading tone on
the last hit; the Voice picks the contour (walk, arp, octave push, pedal)
and high Density turns the gesture into a quiet pedal break. Shift+Hold
lifts the register into a solo that re-rolls each bar and resolves on the
next downbeat. Voice cycling moves to Long so Shift stays free; Previous
Voice and the new Swing Dir enum live in the Configurator.

Authored by an AI coding agent on behalf of kosmar.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ndow

A constant octave lift read as a wrong octave rather than a solo, so the
lift now follows an arc: the phrase starts on a low anchor, peaks around
60 % of the bar and comes back down to land, with answer bars sitting lower
for call and response. Peak height follows the Voice's octave span. Notes
outside the playable window fold by octaves instead of clamping, which had
collapsed every out-of-range note onto the same repeated pitch.

Authored by an AI coding agent on behalf of kosmar.

Co-authored-by: Cursor <cursoragent@cursor.com>
… sign

Jack and CV Dest spent two params on a choice that was really one: a CV In
destination is dead while the jack drives CV Out, and vice versa. They become a
single enum — CV Out, CV In Density, CV In Feel, CV In Reset — matching Grooves.
Swing Dir folds into the sign of Groove max %, where a negative cap swings the
offbeats early; only the magnitude still caps the Feel amount.

The first twelve slots kept their types, so a stored blob folds forward on its
own instead of resetting. Scene storage drops its vestigial `reversed` flag,
which was already being read into a discard.

Authored by an AI coding agent on behalf of kosmar.

Co-authored-by: Cursor <cursoragent@cursor.com>
Note-generating apps each carried their own idea of Root and Scale, so the
device-wide Key/Tonic — live on the Scene button plus Fader 4/5 — could not act
as a transpose. This adds one shared place to resolve it: the normalized device
Key, the tonic pitch class, a root retuned onto it keeping its octave, and a
combined call that costs a single GlobalConfig copy instead of two.

Resolving copies the whole GlobalConfig, so the module documents that callers
must cache per bar or phrase rather than per note.

Authored by an AI coding agent on behalf of kosmar.

Co-authored-by: Cursor <cursoragent@cursor.com>
Density behaved like Grooves' polyphonic model on a monophonic instrument, so
raising it added noise instead of groove. Removes the reveal pass that filled
deliberately empty steps, stops syncopation bias from inflating density and
lets it colour ghost notes instead, keeps core hits instead of dropping them
for air, and scales the Voice personas by Feel rather than driving them all
toward the same busy ceiling. Chromatic approaches are limited to passing
notes, microtiming is capped at two ticks so the bass sits near the kick, and
the genre fill masks are sparser.

Follow device tonic (default on) makes the global Tonic fader a live transpose:
the pitch class comes from the device, the octave stays with Root, resolved
once per bar so a new key lands on a bar line. Follow device scale defaults off
because Scale carries the genre character.

Authored by an AI coding agent on behalf of kosmar.

Co-authored-by: Cursor <cursoragent@cursor.com>
clippy::chunks_exact_to_as_chunks fires on the constant chunk size in the USB RX
loop, and CI runs clippy with -D warnings, so main does not pass its own gate on
a current toolchain. Feature branches have been carrying this same one-line fix
inside unrelated app commits to stay green; landing it here means they no longer
have to.

as_chunks::<4>() yields &[[u8; 4]], so the packet indexing below is unchanged.
The discarded remainder matches the previous behaviour: chunks_exact dropped a
trailing partial packet too, and a USB MIDI bulk transfer is always a multiple
of four bytes.

Authored by an AI coding agent on behalf of kosmar.

Co-authored-by: Cursor <cursoragent@cursor.com>
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 NoteOffs
here run in the async voice task, where awaiting queue space costs at
most a late NoteOff in this app's own loop.

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>
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant