Patcher: ledsaver and credit mods, beat jump needs the hot cue engine

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Thiemo TizianiandClaude Opus 5.5 committed 2026-10-18 23:29:42 +02:00
1 parent bbe784aad2
commit 7dbb4ec6f8
3 files changed
+68 -14

No files matched your search

+42 -9
View File
@@ -57,14 +57,17 @@ From the emulator checkout `../CDJ900NXSVirtual` (`--repo` for another place):
| `code` | arnoldasr: BPM with two decimals, two-line phase meter, RGB waveform | | `code` | arnoldasr: BPM with two decimals, two-line phase meter, RGB waveform |
| `utility` | arnoldasr: UTILITY > PHASE METER switch (needs `code`) | | `utility` | arnoldasr: UTILITY > PHASE METER switch (needs `code`) |
| `overview` | arnoldasr: overview waveform colour gradient | | `overview` | arnoldasr: overview waveform colour gradient |
| `beatjump` | TRACK SEARCH jumps 1/2/4/8 beats, UTILITY > BEAT JUMP (known faults on a player, see Risk) | | `beatjump` | TRACK SEARCH jumps 1/2/4/8 beats, UTILITY > BEAT JUMP (needs `hotcuekeys`), see below |
| `cuecolours` | memory cues in their rekordbox colours, hot cues shown | | `cuecolours` | memory cues in their rekordbox colours, hot cues shown |
| `hotcuekeys` | hot cues A-E on the BEAT DIVIDE keys (needs `cuecolours`), see below | | `hotcuekeys` | hot cues A-C on the BEAT DIVIDE keys (needs `cuecolours`), see below |
| `ledsaver` | UTILITY > LED ANIMATION: while the screen saver dims the display, the button LEDs and the jog's rings play a loop |
| `credit` | the boot screen says "Pionaarr Mods by Givou" where it said "Ver 1.31" (ticked at the start, can be unticked) |
`hotcuekeys`: UTILITY > HOT CUE KEYS ON; then QUANTIZE pressed briefly `hotcuekeys`: UTILITY > HOT CUE KEYS ON; then QUANTIZE pressed briefly
switches the hot key mode (HOT KEYS box beside QUANTIZE), held it is switches the hot key mode (HOT KEYS box beside QUANTIZE), held it is
QUANTIZE. In the mode the BEAT DIVIDE keys 3/4, 1/3, 1/2, 1/4, 1/8 are hot QUANTIZE. In the mode the BEAT DIVIDE keys 3/4, 1/3, 1/2 are hot cues A-C
cues A-E: (1/4 and 1/8 do nothing and stay dark: the player has three hot cue buffers,
one for each):
- press: jump to the hot cue and play (from pause too); - press: jump to the hot cue and play (from pause too);
- hold 0.5 s or longer, then let go: back to the hot cue, standing (gate); - hold 0.5 s or longer, then let go: back to the hot cue, standing (gate);
@@ -72,8 +75,36 @@ cues A-E:
- DELETE held, a key pressed: its hot cue deleted (DELETE alone is DELETE); - DELETE held, a key pressed: its hot cue deleted (DELETE alone is DELETE);
- LEDs: off without a hot cue, dim with one, two bright blinks when pressed. - LEDs: off without a hot cue, dim with one, two bright blinks when pressed.
Hot cues set or deleted live until the next track load; nothing is written Hot cues set or deleted are written to the USB stick (the track's rekordbox
to the USB stick. As with CALL, the CUE point moves to the hot cue. analysis files, as MEMORY and DELETE keep memory cues), so they are there
after the next load too. As with MEMORY, the player takes this only once the
track has settled (a few seconds after loading). Hot cues A-C start at once: once a track is loaded and the
deck stands at its CUE point for a second (in hot key mode), they are loaded
into the player's three hot cue buffers, silently, and the deck goes back to
its CUE point. PLAY or CUE pressed meanwhile wait for it (PLAY is played
after). A hot cue set later loads into
its own buffer on its first press (about a second). A hot cue played from a buffer
leaves the CUE point where it is; one recalled without a buffer moves it, as
CALL does.
`ledsaver`: UTILITY > LED ANIMATION, ON from the first start (set it to OFF
to switch it off; the player keeps the choice). When the
player's own screen saver dims the display (5 minutes without a track, or
100 minutes paused), the BEAT DIVIDE keys fade in a wave down and back up,
SYNC / MASTER and MASTER TEMPO / DISC EJECT fade in and out, CUE, PLAY, REV,
SLIP, the SLIP LOOP bar and RELOOP/EXIT light one after another, and the
jog's three rings light in turn from the inside out and back in. Any key ends
it, as it ends the screen saver.
`beatjump`: UTILITY > BEAT JUMP (where SCREEN SAVER was) sets 1, 2, 4 or 8
beats (OFF: TRACK SEARCH as before); TRACK SEARCH back and forward then jump
that many beats along the track's beat grid, keeping the position within the
beat. While playing, the jump is loaded into a buffer of the player's own
that only beat jumps use (A-C keep theirs): there is a short silence while it loads, but the
deck comes back in phase, as if it had played on through it (the load time is
measured on every jump and allowed for), and the CUE point stays. Paused, it
is a CALL (the CUE point moves with it). A press while a jump or hot cue is
still loading is ignored.
arnoldasr's mods need his repository checked out in arnoldasr's mods need his repository checked out in
`../CDJ900NXSVirtual/mods/cdj900nxs/upstream` (it has no licence, so it is `../CDJ900NXSVirtual/mods/cdj900nxs/upstream` (it has no licence, so it is
@@ -83,9 +114,11 @@ not copied here); without it they are greyed out.
Nothing here talks to a player; you copy the file to a FAT32 USB stick and run Nothing here talks to a player; you copy the file to a FAT32 USB stick and run
the update yourself. Our mods were developed with Claude Code in an emulator. the update yourself. Our mods were developed with Claude Code in an emulator.
On a player: `beatjump` jumped off the grid and, after a few jumps, reported a An earlier `beatjump` jumped off the grid on a player and, after a few quick
corrupted file and needed a restart; it is being reworked. `cuecolours` and jumps, reported a corrupted file and needed a restart; it has been rebuilt on
earlier versions of `hotcuekeys` ran on a player. Recovery from an the hot cue engine (no overlapping loads), which has not run on a player yet,
nor has the hot cue engine. A jump while a loop plays ends the loop. `cuecolours` and earlier versions of `hotcuekeys`
ran on a player. Recovery from an
application that crashes at start-up has not been tested by anyone. Keep the application that crashes at start-up has not been tested by anyone. Keep the
`--stock` file at hand. `--stock` file at hand.
+12 -5
View File
@@ -40,10 +40,11 @@ import upd # noqa: E402
DEFAULT_REPO = os.path.join(os.path.dirname(HERE), 'CDJ900NXSVirtual') DEFAULT_REPO = os.path.join(os.path.dirname(HERE), 'CDJ900NXSVirtual')
UPSTREAM_MODS = ('code', 'utility', 'overview') UPSTREAM_MODS = ('code', 'utility', 'overview')
NEEDS = {'utility': 'code', # the PHASE METER switch chooses the code mod's meter NEEDS = {'utility': 'code', # the PHASE METER switch chooses the code mod's meter
'hotcuekeys': 'cuecolours'} # the hot cues the keys play are read by cuecolours 'hotcuekeys': 'cuecolours', # the hot cues the keys play are read by cuecolours
'beatjump': 'hotcuekeys'} # hotcuekeys' engine makes the jumps
LABEL = '1.32' LABEL = '1.32'
# not ticked at the start of the list, and why # not ticked at the start of the list, and why
KNOWN_FAULTS = {'beatjump': 'known faults on a player (jumps off the grid, "file corrupted" after a few jumps)'} KNOWN_FAULTS = {}
def load_repo(repo): def load_repo(repo):
@@ -114,13 +115,19 @@ def _toggle(chosen, name, mods):
"""Tick or untick one mod: ticking one ticks what it needs, unticking """Tick or untick one mod: ticking one ticks what it needs, unticking
one unticks what needs it (so every mod can be unticked).""" one unticks what needs it (so every mod can be unticked)."""
if name in chosen: if name in chosen:
chosen.discard(name) drop = {name}
chosen.difference_update(n for n, need in NEEDS.items() if need == name) while True:
more = {n for n, need in NEEDS.items() if need in drop} - drop
if not more:
break
drop |= more
chosen.difference_update(drop)
else: else:
chosen.add(name) chosen.add(name)
need = NEEDS.get(name) need = NEEDS.get(name)
if need and any(m[0] == need and m[2] for m in mods): while need and any(m[0] == need and m[2] for m in mods):
chosen.add(need) chosen.add(need)
need = NEEDS.get(need)
def choose(mods): def choose(mods):
+14
View File
@@ -109,6 +109,10 @@ def test_every_mod_can_be_unticked():
assert {'hotcuekeys', 'cuecolours'} <= chosen # and ticking brings what it needs assert {'hotcuekeys', 'cuecolours'} <= chosen # and ticking brings what it needs
cli._toggle(chosen, 'overview', MODS) cli._toggle(chosen, 'overview', MODS)
assert 'overview' not in chosen assert 'overview' not in chosen
cli._toggle(chosen, 'cuecolours', MODS)
assert not {'cuecolours', 'hotcuekeys', 'beatjump'} & chosen # and what needs that
cli._toggle(chosen, 'beatjump', MODS)
assert {'beatjump', 'hotcuekeys', 'cuecolours'} <= chosen
def test_disclaimer_must_be_accepted(monkeypatch): def test_disclaimer_must_be_accepted(monkeypatch):
@@ -125,3 +129,13 @@ def test_disclaimer_without_a_terminal(monkeypatch):
monkeypatch.setattr(cli.sys.stdin, 'isatty', lambda: False, raising=False) monkeypatch.setattr(cli.sys.stdin, 'isatty', lambda: False, raising=False)
with pytest.raises(SystemExit, match='--accept'): with pytest.raises(SystemExit, match='--accept'):
cli.confirm(False) cli.confirm(False)
def test_credit_can_be_unticked():
import types
mods = cli.available(types.SimpleNamespace(MODS={"code": "c", "credit": "x"}), cli.DEFAULT_REPO)
assert [m[0] for m in mods] == ['code', 'credit']
chosen = {m[0] for m in mods if m[0] not in cli.KNOWN_FAULTS}
assert 'credit' in chosen # ticked at the start
cli._toggle(chosen, 'credit', mods)
assert chosen == {'code'}