@cordfuse/nux-qr-tool 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +280 -0
- package/dist/qr-generator.js +5 -3
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# @cordfuse/nux-qr-tool
|
|
2
|
+
|
|
3
|
+
NUX MightyAmp QR preset encoder — generates decorated QR PNG cards from preset JSON.
|
|
4
|
+
|
|
5
|
+
Takes a JSON file describing a NUX MightyAmp tone preset (amp model, effects chain, device target) and outputs a dark-themed PNG card containing a scannable QR code. Works as both a CLI tool (via `npx`) and a Node.js library.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@cordfuse/nux-qr-tool)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Used by
|
|
13
|
+
|
|
14
|
+
| Repo | How it's used |
|
|
15
|
+
|---|---|
|
|
16
|
+
| [cordfuse/toneai-nux-imprint](https://github.com/cordfuse/toneai-nux-imprint) | Agent invokes `npx @cordfuse/nux-qr-tool` to generate QR cards mid-conversation |
|
|
17
|
+
| [cordfuse/toneai-nux-cli](https://github.com/cordfuse/toneai-nux-cli) | Imports `decorateQR` as a library to decorate QR PNGs inside the compiled binary |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
**Run without installing (recommended for agent use):**
|
|
24
|
+
```bash
|
|
25
|
+
npx @cordfuse/nux-qr-tool preset.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Install globally:**
|
|
29
|
+
```bash
|
|
30
|
+
npm install -g @cordfuse/nux-qr-tool
|
|
31
|
+
nux-qr-tool preset.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Install as a library:**
|
|
35
|
+
```bash
|
|
36
|
+
npm install @cordfuse/nux-qr-tool
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## CLI Usage
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx @cordfuse/nux-qr-tool <preset-json-file> [--output <dir>]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Reads the preset JSON, encodes the NUX QR payload, generates a decorated PNG, writes it to `<dir>/<artist>-<song>.png`, and prints the full output path to stdout.
|
|
48
|
+
|
|
49
|
+
The output directory defaults to the current working directory. Use `--output` (or `-o`) to write elsewhere.
|
|
50
|
+
|
|
51
|
+
### Example
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
cat > preset.json << 'EOF'
|
|
55
|
+
{
|
|
56
|
+
"artist": "Led Zeppelin",
|
|
57
|
+
"song": "Whole Lotta Love",
|
|
58
|
+
"device": "plugpro",
|
|
59
|
+
"preset_name": "Page Sunburst",
|
|
60
|
+
"preset_name_short": "Page SB",
|
|
61
|
+
"amp": { "id": 3, "gain": 72, "master": 75, "bass": 55, "mid": 50, "treble": 48 },
|
|
62
|
+
"cabinet": { "id": 5, "level_db": 0, "low_cut_hz": 80, "high_cut": 50 },
|
|
63
|
+
"noise_gate": { "enabled": true, "sensitivity": 35, "decay": 50 },
|
|
64
|
+
"efx": { "id": 7, "enabled": true, "p1": 60, "p2": 50, "p3": 55 },
|
|
65
|
+
"reverb": { "id": 1, "enabled": true, "p1": 20, "p2": 40 },
|
|
66
|
+
"master_db": 0
|
|
67
|
+
}
|
|
68
|
+
EOF
|
|
69
|
+
|
|
70
|
+
npx @cordfuse/nux-qr-tool preset.json
|
|
71
|
+
# → /current/working/dir/led-zeppelin-whole-lotta-love.png
|
|
72
|
+
|
|
73
|
+
npx @cordfuse/nux-qr-tool preset.json --output ./cards
|
|
74
|
+
# → ./cards/led-zeppelin-whole-lotta-love.png
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The output directory is created automatically if it doesn't exist.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Output Card
|
|
82
|
+
|
|
83
|
+
Each PNG is a 548×620px decorated card:
|
|
84
|
+
|
|
85
|
+
- **Header:** app name (top-left) and version (top-right) in red/grey on a dark background
|
|
86
|
+
- **QR code:** 500×500px, high error correction (level H), white on black
|
|
87
|
+
- **Footer:** artist and song title (bold), device name and embedded-name indicator
|
|
88
|
+
|
|
89
|
+
Pro format devices embed the preset name directly into the QR payload. The footer notes this with `· name embedded in QR`.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Library Usage
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { decorateQR } from '@cordfuse/nux-qr-tool'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `decorateQR(qrPng, artist, song, deviceId, deviceName, options?)`
|
|
100
|
+
|
|
101
|
+
Takes a raw QR PNG buffer and adds the dark card decoration around it.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import QRCode from 'qrcode'
|
|
105
|
+
import { decorateQR } from '@cordfuse/nux-qr-tool'
|
|
106
|
+
|
|
107
|
+
const qrBuffer = await QRCode.toBuffer('nux://MightyAmp:...', {
|
|
108
|
+
errorCorrectionLevel: 'H',
|
|
109
|
+
width: 500,
|
|
110
|
+
margin: 4,
|
|
111
|
+
color: { dark: '#000000', light: '#ffffff' },
|
|
112
|
+
}) as Buffer
|
|
113
|
+
|
|
114
|
+
const decorated = await decorateQR(
|
|
115
|
+
qrBuffer,
|
|
116
|
+
'Led Zeppelin',
|
|
117
|
+
'Whole Lotta Love',
|
|
118
|
+
'plugpro',
|
|
119
|
+
'Mighty Plug Pro',
|
|
120
|
+
{ appName: 'my-app', appVersion: '1.0.0' } // optional — defaults to 'ToneAI' + package version
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
fs.writeFileSync('output.png', decorated)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### Parameters
|
|
127
|
+
|
|
128
|
+
| Param | Type | Description |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| `qrPng` | `Buffer` | Raw QR code PNG at 500×500px |
|
|
131
|
+
| `artist` | `string` | Artist name — shown in footer |
|
|
132
|
+
| `song` | `string` | Song title — shown in footer |
|
|
133
|
+
| `deviceId` | `string` | NUX device ID (e.g. `plugpro`) — determines footer note |
|
|
134
|
+
| `deviceName` | `string` | Human-readable device name — shown in footer |
|
|
135
|
+
| `options.appName` | `string` | App name in header (default: `'ToneAI'`) |
|
|
136
|
+
| `options.appVersion` | `string` | Version in header (default: package version) |
|
|
137
|
+
|
|
138
|
+
Returns `Promise<Buffer>` — the decorated PNG as a Buffer.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Preset JSON Format
|
|
143
|
+
|
|
144
|
+
The CLI input JSON must include at minimum `artist`, `song`, `device`, and `amp`. All other fields are optional and default to off/zero.
|
|
145
|
+
|
|
146
|
+
### Top-level fields
|
|
147
|
+
|
|
148
|
+
| Field | Type | Required | Description |
|
|
149
|
+
|---|---|---|---|
|
|
150
|
+
| `artist` | `string` | yes | Artist name — used for output filename and card footer |
|
|
151
|
+
| `song` | `string` | yes | Song title — used for output filename and card footer |
|
|
152
|
+
| `device` | `string` | yes | Target NUX device ID (see Devices table below) |
|
|
153
|
+
| `preset_name` | `string` | yes | Full preset name |
|
|
154
|
+
| `preset_name_short` | `string` | no | Short name for Pro QR payload (max 15 chars) — falls back to `preset_name` |
|
|
155
|
+
| `amp` | `AmpParams` | yes | Amp model and EQ settings |
|
|
156
|
+
| `cabinet` | `CabinetParams` | no | Cabinet IR settings (Pro and most Standard devices) |
|
|
157
|
+
| `noise_gate` | `NoiseGateParams` | yes | Noise gate settings |
|
|
158
|
+
| `efx` | `EffectParams` | no | EFX slot (drive/wah effects) |
|
|
159
|
+
| `compressor` | `EffectParams` | no | Compressor (Pro format only) |
|
|
160
|
+
| `modulation` | `EffectParams` | no | Modulation effect |
|
|
161
|
+
| `delay` | `EffectParams` | no | Delay effect |
|
|
162
|
+
| `reverb` | `EffectParams` | no | Reverb effect |
|
|
163
|
+
| `eq` | `EQParams` | no | EQ (Pro format only) |
|
|
164
|
+
| `wah` | `WahParams` | no | Wah pedal (2040bt/40bt only) |
|
|
165
|
+
| `master_db` | `number` | yes | Master volume offset in dB (range: −12 to +12) |
|
|
166
|
+
|
|
167
|
+
### AmpParams
|
|
168
|
+
|
|
169
|
+
| Field | Type | Range | Description |
|
|
170
|
+
|---|---|---|---|
|
|
171
|
+
| `id` | `number` | device-specific | Amp model ID (nux index, 0- or 1-indexed depending on device) |
|
|
172
|
+
| `gain` | `number` | 0–100 | Gain |
|
|
173
|
+
| `master` | `number` | 0–100 | Master volume |
|
|
174
|
+
| `bass` | `number` | 0–100 | Bass EQ |
|
|
175
|
+
| `mid` | `number` | 0–100 | Mid EQ |
|
|
176
|
+
| `treble` | `number` | 0–100 | Treble EQ |
|
|
177
|
+
| `param6` | `number` | 0–100 | Amp-specific 6th parameter (presence, resonance, etc.) |
|
|
178
|
+
| `param7` | `number` | 0–100 | Amp-specific 7th parameter (rare) |
|
|
179
|
+
|
|
180
|
+
### CabinetParams
|
|
181
|
+
|
|
182
|
+
| Field | Type | Range | Description |
|
|
183
|
+
|---|---|---|---|
|
|
184
|
+
| `id` | `number` | device-specific | Cabinet IR model ID |
|
|
185
|
+
| `level_db` | `number` | −12 to +12 | Cabinet output level in dB |
|
|
186
|
+
| `low_cut_hz` | `number` | 20–300 | Low cut frequency |
|
|
187
|
+
| `high_cut` | `number` | 0–100 | High cut amount |
|
|
188
|
+
|
|
189
|
+
### NoiseGateParams
|
|
190
|
+
|
|
191
|
+
| Field | Type | Range | Description |
|
|
192
|
+
|---|---|---|---|
|
|
193
|
+
| `enabled` | `boolean` | — | Whether noise gate is active |
|
|
194
|
+
| `sensitivity` | `number` | 0–100 | Gate sensitivity (threshold) |
|
|
195
|
+
| `decay` | `number` | 0–100 | Gate decay/release |
|
|
196
|
+
|
|
197
|
+
### EffectParams (efx, compressor, modulation, delay, reverb)
|
|
198
|
+
|
|
199
|
+
| Field | Type | Range | Description |
|
|
200
|
+
|---|---|---|---|
|
|
201
|
+
| `id` | `number` | device-specific | Effect model ID (nux index) |
|
|
202
|
+
| `enabled` | `boolean` | — | Whether effect is active |
|
|
203
|
+
| `p1` | `number` | 0–100 | Parameter 1 |
|
|
204
|
+
| `p2` | `number` | 0–100 | Parameter 2 |
|
|
205
|
+
| `p3` | `number` | 0–100 | Parameter 3 (effect-dependent) |
|
|
206
|
+
|
|
207
|
+
### EQParams (Pro format only)
|
|
208
|
+
|
|
209
|
+
| Field | Type | Description |
|
|
210
|
+
|---|---|---|
|
|
211
|
+
| `id` | `number` | `1` = 6-Band, `3` = 10-Band |
|
|
212
|
+
| `enabled` | `boolean` | Whether EQ is active |
|
|
213
|
+
| `bands` | `number[]` | Per-band dB values (−15 to +15). 6 values for 6-Band, 11 for 10-Band |
|
|
214
|
+
|
|
215
|
+
### WahParams (2040bt and 40bt only)
|
|
216
|
+
|
|
217
|
+
| Field | Type | Range | Description |
|
|
218
|
+
|---|---|---|---|
|
|
219
|
+
| `enabled` | `boolean` | — | Whether wah is active |
|
|
220
|
+
| `pedal` | `number` | 0–100 | Pedal position |
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Devices
|
|
225
|
+
|
|
226
|
+
| ID | Device | Format | Notes |
|
|
227
|
+
|---|---|---|---|
|
|
228
|
+
| `plugpro` | Mighty Plug Pro | Pro (113 bytes) | Full chain, preset name in QR |
|
|
229
|
+
| `space` | Mighty Space | Pro (113 bytes) | Full chain, preset name in QR |
|
|
230
|
+
| `litemk2` | Mighty Lite MkII | Pro (113 bytes) | Full chain, preset name in QR |
|
|
231
|
+
| `8btmk2` | Mighty 8BT MkII | Pro (113 bytes) | Full chain, preset name in QR |
|
|
232
|
+
| `20btmk2` | Mighty 20BT MkII | Pro (113 bytes) | Full chain, preset name in QR |
|
|
233
|
+
| `40btmk2` | Mighty 40BT MkII | Pro (113 bytes) | Full chain, preset name in QR |
|
|
234
|
+
| `60btmk2` | Mighty 60BT MkII | Pro (113 bytes) | Full chain, preset name in QR |
|
|
235
|
+
| `plugair_v1` | Mighty Plug (v1) | Standard (40 bytes) | EFX slot, no preset name |
|
|
236
|
+
| `plugair_v2` | Mighty Plug (v2) | Standard (40 bytes) | EFX slot, no preset name |
|
|
237
|
+
| `mightyair_v1` | Mighty Air (v1) | Standard (40 bytes) | EFX slot, no preset name |
|
|
238
|
+
| `mightyair_v2` | Mighty Air (v2) | Standard (40 bytes) | EFX slot, no preset name |
|
|
239
|
+
| `mightygo` | Mighty Go | Standard (40 bytes) | EFX slot, no preset name |
|
|
240
|
+
| `lite` | Mighty Lite BT | Standard (40 bytes) | Single ambience slot (delay OR reverb) |
|
|
241
|
+
| `8bt` | Mighty 8BT | Standard (40 bytes) | Separate delay and reverb |
|
|
242
|
+
| `2040bt` | Mighty 20/40BT (original) | Standard (40 bytes) | Wah pedal, bass/mid/treble EQ |
|
|
243
|
+
| `40bt` | Mighty 40BT (original) | Standard (40 bytes) | Same format as 2040bt, separate QR ID |
|
|
244
|
+
|
|
245
|
+
### Pro vs Standard format
|
|
246
|
+
|
|
247
|
+
**Pro** devices use a 113-byte payload with the full effects chain (Compressor, EFX, Amp, EQ, Noise Gate, Modulation, Delay, Reverb, Cabinet) and embed the preset name in bytes 98–112.
|
|
248
|
+
|
|
249
|
+
**Standard** devices use a 40-byte device-specific payload. Amp and effect IDs are different from Pro devices and are 0-indexed. Cabinets and EQ are absent on Lite/8BT/2040BT. The Lite BT uses a single ambience slot shared between delay and reverb — reverb takes priority.
|
|
250
|
+
|
|
251
|
+
### QR string format
|
|
252
|
+
|
|
253
|
+
The encoded QR string has the form:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
nux://MightyAmp:<base64>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Where the base64 decodes to `[deviceQRId, deviceQRVersion, ...payload]`.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Requirements
|
|
264
|
+
|
|
265
|
+
- Node.js 18+ (for `npx` / global install)
|
|
266
|
+
- Or any runtime that can execute Node-compatible ESM (Bun, Deno with compat flag)
|
|
267
|
+
|
|
268
|
+
No external binaries required. Canvas rendering is handled by [`@napi-rs/canvas`](https://github.com/Brooooooklyn/canvas) — pre-built native binaries are downloaded automatically on install for Linux x64 (glibc and musl), macOS (arm64 and x64), and Windows x64.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## QR format reference
|
|
273
|
+
|
|
274
|
+
The NUX QR format was reverse-engineered from the open-source [mightier_amp](https://github.com/tuntorius/mightier_amp) Flutter app by [tuntorius](https://github.com/tuntorius). Key reference files: `NuxConstants.dart` and the per-device effect files under `lib/bluetooth/devices/effects/`.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## License
|
|
279
|
+
|
|
280
|
+
MIT
|
package/dist/qr-generator.js
CHANGED
|
@@ -6108,9 +6108,12 @@ async function decorateQR(qrPng, artist, song, deviceId, deviceName, options) {
|
|
|
6108
6108
|
return canvas.toBuffer("image/png");
|
|
6109
6109
|
}
|
|
6110
6110
|
async function main() {
|
|
6111
|
-
const
|
|
6111
|
+
const args = process.argv.slice(2);
|
|
6112
|
+
const outputFlagIdx = args.findIndex((a) => a === "--output" || a === "-o");
|
|
6113
|
+
const outDir = outputFlagIdx !== -1 ? resolve(args[outputFlagIdx + 1]) : process.cwd();
|
|
6114
|
+
const jsonPath = args.find((a, i) => !a.startsWith("-") && i !== outputFlagIdx + 1);
|
|
6112
6115
|
if (!jsonPath) {
|
|
6113
|
-
console.error("Usage: npx
|
|
6116
|
+
console.error("Usage: npx @cordfuse/nux-qr-tool <params-json-file> [--output <dir>]");
|
|
6114
6117
|
process.exit(1);
|
|
6115
6118
|
}
|
|
6116
6119
|
const raw = JSON.parse(readFileSync(resolve(jsonPath), "utf8"));
|
|
@@ -6129,7 +6132,6 @@ async function main() {
|
|
|
6129
6132
|
});
|
|
6130
6133
|
const decorated = await decorateQR(qrPng, params.artist, params.song, params.device, device.displayName);
|
|
6131
6134
|
const slug = `${params.artist}-${params.song}`.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
6132
|
-
const outDir = resolve("./output");
|
|
6133
6135
|
mkdirSync(outDir, { recursive: true });
|
|
6134
6136
|
const outPath = join(outDir, `${slug}.png`);
|
|
6135
6137
|
const { writeFileSync } = await import("fs");
|