From 429c9ab8fc8710df4dc3aca6c2b4ec41f7da4615 Mon Sep 17 00:00:00 2001 From: Thiemo Tiziani Date: Sun, 18 Oct 2026 23:49:49 +0200 Subject: [PATCH] Patcher: arnoldasr's mods as extended_bpm, phasemeter, rgb_waveform Co-Authored-By: Claude Opus 5.5 --- README.md | 10 +++++----- cdj900nxs_patcher.py | 13 ++++++------- test_patcher.py | 16 ++++++++-------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 781b73d..7ea5557 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ python cdj900nxs_patcher.py --list In the list: **arrow keys** move, **space** ticks or unticks, **a** ticks all or none, **enter** builds, **q** quits. Ticking a mod ticks what it needs; -unticking one unticks what needs it (`utility` needs `code`, `hotcuekeys` -needs `cuecolours`). A mod with known faults starts unticked. +unticking one unticks what needs it (`hotcuekeys` needs `cuecolours`, +`beatjump` needs `hotcuekeys`). A mod with known faults starts unticked. Before anything is built the patcher shows a disclaimer that must be accepted by typing `yes` (or with `--accept`, where no one can type): the @@ -54,9 +54,9 @@ From the emulator checkout `../CDJ900NXSVirtual` (`--repo` for another place): | mod | what it does | |---|---| -| `code` | arnoldasr: BPM with two decimals, two-line phase meter, RGB waveform | -| `utility` | arnoldasr: UTILITY > PHASE METER switch (needs `code`) | -| `overview` | arnoldasr: overview waveform colour gradient | +| `extended_bpm` | arnoldasr: BPM with two decimals (tenths as a small digit above) | +| `phasemeter` | arnoldasr: two-line phase meter, chosen in UTILITY > PHASE METER (was SLIP FLASHING) | +| `rgb_waveform` | arnoldasr: big waveform in RGB colours, overview as a red-to-white gradient | | `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 | | `hotcuekeys` | hot cues A-C on the BEAT DIVIDE keys (needs `cuecolours`), see below | diff --git a/cdj900nxs_patcher.py b/cdj900nxs_patcher.py index d97429a..5d12fb3 100644 --- a/cdj900nxs_patcher.py +++ b/cdj900nxs_patcher.py @@ -26,7 +26,7 @@ choose, as a new C900NXS.UPD for the player's own USB update. The mods come from the CDJ900NXSVirtual checkout next to this folder (--repo): mods/patch_cdj900nxs.py, which also needs arnoldasr's checkout in -mods/cdj900nxs/upstream for his mods (code, utility, overview). +mods/cdj900nxs/upstream for his mods (extended_bpm, phasemeter, rgb_waveform). """ import argparse import os @@ -38,9 +38,8 @@ sys.path.insert(0, HERE) import upd # noqa: E402 DEFAULT_REPO = os.path.join(os.path.dirname(HERE), 'CDJ900NXSVirtual') -UPSTREAM_MODS = ('code', 'utility', 'overview') -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 +UPSTREAM_MODS = ('extended_bpm', 'phasemeter', 'rgb_waveform') +NEEDS = {'hotcuekeys': 'cuecolours', # the hot cues the keys play are read by cuecolours 'beatjump': 'hotcuekeys'} # hotcuekeys' engine makes the jumps LABEL = '1.32' # not ticked at the start of the list, and why @@ -148,7 +147,7 @@ def choose(mods): for i, (name, what, ok, why) in enumerate(mods): mark = '[x]' if name in chosen else ('[ ]' if ok else ' - ') cursor = '>' if i == at else ' ' - text = '%s %s %-11s %s' % (cursor, mark, name, what if ok else why) + text = '%s %s %-13s %s' % (cursor, mark, name, what if ok else why) sys.stdout.write('\x1b[2K' + ('\x1b[7m%s\x1b[0m' % text if i == at else text)[:200] + '\n') need = ', '.join('%s needs %s' % kv for kv in NEEDS.items()) sys.stdout.write('\x1b[2K (%s)\n\x1b[2K\n' % need) @@ -181,7 +180,7 @@ def choose_by_number(mods): print('\n Mods (all usable ones without known faults are ticked):') for i, (name, what, ok, why) in enumerate(mods, 1): mark = '[x]' if name in chosen else ('[ ]' if ok else ' - ') - print(' %d %s %-11s %s' % (i, mark, name, what if ok else why)) + print(' %d %s %-13s %s' % (i, mark, name, what if ok else why)) answer = input(' Numbers to tick/untick (e.g. 1 3), enter to go on, q to quit: ').strip() if not answer: break @@ -264,7 +263,7 @@ def main(argv=None): mods = available(patcher, args.repo) if args.list: for name, what, ok, why in mods: - print('%-11s %s%s' % (name, what, '' if ok else ' (%s)' % why)) + print('%-13s %s%s' % (name, what, '' if ok else ' (%s)' % why)) return 0 if not args.stock: ap.error('give the official C900NXS.UPD') diff --git a/test_patcher.py b/test_patcher.py index ecdb67d..ed6ea44 100644 --- a/test_patcher.py +++ b/test_patcher.py @@ -95,20 +95,20 @@ def test_boot_area_tampering_caught(): upd.verify(bad, stock, stock.application) -MODS = [('code', '', True, ''), ('utility', '', True, ''), ('overview', '', True, ''), +MODS = [('extended_bpm', '', True, ''), ('phasemeter', '', True, ''), ('rgb_waveform', '', True, ''), ('beatjump', '', True, ''), ('cuecolours', '', True, ''), ('hotcuekeys', '', True, '')] def test_every_mod_can_be_unticked(): chosen = {m[0] for m in MODS} - cli._toggle(chosen, 'code', MODS) - assert 'code' not in chosen and 'utility' not in chosen # what needs it goes with it + cli._toggle(chosen, 'extended_bpm', MODS) + assert 'extended_bpm' not in chosen and 'phasemeter' in chosen # each on its own cli._toggle(chosen, 'cuecolours', MODS) assert 'cuecolours' not in chosen and 'hotcuekeys' not in chosen cli._toggle(chosen, 'hotcuekeys', MODS) assert {'hotcuekeys', 'cuecolours'} <= chosen # and ticking brings what it needs - cli._toggle(chosen, 'overview', MODS) - assert 'overview' not in chosen + cli._toggle(chosen, 'rgb_waveform', MODS) + assert 'rgb_waveform' not in chosen cli._toggle(chosen, 'cuecolours', MODS) assert not {'cuecolours', 'hotcuekeys', 'beatjump'} & chosen # and what needs that cli._toggle(chosen, 'beatjump', MODS) @@ -133,9 +133,9 @@ def test_disclaimer_without_a_terminal(monkeypatch): 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'] + mods = cli.available(types.SimpleNamespace(MODS={"extended_bpm": "c", "credit": "x"}), cli.DEFAULT_REPO) + assert [m[0] for m in mods] == ['extended_bpm', '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'} + assert chosen == {'extended_bpm'}