alphatheta-connect 0.23.2 → 0.25.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/lib/cli.js +340 -55
- package/lib/cli.js.map +1 -1
- package/lib/constants.d.ts +4 -2
- package/lib/index.d.ts +2 -2
- package/lib/index.js +342 -56
- package/lib/index.js.map +1 -1
- package/lib/network.d.ts +10 -9
- package/lib/remotedb/index.d.ts +9 -5
- package/lib/status/types.d.ts +54 -6
- package/lib/types.js +20 -1
- package/lib/types.js.map +1 -1
- package/lib/virtualcdj/device-id.d.ts +36 -7
- package/lib/virtualcdj/heartbeat.d.ts +51 -0
- package/lib/virtualcdj/heartbeat.test.d.ts +1 -0
- package/lib/virtualcdj/index.d.ts +1 -0
- package/package.json +1 -1
package/lib/network.d.ts
CHANGED
|
@@ -21,18 +21,19 @@ export interface NetworkConfig {
|
|
|
21
21
|
*
|
|
22
22
|
* IMPORTANT:
|
|
23
23
|
*
|
|
24
|
-
* You will likely want to configure this to be > 6
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* You will likely want to configure this to be > 6: if you choose an ID
|
|
25
|
+
* within the 1-6 range, no other CDJ may exist on the network using that ID
|
|
26
|
+
* — you CAN NOT have 6 CDJs if you're using one of their slots.
|
|
27
27
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* This choice does NOT affect remotedb metadata (unanalyzed media, CD disc
|
|
29
|
+
* data, streaming tracks). CDJs restrict remotedb to a device-ID byte in
|
|
30
|
+
* the 1-6 range, but that byte lives inside the remotedb messages and is
|
|
31
|
+
* picked per-connection by RemoteDatabase, independent of the announced ID
|
|
32
|
+
* (hardware-verified on CDJ-3000, 2026-08-30).
|
|
32
33
|
*
|
|
33
34
|
* Note that rekordbox analyzed media connected to the CDJ is accessed out of
|
|
34
|
-
* band of the networks remote database protocol, and
|
|
35
|
-
* restriction.
|
|
35
|
+
* band of the networks remote database protocol, and was never limited by
|
|
36
|
+
* that restriction either.
|
|
36
37
|
*/
|
|
37
38
|
vcdjId?: number;
|
|
38
39
|
/**
|
package/lib/remotedb/index.d.ts
CHANGED
|
@@ -138,12 +138,16 @@ export declare class QueryInterface {
|
|
|
138
138
|
*/
|
|
139
139
|
export default class RemoteDatabase {
|
|
140
140
|
#private;
|
|
141
|
-
constructor(deviceManager: DeviceManager, hostDevice: Device
|
|
141
|
+
constructor(deviceManager: DeviceManager, hostDevice: Device, { pickQueryId }?: {
|
|
142
|
+
pickQueryId?: boolean;
|
|
143
|
+
});
|
|
142
144
|
/**
|
|
143
|
-
* The device
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
145
|
+
* The device this service belongs to — the virtual CDJ announced on the
|
|
146
|
+
* network. Note that this is NOT necessarily the ID carried inside remotedb
|
|
147
|
+
* messages: CDJs only answer queries whose in-protocol device-ID byte is
|
|
148
|
+
* 1-6, so when this device sits outside that range (announced above the
|
|
149
|
+
* player range to avoid collisions), each connection picks an in-range
|
|
150
|
+
* query ID via {@link pickRemoteDbQueryId} instead.
|
|
147
151
|
*/
|
|
148
152
|
get hostDevice(): Device;
|
|
149
153
|
/**
|
package/lib/status/types.d.ts
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
import { DeviceID, MediaSlot, TrackType } from "../types";
|
|
2
2
|
/**
|
|
3
|
-
* Status flag bitmasks
|
|
3
|
+
* Status flag bitmasks (byte 0x89 of the CDJ status packet).
|
|
4
|
+
*
|
|
5
|
+
* Verified against CDJ-3000 firmware (EP122 FW3.20): the deck assembles this
|
|
6
|
+
* byte in `sub_d7d3c8` (0xd7d3c8) from the BeatSyncMaster state struct, one
|
|
7
|
+
* source boolean per bit. The bits not named here:
|
|
8
|
+
*
|
|
9
|
+
* - bit 0 (0x01): structurally unused — no code path ever sets it (always 0).
|
|
10
|
+
* - bit 2 (0x04): a real, dedicated beat-sync boolean (struct +5), but its
|
|
11
|
+
* name did not survive in the stripped `usecase::sync` async-task code. It
|
|
12
|
+
* is normally 0, which is why it has never been observed on the wire.
|
|
13
|
+
* - bit 7 (0x80): a real bit (struct +0xa) that is default-set at init and
|
|
14
|
+
* whose de-assertion itself triggers a state-change notification — best read
|
|
15
|
+
* as a "sync-master active / handoff-in-progress" toggle. Also normally 0 in
|
|
16
|
+
* steady state.
|
|
4
17
|
*/
|
|
5
18
|
export declare enum StatusFlag {
|
|
19
|
+
/**
|
|
20
|
+
* Degraded to BPM Sync: the player is still tracking the master's tempo, but
|
|
21
|
+
* beat alignment was dropped after a pitch-bend / jog nudge. Firmware sources
|
|
22
|
+
* this from BeatSyncMaster struct +4.
|
|
23
|
+
*/
|
|
24
|
+
BpmSync = 2,
|
|
6
25
|
OnAir = 8,
|
|
7
26
|
Sync = 16,
|
|
8
27
|
Master = 32,
|
|
@@ -68,6 +87,14 @@ export interface State {
|
|
|
68
87
|
* Whether the CDJ is synced.
|
|
69
88
|
*/
|
|
70
89
|
isSync: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Whether the CDJ has degraded into BPM Sync — still in Sync mode and
|
|
92
|
+
* tracking the master's tempo, but no longer beat-aligned because the DJ used
|
|
93
|
+
* pitch bend (e.g. nudged the jog wheel). Corresponds to
|
|
94
|
+
* {@link StatusFlag.BpmSync} (bit 1 of byte 0x89), which older pre-nexus
|
|
95
|
+
* players never set.
|
|
96
|
+
*/
|
|
97
|
+
isBpmSync: boolean;
|
|
71
98
|
/**
|
|
72
99
|
* Whether the CDJ is the master player.
|
|
73
100
|
*/
|
|
@@ -82,13 +109,17 @@ export interface State {
|
|
|
82
109
|
*/
|
|
83
110
|
trackBPM: number | null;
|
|
84
111
|
/**
|
|
85
|
-
* The
|
|
86
|
-
*
|
|
87
|
-
*
|
|
112
|
+
* The pitch actually *in effect* — the value shown on the BPM display,
|
|
113
|
+
* whether it comes from the local pitch fader or a synced tempo master
|
|
114
|
+
* (packet Pitch1 @ 0x8c). This is the value to combine with `trackBPM` to get
|
|
115
|
+
* the playing BPM. It is also what is reported when the jog wheel is nudged,
|
|
116
|
+
* the platter is held, or the deck spins down on the vinyl stop knob.
|
|
88
117
|
*/
|
|
89
118
|
effectivePitch: number;
|
|
90
119
|
/**
|
|
91
|
-
* The
|
|
120
|
+
* The *local pitch-fader* position (packet Pitch2 @ 0x98) — always tied to
|
|
121
|
+
* the physical fader, following the player's brake/release ramp as playback
|
|
122
|
+
* stops or starts, regardless of any sync master.
|
|
92
123
|
*/
|
|
93
124
|
sliderPitch: number;
|
|
94
125
|
/**
|
|
@@ -106,7 +137,24 @@ export interface State {
|
|
|
106
137
|
*/
|
|
107
138
|
beat: number | null;
|
|
108
139
|
/**
|
|
109
|
-
*
|
|
140
|
+
* The player-type / capability byte (packet byte 0xcc, dysentery's "nx").
|
|
141
|
+
*
|
|
142
|
+
* This is a capability *bitfield*, not a model id. Known values: `0x05` for
|
|
143
|
+
* older (pre-nexus) players, `0x0f` for nexus, and `0x1f` for the CDJ-3000
|
|
144
|
+
* and XDJ-XZ (the nexus value plus bit 4). Firmware (CDJ-3000 FW3.20, one
|
|
145
|
+
* 16-bit store in `sub_d858b8`) hardcodes `0x1f`. The byte is version-gated:
|
|
146
|
+
* a player zeroes it toward peers advertising a Pro DJ Link protocol version
|
|
147
|
+
* below 3, so a much older device on the link may report `0`.
|
|
148
|
+
*/
|
|
149
|
+
deviceType: number;
|
|
150
|
+
/**
|
|
151
|
+
* A counter that increments for every status packet sent (packet byte 0xc8).
|
|
152
|
+
*
|
|
153
|
+
* Caveat: on the CDJ-3000 this field is hardwired to 0 — the firmware never
|
|
154
|
+
* writes packet offset 0xc8 (verified: no store to the status body's +0xa4).
|
|
155
|
+
* The CDJ-3000's live per-packet counter moved to its high-resolution stream
|
|
156
|
+
* packet on UDP 50004 (a big-endian u32 at that packet's offset 0x28). Do not
|
|
157
|
+
* rely on `packetNum` to detect liveness or drops on CDJ-3000 hardware.
|
|
110
158
|
*/
|
|
111
159
|
packetNum: number;
|
|
112
160
|
}
|
package/lib/types.js
CHANGED
|
@@ -12,10 +12,29 @@
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
13
13
|
exports.PlayState = exports.StatusFlag = void 0;
|
|
14
14
|
/**
|
|
15
|
-
* Status flag bitmasks
|
|
15
|
+
* Status flag bitmasks (byte 0x89 of the CDJ status packet).
|
|
16
|
+
*
|
|
17
|
+
* Verified against CDJ-3000 firmware (EP122 FW3.20): the deck assembles this
|
|
18
|
+
* byte in `sub_d7d3c8` (0xd7d3c8) from the BeatSyncMaster state struct, one
|
|
19
|
+
* source boolean per bit. The bits not named here:
|
|
20
|
+
*
|
|
21
|
+
* - bit 0 (0x01): structurally unused — no code path ever sets it (always 0).
|
|
22
|
+
* - bit 2 (0x04): a real, dedicated beat-sync boolean (struct +5), but its
|
|
23
|
+
* name did not survive in the stripped `usecase::sync` async-task code. It
|
|
24
|
+
* is normally 0, which is why it has never been observed on the wire.
|
|
25
|
+
* - bit 7 (0x80): a real bit (struct +0xa) that is default-set at init and
|
|
26
|
+
* whose de-assertion itself triggers a state-change notification — best read
|
|
27
|
+
* as a "sync-master active / handoff-in-progress" toggle. Also normally 0 in
|
|
28
|
+
* steady state.
|
|
16
29
|
*/
|
|
17
30
|
var StatusFlag;
|
|
18
31
|
(function (StatusFlag) {
|
|
32
|
+
/**
|
|
33
|
+
* Degraded to BPM Sync: the player is still tracking the master's tempo, but
|
|
34
|
+
* beat alignment was dropped after a pitch-bend / jog nudge. Firmware sources
|
|
35
|
+
* this from BeatSyncMaster struct +4.
|
|
36
|
+
*/
|
|
37
|
+
StatusFlag[StatusFlag["BpmSync"] = 2] = "BpmSync";
|
|
19
38
|
StatusFlag[StatusFlag["OnAir"] = 8] = "OnAir";
|
|
20
39
|
StatusFlag[StatusFlag["Sync"] = 16] = "Sync";
|
|
21
40
|
StatusFlag[StatusFlag["Master"] = 32] = "Master";
|
package/lib/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","mappings":";;;;;;;;;;;;;AAEA;;GAEG;AACH,IAAY,UAKX;AALD,WAAY,UAAU;IACpB,6CAAc;IACd,4CAAa;IACb,gDAAe;IACf,kDAAgB;AAClB,CAAC,EALW,UAAU,0BAAV,UAAU,QAKrB;AAED;;GAEG;AACH,IAAY,SAYX;AAZD,WAAY,SAAS;IACnB,2CAAY;IACZ,+CAAc;IACd,+CAAc;IACd,+CAAc;IACd,6CAAa;IACb,yCAAW;IACX,2CAAY;IACZ,uDAAkB;IAClB,mDAAgB;IAChB,kDAAe;IACf,4CAAY;AACd,CAAC,EAZW,SAAS,yBAAT,SAAS,QAYpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACvBD,uGAA8C;AAyB9C;;GAEG;AACH,IAAY,UAKX;AALD,WAAY,UAAU;IACpB,yCAAU;IACV,6CAAY;IACZ,qDAAgB;IAChB,qDAAgB;AAClB,CAAC,EALW,UAAU,0BAAV,UAAU,QAKrB;AAsED,IAAY,UAUX;AAVD,WAAY,UAAU;IACpB,iDAAc;IACd,2CAAW;IACX,yCAAU;IACV,+CAAa;IACb,+CAAa;IACb,6CAAY;IACZ,2CAAW;IACX,2CAAW;IACX,+CAAa;AACf,CAAC,EAVW,UAAU,0BAAV,UAAU,QAUrB;AAED;;GAEG;AACH,IAAY,SAWX;AAXD,WAAY,SAAS;IACnB,2CAAY;IACZ,qCAAS;IACT,qCAAS;IACT,uCAAU;IACV,qCAAS;IACT,mDAAgB;IAChB,uEAA0B;IAC1B,mDAAgB;IAChB,mDAAgB;IAChB,iDAAe;AACjB,CAAC,EAXW,SAAS,yBAAT,SAAS,QAWpB;AAED;;GAEG;AACH,IAAY,SAMX;AAND,WAAY,SAAS;IACnB,yCAAW;IACX,qCAAS;IACT,qDAAiB;IACjB,+CAAc;IACd,mDAAgB;AAClB,CAAC,EANW,SAAS,yBAAT,SAAS,QAMpB;AAkGD,+FAA0D;AAAlD,uHAAQ;AAAE,+HAAY;AAwL9B,IAAY,YAoBX;AApBD,WAAY,YAAY;IACtB;;;OAGG;IACH,qDAAO;IACP;;;OAGG;IACH,mDAAM;IACN;;OAEG;IACH,yDAAS;IACT;;;OAGG;IACH,mDAAM;AACR,CAAC,EApBW,YAAY,4BAAZ,YAAY,QAoBvB;AAED;;;GAGG;AACH,IAAY,aA+BX;AA/BD,WAAY,aAAa;IACvB;;;;;;;;;;;;;;;;;;OAkBG;IACH,+DAAW;IACX;;;;OAIG;IACH,uEAAe;IACf;;OAEG;IACH,mEAAa;AACf,CAAC,EA/BW,aAAa,6BAAb,aAAa,QA+BxB;;;;;;;;;;;ACneD,+C;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;UE5BA;UACA;UACA;UACA","sources":["webpack://alphatheta-connect/./src/status/types.ts","webpack://alphatheta-connect/./src/types.ts","webpack://alphatheta-connect/external commonjs \"onelibrary-connect\"","webpack://alphatheta-connect/webpack/bootstrap","webpack://alphatheta-connect/webpack/before-startup","webpack://alphatheta-connect/webpack/startup","webpack://alphatheta-connect/webpack/after-startup"],"sourcesContent":["import {DeviceID, MediaSlot, TrackType} from 'src/types';\n\n/**\n * Status flag bitmasks\n */\nexport enum StatusFlag {\n OnAir = 1 << 3,\n Sync = 1 << 4,\n Master = 1 << 5,\n Playing = 1 << 6,\n}\n\n/**\n * Play state flags\n */\nexport enum PlayState {\n Empty = 0x00,\n Loading = 0x02,\n Playing = 0x03,\n Looping = 0x04,\n Paused = 0x05,\n Cued = 0x06,\n Cuing = 0x07,\n PlatterHeld = 0x08,\n Searching = 0x09,\n SpunDown = 0x0e,\n Ended = 0x11,\n}\n\n/**\n * Represents various details about the current state of the CDJ.\n */\nexport interface State {\n /**\n * The device reporting this status.\n */\n deviceId: number;\n /**\n * The ID of the track loaded on the device.\n *\n * 0 When no track is loaded.\n */\n trackId: number;\n /**\n * The device ID the track is loaded from.\n *\n * For example if you have two CDJs and you've loaded a track over the 'LINK',\n * this will be the ID of the player with the USB media device connected to it.\n */\n trackDeviceId: DeviceID;\n /**\n * The MediaSlot the track is loaded from. For example a SD card or USB device.\n */\n trackSlot: MediaSlot;\n /**\n * The TrackType of the track, for example a CD or Rekordbox analyzed track.\n */\n trackType: TrackType;\n /**\n * The current play state of the CDJ.\n */\n playState: PlayState;\n /**\n * Whether the CDJ is currently reporting itself as 'on-air'.\n *\n * This is indicated by the red ring around the platter on the CDJ Nexus models.\n * A DJM mixer must be ont he network for the CDJ to report this as true.\n */\n isOnAir: boolean;\n /**\n * Whether the CDJ is synced.\n */\n isSync: boolean;\n /**\n * Whether the CDJ is the master player.\n */\n isMaster: boolean;\n /**\n * Whether the CDJ is in an emergency state (emergecy loop / emergency mode\n * on newer players)\n */\n isEmergencyMode: boolean;\n /**\n * The BPM of the loaded track. null if no track is loaded or the BPM is unknown.\n */\n trackBPM: number | null;\n /**\n * The \"effective\" pitch of the plyaer. This is reported anytime the jogwheel is\n * nudged, the CDJ spins down by pausing with the vinyl stop knob not at 0, or\n * by holding the platter.\n */\n effectivePitch: number;\n /**\n * The current slider pitch\n */\n sliderPitch: number;\n /**\n * The current beat within the measure. 1-4. 0 when no track is loaded.\n */\n beatInMeasure: number;\n /**\n * Number of beats remaining until the next cue point is reached. Null if there\n * is no next cue point\n */\n beatsUntilCue: number | null;\n /**\n * The beat 'timestamp' of the track. Can be used to compute absolute track time\n * given the slider pitch.\n */\n beat: number | null;\n /**\n * A counter that increments for every status packet sent.\n */\n packetNum: number;\n}\n\n/**\n * Absolute position information from CDJ-3000+ devices.\n * Sent every 30ms on port 50001 while a track is loaded.\n * Provides precise playhead position independent of beat grid.\n */\nexport interface PositionState {\n /**\n * The device ID sending this position update.\n */\n deviceId: number;\n /**\n * Track length in seconds (rounded down to nearest second).\n */\n trackLength: number;\n /**\n * Absolute playhead position in milliseconds.\n */\n playhead: number;\n /**\n * Pitch slider value as shown on screen.\n * For example, 3.26% is represented as 3.26.\n */\n pitch: number;\n /**\n * Effective BPM (track BPM adjusted by pitch) as shown on screen.\n * null if BPM is unknown.\n */\n bpm: number | null;\n}\n\n/**\n * On-Air status from DJM mixer.\n * Broadcast by the mixer to indicate which channels are currently audible.\n * Supports both 4-channel (DJM-900/1000) and 6-channel (DJM-V10) mixers.\n */\nexport interface OnAirStatus {\n /**\n * The mixer device ID (typically 33 / 0x21).\n */\n deviceId: number;\n /**\n * On-air flags for channels 1-4 (always present).\n * 0x00 = channel is off-air (silenced)\n * 0x01 = channel is on-air (audible)\n */\n channels: {\n 1: boolean;\n 2: boolean;\n 3: boolean;\n 4: boolean;\n 5?: boolean;\n 6?: boolean;\n };\n /**\n * Whether this is a 6-channel variant (CDJ-3000 + DJM-V10).\n * Determined by packet subtype (0x00 = 4-channel, 0x03 = 6-channel).\n */\n isSixChannel: boolean;\n}\n\n/**\n * State of a single mixer channel fader, EQ, trim and routing.\n */\nexport interface ChannelState {\n /**\n * Input Trim level (0-255).\n * Unity gain is typically 128 (0x80).\n */\n trim: number;\n /**\n * EQ High level (0-255).\n * Fully cut at 0, unity at 128 (0x80), boosted to max at 255.\n */\n eqHi: number;\n /**\n * EQ Mid level (0-255).\n * Fully cut at 0, unity at 128 (0x80), boosted to max at 255.\n */\n eqMid: number;\n /**\n * EQ Low level (0-255).\n * Fully cut at 0, unity at 128 (0x80), boosted to max at 255.\n */\n eqLow: number;\n /**\n * Color FX knob position (0-255).\n * Centered at 128 (0x80).\n */\n colorFx: number;\n /**\n * Channel fader position (0-255).\n * 0 is completely closed, 255 is maximum level.\n */\n fader: number;\n /**\n * Crossfader assignment.\n */\n crossfaderAssign: 'thru' | 'A' | 'B';\n}\n\n/**\n * Full mixer control state parsed from Stagehand unicast status (0x39 packets).\n */\nexport interface MixerState {\n /**\n * The reporting device ID (typically 33).\n */\n deviceId: number;\n /**\n * Device name reported by the mixer (e.g. \"DJM-A9\").\n */\n deviceName: string;\n /**\n * State of mixer channels (1-4).\n */\n channels: Record<number, ChannelState>;\n /**\n * Crossfader position (0-255).\n * 0 is full-left (A), 255 is full-right (B).\n */\n crossfader: number;\n}\n\n/**\n * A single audio VU level frame.\n */\nexport interface VUFrame {\n /**\n * Left channel level (0-65535).\n */\n left: number;\n /**\n * Right channel level (0-65535).\n */\n right: number;\n}\n\n/**\n * Real-time sliding window audio level VU data parsed from Stagehand unicast packets (0x58).\n */\nexport interface VUState {\n /**\n * The reporting device ID (typically 33).\n */\n deviceId: number;\n /**\n * Array of 15 sliding window stereo VU level frames per channel (1-4).\n */\n channels: Record<number, VUFrame[]>;\n}\n","import type {Address4} from 'ip-address';\n\nimport type {Playlist, Track} from './entities';\n\nexport * as CDJStatus from 'src/status/types';\n\n/**\n * Re-export various types for the types only compile target\n */\n\nexport type {TrackAnalysis} from './db/getTrackAnalysis';\nexport type {\n Album,\n Artist,\n Artwork,\n Color,\n Genre,\n Key,\n Label,\n Playlist,\n Track,\n} from './entities';\nexport type {HydrationProgress} from './localdb/rekordbox';\nexport type {MixstatusConfig, MixstatusProcessor} from './mixstatus';\n// Note: ProlinkNetwork is exported as a class from ./network, not re-exported here as type-only\n// to preserve method signatures like close()\nexport type {ConnectedProlinkNetwork, NetworkConfig} from './network';\nexport type {FetchProgress} from './nfs';\n\n/**\n * Known device types on the network\n */\nexport enum DeviceType {\n CDJ = 0x01,\n Mixer = 0x03,\n Rekordbox = 0x04,\n Stagehand = 0x05,\n}\n\n/**\n * The 8-bit identifier of the device on the network\n */\nexport type DeviceID = number;\n\n/**\n * Represents a device on the prolink network.\n */\nexport interface Device {\n name: string;\n id: DeviceID;\n type: DeviceType;\n macAddr: Uint8Array;\n ip: Address4;\n lastActive?: Date;\n}\n\n/**\n * Details of a particular media slot on the CDJ\n */\nexport interface MediaSlotInfo {\n /**\n * The device the slot physically exists on\n */\n deviceId: DeviceID;\n /**\n * The slot type\n */\n slot: MediaSlot;\n /**\n * The name of the media connected\n */\n name: string;\n /**\n * The rekordbox configured color of the media connected\n */\n color: MediaColor;\n /**\n * Creation date\n */\n createdDate: Date;\n /**\n * Number of free bytes available on the media\n */\n freeBytes: bigint;\n /**\n * Number of bytes used on the media\n */\n totalBytes: bigint;\n /**\n * Specifies the available tracks type on the media\n */\n tracksType: TrackType;\n /**\n * Total number of rekordbox tracks on the media. Will be zero if there is\n * no rekordbox database on the media\n */\n trackCount: number;\n /**\n * Same as track count, except for playlists\n */\n playlistCount: number;\n /**\n * True when a rekordbox 'my settings' file has been exported to the media\n */\n hasSettings: boolean;\n}\n\nexport enum MediaColor {\n Default = 0x00,\n Pink = 0x01,\n Red = 0x02,\n Orange = 0x03,\n Yellow = 0x04,\n Green = 0x05,\n Aqua = 0x06,\n Blue = 0x07,\n Purple = 0x08,\n}\n\n/**\n * A slot where media is present on the CDJ\n */\nexport enum MediaSlot {\n Empty = 0x00,\n CD = 0x01,\n SD = 0x02,\n USB = 0x03,\n RB = 0x04,\n Unknown05 = 0x05, // Possibly TIDAL, Apple Music, or other streaming\n StreamingDirectPlay = 0x06,\n Unknown07 = 0x07, // Possibly TIDAL, Apple Music, or other streaming\n Unknown08 = 0x08, // Possibly TIDAL, Apple Music, or other streaming\n Beatport = 0x09,\n}\n\n/**\n * Track type flags\n */\nexport enum TrackType {\n None = 0x00,\n RB = 0x01,\n Unanalyzed = 0x02,\n AudioCD = 0x05,\n Streaming = 0x06,\n}\n\n/**\n * A beat grid is a series of offsets from the start of the track. Each offset\n * indicates what count within the measure it is along with the BPM.\n */\nexport type BeatGrid = Array<{\n /**\n * Offset from the beginning of track in milliseconds of this beat.\n */\n offset: number;\n /**\n * The count of this particular beat within the measure\n */\n count: 1 | 2 | 3 | 4;\n /**\n * The BPM at this beat.\n */\n bpm: number;\n}>;\n\n/**\n * A waveform segment contains a height and 'whiteness' value.\n */\ninterface WaveformSegment {\n /**\n * The height this segment in the waveform. Ranges from 0 - 31.\n */\n height: number;\n /**\n * The level of \"whiteness\" of the waveform. 0 being completely blue, and 1\n * being completely white.\n */\n whiteness: number;\n}\n\n/**\n * A HD waveform segment contains the height of the waveform, and it's color\n * represented as RGB values.\n */\ninterface WaveformHDSegment {\n /**\n * The height this segment in the waveform. Ranges from 0 - 31.\n */\n height: number;\n /**\n * the RGB value, each channel ranges from 0-1 for the segment.\n */\n color: [number, number, number];\n}\n\n/**\n * The waveform preview will be 400 segments of data.\n */\nexport type WaveformPreview = WaveformSegment[];\n\n/**\n * Detailed waveforms have 150 segments per second of audio (150 'half frames'\n * per second of audio).\n */\nexport type WaveformDetailed = WaveformSegment[];\n\n/**\n * HD waveforms have 150 segments per second of audio (150 'half frames' per\n * second of audio).\n */\nexport type WaveformHD = WaveformHDSegment[];\n\n/**\n * The result of looking up track waveforms\n */\nexport interface Waveforms {\n /**\n * The full-size and full-color waveform\n */\n waveformHd: WaveformHD;\n\n /**\n * Color waveform preview (PWV4 tag).\n * Raw bytes: 1200 columns × 6 bytes per column (3 frequency bands × 2 bytes each).\n */\n waveformColorPreview?: Uint8Array;\n\n /**\n * Standard waveform preview (400 entries). Available for streaming tracks.\n */\n waveformPreview?: WaveformPreview;\n\n /**\n * Full detailed waveform. Available for streaming tracks.\n */\n waveformDetailed?: WaveformDetailed;\n}\n\n/**\n * Re-export cue types from onelibrary-connect\n */\nexport type {CueAndLoop, CuePoint, Hotcue, Hotloop, Loop} from 'onelibrary-connect';\nexport {CueColor, HotcueButton} from 'onelibrary-connect';\n\n/**\n * Extended cue with color and comment support (PCO2 tag from rekordbox).\n * Includes additional metadata like RGB colors, comments, and quantized loop information.\n */\nexport interface ExtendedCue {\n /**\n * Hot cue number (0 for memory points, 1-8 for hot cues A-H)\n */\n hotCue: number;\n /**\n * Type of cue: 1 = simple position/cue, 2 = loop\n */\n type: 1 | 2;\n /**\n * Position in milliseconds from the start of the track\n */\n time: number;\n /**\n * For loops, the end position in milliseconds\n */\n loopTime?: number;\n /**\n * Color ID referencing the color table (for memory points/loops)\n */\n colorId?: number;\n /**\n * Color code for the hot cue palette (0x00 = default green, 0x01-0x3e = palette colors)\n */\n colorCode?: number;\n /**\n * RGB color values used to illuminate the player's RGB LEDs\n */\n colorRgb?: {r: number; g: number; b: number};\n /**\n * User-assigned comment text for the cue\n */\n comment?: string;\n /**\n * For quantized loops, the numerator of the loop size fraction (e.g., 4 for a 4-beat loop)\n */\n loopNumerator?: number;\n /**\n * For quantized loops, the denominator of the loop size fraction (e.g., 1 for a 4-beat loop)\n */\n loopDenominator?: number;\n}\n\n/**\n * A phrase within a track's song structure\n */\nexport interface Phrase {\n /**\n * Sequential phrase number starting from 1\n */\n index: number;\n /**\n * Beat number where this phrase begins\n */\n beat: number;\n /**\n * Raw phrase kind value from rekordbox\n */\n kind: number;\n /**\n * Human-readable phrase type (e.g., \"Intro\", \"Verse 1\", \"Chorus\")\n */\n phraseType: string;\n /**\n * Whether this phrase has a fill-in section (non-zero if present)\n */\n fill?: number;\n /**\n * Beat number where the fill-in begins (if present)\n */\n fillBeat?: number;\n}\n\n/**\n * Song structure / phrase analysis (PSSI tag from rekordbox).\n * Used by CDJ-3000 players for phrase-based navigation and lighting control.\n */\nexport interface SongStructure {\n /**\n * Overall mood classification of the track\n */\n mood: 'high' | 'mid' | 'low';\n /**\n * Stylistic bank assigned for lighting control\n */\n bank:\n | 'default'\n | 'cool'\n | 'natural'\n | 'hot'\n | 'subtle'\n | 'warm'\n | 'vivid'\n | 'club_1'\n | 'club_2';\n /**\n * Beat number where the last phrase ends (track may continue after this)\n */\n endBeat: number;\n /**\n * List of identified phrases in the track\n */\n phrases: Phrase[];\n}\n\n/**\n * 3-band color waveform preview (PWV6 tag from .2EX files).\n * Same resolution as PWV4 (typically 1200 entries) but with separate\n * low, mid, and high frequency band amplitudes.\n *\n * @see https://djl-analysis.deepsymmetry.org/djl-analysis/track-metadata.html#color-3band-preview-waveform\n */\nexport interface Waveform3BandPreview {\n numEntries: number;\n /** Raw interleaved bytes: numEntries × 3 (low, mid, high per entry) */\n data: Uint8Array;\n}\n\n/**\n * 3-band color detail waveform (PWV7 tag from .2EX files).\n * Higher resolution than PWV6, approximately 150 entries per second.\n *\n * @see https://djl-analysis.deepsymmetry.org/djl-analysis/track-metadata.html#color-3band-detail-waveform\n */\nexport interface Waveform3BandDetail {\n numEntries: number;\n /** Raw interleaved bytes: numEntries × 3 (low, mid, high per entry) */\n data: Uint8Array;\n}\n\n/**\n * Vocal detection configuration (PWVC tag from .2EX files).\n * Threshold values used to classify frequency content as vocal or non-vocal.\n *\n * Values are u16 but observed range across 192 real files is 80-159,\n * matching the 0-255 byte scale used by waveform band values.\n * Observed ranges: low 80-114, mid 80-146, high 98-159.\n */\nexport interface VocalConfig {\n thresholdLow: number;\n thresholdMid: number;\n thresholdHigh: number;\n}\n\n/**\n * Monochrome waveform preview data (PWAV/PWV2 tags).\n * PWAV contains 400 bytes, PWV2 contains 100 bytes.\n */\nexport interface WaveformPreviewData {\n /**\n * Raw waveform data - each byte encodes height and whiteness\n */\n data: Uint8Array;\n}\n\n/**\n * Represents the contents of a playlist\n */\nexport interface PlaylistContents {\n /**\n * The playlists in this playlist.\n */\n playlists: Playlist[];\n /**\n * The folders in this playlist.\n */\n folders: Playlist[];\n /**\n * The tracks in this playlist. This is an AsyncIterator as looking up track\n * metadata may be slow when connected to the remote database.\n */\n tracks: AsyncIterable<Track>;\n /**\n * The total number of tracks in this playlist.\n */\n totalTracks: number;\n}\n\nexport enum NetworkState {\n /**\n * The network is offline when we don't have an open connection to the network\n * (no connection to the announcement and or status UDP socket is present).\n */\n Offline,\n /**\n * The network is online when we have opened sockets to the network, but have\n * not yet started announcing ourselves as a virtual CDJ.\n */\n Online,\n /**\n * The network is connected once we have heard from another device on the network\n */\n Connected,\n /**\n * The network may have failed to connect if we aren't able to open the\n * announcement and or status UDP socket.\n */\n Failed,\n}\n\n/**\n * Mixstatus reporting modes specify how the mixstatus processor will determine when a new\n * track is 'now playing'.\n */\nexport enum MixstatusMode {\n /**\n * Tracks will be smartly marked as playing following rules:\n *\n * - The track that has been in the play state with the CDJ in the \"on air\" state\n * for the longest period of time (allowing for a configurable length of\n * interruption with allowedInterruptBeats) is considered to be the active\n * track that incoming tracks will be compared against.\n *\n * - A incoming track will immediately be reported as nowPlaying if it is on\n * air, playing, and the last active track has been cued.\n *\n * - A incoming track will be reported as nowPlaying if the active track has\n * not been on air or has not been playing for the configured\n * allowedInterruptBeats.\n *\n * - A incoming track will be reported as nowPlaying if it has played\n * consecutively (with allowedInterruptBeats honored for the incoming track)\n * for the configured beatsUntilReported.\n */\n SmartTiming,\n /**\n * Tracks will not be reported after the beatsUntilReported AND will ONLY\n * be reported if the other track has gone into a non-playing play state, or\n * taken off air (when useOnAirStatus is enabled).\n */\n WaitsForSilence,\n /**\n * The track will simply be reported only after the player becomes master.\n */\n FollowsMaster,\n}\n","module.exports = require(\"onelibrary-connect\");","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Check if module exists (development only)\n\tif (__webpack_modules__[moduleId] === undefined) {\n\t\tvar e = new Error(\"Cannot find module '\" + moduleId + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(\"./src/types.ts\");\n",""],"names":[],"ignoreList":[],"sourceRoot":""}
|
|
1
|
+
{"version":3,"file":"types.js","mappings":";;;;;;;;;;;;;AAEA;;;;;;;;;;;;;;;GAeG;AACH,IAAY,UAWX;AAXD,WAAY,UAAU;IACpB;;;;OAIG;IACH,iDAAgB;IAChB,6CAAc;IACd,4CAAa;IACb,gDAAe;IACf,kDAAgB;AAClB,CAAC,EAXW,UAAU,0BAAV,UAAU,QAWrB;AAED;;GAEG;AACH,IAAY,SAYX;AAZD,WAAY,SAAS;IACnB,2CAAY;IACZ,+CAAc;IACd,+CAAc;IACd,+CAAc;IACd,6CAAa;IACb,yCAAW;IACX,2CAAY;IACZ,uDAAkB;IAClB,mDAAgB;IAChB,kDAAe;IACf,4CAAY;AACd,CAAC,EAZW,SAAS,yBAAT,SAAS,QAYpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1CD,uGAA8C;AAyB9C;;GAEG;AACH,IAAY,UAKX;AALD,WAAY,UAAU;IACpB,yCAAU;IACV,6CAAY;IACZ,qDAAgB;IAChB,qDAAgB;AAClB,CAAC,EALW,UAAU,0BAAV,UAAU,QAKrB;AAsED,IAAY,UAUX;AAVD,WAAY,UAAU;IACpB,iDAAc;IACd,2CAAW;IACX,yCAAU;IACV,+CAAa;IACb,+CAAa;IACb,6CAAY;IACZ,2CAAW;IACX,2CAAW;IACX,+CAAa;AACf,CAAC,EAVW,UAAU,0BAAV,UAAU,QAUrB;AAED;;GAEG;AACH,IAAY,SAWX;AAXD,WAAY,SAAS;IACnB,2CAAY;IACZ,qCAAS;IACT,qCAAS;IACT,uCAAU;IACV,qCAAS;IACT,mDAAgB;IAChB,uEAA0B;IAC1B,mDAAgB;IAChB,mDAAgB;IAChB,iDAAe;AACjB,CAAC,EAXW,SAAS,yBAAT,SAAS,QAWpB;AAED;;GAEG;AACH,IAAY,SAMX;AAND,WAAY,SAAS;IACnB,yCAAW;IACX,qCAAS;IACT,qDAAiB;IACjB,+CAAc;IACd,mDAAgB;AAClB,CAAC,EANW,SAAS,yBAAT,SAAS,QAMpB;AAkGD,+FAA0D;AAAlD,uHAAQ;AAAE,+HAAY;AAwL9B,IAAY,YAoBX;AApBD,WAAY,YAAY;IACtB;;;OAGG;IACH,qDAAO;IACP;;;OAGG;IACH,mDAAM;IACN;;OAEG;IACH,yDAAS;IACT;;;OAGG;IACH,mDAAM;AACR,CAAC,EApBW,YAAY,4BAAZ,YAAY,QAoBvB;AAED;;;GAGG;AACH,IAAY,aA+BX;AA/BD,WAAY,aAAa;IACvB;;;;;;;;;;;;;;;;;;OAkBG;IACH,+DAAW;IACX;;;;OAIG;IACH,uEAAe;IACf;;OAEG;IACH,mEAAa;AACf,CAAC,EA/BW,aAAa,6BAAb,aAAa,QA+BxB;;;;;;;;;;;ACneD,+C;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;UE5BA;UACA;UACA;UACA","sources":["webpack://alphatheta-connect/./src/status/types.ts","webpack://alphatheta-connect/./src/types.ts","webpack://alphatheta-connect/external commonjs \"onelibrary-connect\"","webpack://alphatheta-connect/webpack/bootstrap","webpack://alphatheta-connect/webpack/before-startup","webpack://alphatheta-connect/webpack/startup","webpack://alphatheta-connect/webpack/after-startup"],"sourcesContent":["import {DeviceID, MediaSlot, TrackType} from 'src/types';\n\n/**\n * Status flag bitmasks (byte 0x89 of the CDJ status packet).\n *\n * Verified against CDJ-3000 firmware (EP122 FW3.20): the deck assembles this\n * byte in `sub_d7d3c8` (0xd7d3c8) from the BeatSyncMaster state struct, one\n * source boolean per bit. The bits not named here:\n *\n * - bit 0 (0x01): structurally unused — no code path ever sets it (always 0).\n * - bit 2 (0x04): a real, dedicated beat-sync boolean (struct +5), but its\n * name did not survive in the stripped `usecase::sync` async-task code. It\n * is normally 0, which is why it has never been observed on the wire.\n * - bit 7 (0x80): a real bit (struct +0xa) that is default-set at init and\n * whose de-assertion itself triggers a state-change notification — best read\n * as a \"sync-master active / handoff-in-progress\" toggle. Also normally 0 in\n * steady state.\n */\nexport enum StatusFlag {\n /**\n * Degraded to BPM Sync: the player is still tracking the master's tempo, but\n * beat alignment was dropped after a pitch-bend / jog nudge. Firmware sources\n * this from BeatSyncMaster struct +4.\n */\n BpmSync = 1 << 1,\n OnAir = 1 << 3,\n Sync = 1 << 4,\n Master = 1 << 5,\n Playing = 1 << 6,\n}\n\n/**\n * Play state flags\n */\nexport enum PlayState {\n Empty = 0x00,\n Loading = 0x02,\n Playing = 0x03,\n Looping = 0x04,\n Paused = 0x05,\n Cued = 0x06,\n Cuing = 0x07,\n PlatterHeld = 0x08,\n Searching = 0x09,\n SpunDown = 0x0e,\n Ended = 0x11,\n}\n\n/**\n * Represents various details about the current state of the CDJ.\n */\nexport interface State {\n /**\n * The device reporting this status.\n */\n deviceId: number;\n /**\n * The ID of the track loaded on the device.\n *\n * 0 When no track is loaded.\n */\n trackId: number;\n /**\n * The device ID the track is loaded from.\n *\n * For example if you have two CDJs and you've loaded a track over the 'LINK',\n * this will be the ID of the player with the USB media device connected to it.\n */\n trackDeviceId: DeviceID;\n /**\n * The MediaSlot the track is loaded from. For example a SD card or USB device.\n */\n trackSlot: MediaSlot;\n /**\n * The TrackType of the track, for example a CD or Rekordbox analyzed track.\n */\n trackType: TrackType;\n /**\n * The current play state of the CDJ.\n */\n playState: PlayState;\n /**\n * Whether the CDJ is currently reporting itself as 'on-air'.\n *\n * This is indicated by the red ring around the platter on the CDJ Nexus models.\n * A DJM mixer must be ont he network for the CDJ to report this as true.\n */\n isOnAir: boolean;\n /**\n * Whether the CDJ is synced.\n */\n isSync: boolean;\n /**\n * Whether the CDJ has degraded into BPM Sync — still in Sync mode and\n * tracking the master's tempo, but no longer beat-aligned because the DJ used\n * pitch bend (e.g. nudged the jog wheel). Corresponds to\n * {@link StatusFlag.BpmSync} (bit 1 of byte 0x89), which older pre-nexus\n * players never set.\n */\n isBpmSync: boolean;\n /**\n * Whether the CDJ is the master player.\n */\n isMaster: boolean;\n /**\n * Whether the CDJ is in an emergency state (emergecy loop / emergency mode\n * on newer players)\n */\n isEmergencyMode: boolean;\n /**\n * The BPM of the loaded track. null if no track is loaded or the BPM is unknown.\n */\n trackBPM: number | null;\n /**\n * The pitch actually *in effect* — the value shown on the BPM display,\n * whether it comes from the local pitch fader or a synced tempo master\n * (packet Pitch1 @ 0x8c). This is the value to combine with `trackBPM` to get\n * the playing BPM. It is also what is reported when the jog wheel is nudged,\n * the platter is held, or the deck spins down on the vinyl stop knob.\n */\n effectivePitch: number;\n /**\n * The *local pitch-fader* position (packet Pitch2 @ 0x98) — always tied to\n * the physical fader, following the player's brake/release ramp as playback\n * stops or starts, regardless of any sync master.\n */\n sliderPitch: number;\n /**\n * The current beat within the measure. 1-4. 0 when no track is loaded.\n */\n beatInMeasure: number;\n /**\n * Number of beats remaining until the next cue point is reached. Null if there\n * is no next cue point\n */\n beatsUntilCue: number | null;\n /**\n * The beat 'timestamp' of the track. Can be used to compute absolute track time\n * given the slider pitch.\n */\n beat: number | null;\n /**\n * The player-type / capability byte (packet byte 0xcc, dysentery's \"nx\").\n *\n * This is a capability *bitfield*, not a model id. Known values: `0x05` for\n * older (pre-nexus) players, `0x0f` for nexus, and `0x1f` for the CDJ-3000\n * and XDJ-XZ (the nexus value plus bit 4). Firmware (CDJ-3000 FW3.20, one\n * 16-bit store in `sub_d858b8`) hardcodes `0x1f`. The byte is version-gated:\n * a player zeroes it toward peers advertising a Pro DJ Link protocol version\n * below 3, so a much older device on the link may report `0`.\n */\n deviceType: number;\n /**\n * A counter that increments for every status packet sent (packet byte 0xc8).\n *\n * Caveat: on the CDJ-3000 this field is hardwired to 0 — the firmware never\n * writes packet offset 0xc8 (verified: no store to the status body's +0xa4).\n * The CDJ-3000's live per-packet counter moved to its high-resolution stream\n * packet on UDP 50004 (a big-endian u32 at that packet's offset 0x28). Do not\n * rely on `packetNum` to detect liveness or drops on CDJ-3000 hardware.\n */\n packetNum: number;\n}\n\n/**\n * Absolute position information from CDJ-3000+ devices.\n * Sent every 30ms on port 50001 while a track is loaded.\n * Provides precise playhead position independent of beat grid.\n */\nexport interface PositionState {\n /**\n * The device ID sending this position update.\n */\n deviceId: number;\n /**\n * Track length in seconds (rounded down to nearest second).\n */\n trackLength: number;\n /**\n * Absolute playhead position in milliseconds.\n */\n playhead: number;\n /**\n * Pitch slider value as shown on screen.\n * For example, 3.26% is represented as 3.26.\n */\n pitch: number;\n /**\n * Effective BPM (track BPM adjusted by pitch) as shown on screen.\n * null if BPM is unknown.\n */\n bpm: number | null;\n}\n\n/**\n * On-Air status from DJM mixer.\n * Broadcast by the mixer to indicate which channels are currently audible.\n * Supports both 4-channel (DJM-900/1000) and 6-channel (DJM-V10) mixers.\n */\nexport interface OnAirStatus {\n /**\n * The mixer device ID (typically 33 / 0x21).\n */\n deviceId: number;\n /**\n * On-air flags for channels 1-4 (always present).\n * 0x00 = channel is off-air (silenced)\n * 0x01 = channel is on-air (audible)\n */\n channels: {\n 1: boolean;\n 2: boolean;\n 3: boolean;\n 4: boolean;\n 5?: boolean;\n 6?: boolean;\n };\n /**\n * Whether this is a 6-channel variant (CDJ-3000 + DJM-V10).\n * Determined by packet subtype (0x00 = 4-channel, 0x03 = 6-channel).\n */\n isSixChannel: boolean;\n}\n\n/**\n * State of a single mixer channel fader, EQ, trim and routing.\n */\nexport interface ChannelState {\n /**\n * Input Trim level (0-255).\n * Unity gain is typically 128 (0x80).\n */\n trim: number;\n /**\n * EQ High level (0-255).\n * Fully cut at 0, unity at 128 (0x80), boosted to max at 255.\n */\n eqHi: number;\n /**\n * EQ Mid level (0-255).\n * Fully cut at 0, unity at 128 (0x80), boosted to max at 255.\n */\n eqMid: number;\n /**\n * EQ Low level (0-255).\n * Fully cut at 0, unity at 128 (0x80), boosted to max at 255.\n */\n eqLow: number;\n /**\n * Color FX knob position (0-255).\n * Centered at 128 (0x80).\n */\n colorFx: number;\n /**\n * Channel fader position (0-255).\n * 0 is completely closed, 255 is maximum level.\n */\n fader: number;\n /**\n * Crossfader assignment.\n */\n crossfaderAssign: 'thru' | 'A' | 'B';\n}\n\n/**\n * Full mixer control state parsed from Stagehand unicast status (0x39 packets).\n */\nexport interface MixerState {\n /**\n * The reporting device ID (typically 33).\n */\n deviceId: number;\n /**\n * Device name reported by the mixer (e.g. \"DJM-A9\").\n */\n deviceName: string;\n /**\n * State of mixer channels (1-4).\n */\n channels: Record<number, ChannelState>;\n /**\n * Crossfader position (0-255).\n * 0 is full-left (A), 255 is full-right (B).\n */\n crossfader: number;\n}\n\n/**\n * A single audio VU level frame.\n */\nexport interface VUFrame {\n /**\n * Left channel level (0-65535).\n */\n left: number;\n /**\n * Right channel level (0-65535).\n */\n right: number;\n}\n\n/**\n * Real-time sliding window audio level VU data parsed from Stagehand unicast packets (0x58).\n */\nexport interface VUState {\n /**\n * The reporting device ID (typically 33).\n */\n deviceId: number;\n /**\n * Array of 15 sliding window stereo VU level frames per channel (1-4).\n */\n channels: Record<number, VUFrame[]>;\n}\n","import type {Address4} from 'ip-address';\n\nimport type {Playlist, Track} from './entities';\n\nexport * as CDJStatus from 'src/status/types';\n\n/**\n * Re-export various types for the types only compile target\n */\n\nexport type {TrackAnalysis} from './db/getTrackAnalysis';\nexport type {\n Album,\n Artist,\n Artwork,\n Color,\n Genre,\n Key,\n Label,\n Playlist,\n Track,\n} from './entities';\nexport type {HydrationProgress} from './localdb/rekordbox';\nexport type {MixstatusConfig, MixstatusProcessor} from './mixstatus';\n// Note: ProlinkNetwork is exported as a class from ./network, not re-exported here as type-only\n// to preserve method signatures like close()\nexport type {ConnectedProlinkNetwork, NetworkConfig} from './network';\nexport type {FetchProgress} from './nfs';\n\n/**\n * Known device types on the network\n */\nexport enum DeviceType {\n CDJ = 0x01,\n Mixer = 0x03,\n Rekordbox = 0x04,\n Stagehand = 0x05,\n}\n\n/**\n * The 8-bit identifier of the device on the network\n */\nexport type DeviceID = number;\n\n/**\n * Represents a device on the prolink network.\n */\nexport interface Device {\n name: string;\n id: DeviceID;\n type: DeviceType;\n macAddr: Uint8Array;\n ip: Address4;\n lastActive?: Date;\n}\n\n/**\n * Details of a particular media slot on the CDJ\n */\nexport interface MediaSlotInfo {\n /**\n * The device the slot physically exists on\n */\n deviceId: DeviceID;\n /**\n * The slot type\n */\n slot: MediaSlot;\n /**\n * The name of the media connected\n */\n name: string;\n /**\n * The rekordbox configured color of the media connected\n */\n color: MediaColor;\n /**\n * Creation date\n */\n createdDate: Date;\n /**\n * Number of free bytes available on the media\n */\n freeBytes: bigint;\n /**\n * Number of bytes used on the media\n */\n totalBytes: bigint;\n /**\n * Specifies the available tracks type on the media\n */\n tracksType: TrackType;\n /**\n * Total number of rekordbox tracks on the media. Will be zero if there is\n * no rekordbox database on the media\n */\n trackCount: number;\n /**\n * Same as track count, except for playlists\n */\n playlistCount: number;\n /**\n * True when a rekordbox 'my settings' file has been exported to the media\n */\n hasSettings: boolean;\n}\n\nexport enum MediaColor {\n Default = 0x00,\n Pink = 0x01,\n Red = 0x02,\n Orange = 0x03,\n Yellow = 0x04,\n Green = 0x05,\n Aqua = 0x06,\n Blue = 0x07,\n Purple = 0x08,\n}\n\n/**\n * A slot where media is present on the CDJ\n */\nexport enum MediaSlot {\n Empty = 0x00,\n CD = 0x01,\n SD = 0x02,\n USB = 0x03,\n RB = 0x04,\n Unknown05 = 0x05, // Possibly TIDAL, Apple Music, or other streaming\n StreamingDirectPlay = 0x06,\n Unknown07 = 0x07, // Possibly TIDAL, Apple Music, or other streaming\n Unknown08 = 0x08, // Possibly TIDAL, Apple Music, or other streaming\n Beatport = 0x09,\n}\n\n/**\n * Track type flags\n */\nexport enum TrackType {\n None = 0x00,\n RB = 0x01,\n Unanalyzed = 0x02,\n AudioCD = 0x05,\n Streaming = 0x06,\n}\n\n/**\n * A beat grid is a series of offsets from the start of the track. Each offset\n * indicates what count within the measure it is along with the BPM.\n */\nexport type BeatGrid = Array<{\n /**\n * Offset from the beginning of track in milliseconds of this beat.\n */\n offset: number;\n /**\n * The count of this particular beat within the measure\n */\n count: 1 | 2 | 3 | 4;\n /**\n * The BPM at this beat.\n */\n bpm: number;\n}>;\n\n/**\n * A waveform segment contains a height and 'whiteness' value.\n */\ninterface WaveformSegment {\n /**\n * The height this segment in the waveform. Ranges from 0 - 31.\n */\n height: number;\n /**\n * The level of \"whiteness\" of the waveform. 0 being completely blue, and 1\n * being completely white.\n */\n whiteness: number;\n}\n\n/**\n * A HD waveform segment contains the height of the waveform, and it's color\n * represented as RGB values.\n */\ninterface WaveformHDSegment {\n /**\n * The height this segment in the waveform. Ranges from 0 - 31.\n */\n height: number;\n /**\n * the RGB value, each channel ranges from 0-1 for the segment.\n */\n color: [number, number, number];\n}\n\n/**\n * The waveform preview will be 400 segments of data.\n */\nexport type WaveformPreview = WaveformSegment[];\n\n/**\n * Detailed waveforms have 150 segments per second of audio (150 'half frames'\n * per second of audio).\n */\nexport type WaveformDetailed = WaveformSegment[];\n\n/**\n * HD waveforms have 150 segments per second of audio (150 'half frames' per\n * second of audio).\n */\nexport type WaveformHD = WaveformHDSegment[];\n\n/**\n * The result of looking up track waveforms\n */\nexport interface Waveforms {\n /**\n * The full-size and full-color waveform\n */\n waveformHd: WaveformHD;\n\n /**\n * Color waveform preview (PWV4 tag).\n * Raw bytes: 1200 columns × 6 bytes per column (3 frequency bands × 2 bytes each).\n */\n waveformColorPreview?: Uint8Array;\n\n /**\n * Standard waveform preview (400 entries). Available for streaming tracks.\n */\n waveformPreview?: WaveformPreview;\n\n /**\n * Full detailed waveform. Available for streaming tracks.\n */\n waveformDetailed?: WaveformDetailed;\n}\n\n/**\n * Re-export cue types from onelibrary-connect\n */\nexport type {CueAndLoop, CuePoint, Hotcue, Hotloop, Loop} from 'onelibrary-connect';\nexport {CueColor, HotcueButton} from 'onelibrary-connect';\n\n/**\n * Extended cue with color and comment support (PCO2 tag from rekordbox).\n * Includes additional metadata like RGB colors, comments, and quantized loop information.\n */\nexport interface ExtendedCue {\n /**\n * Hot cue number (0 for memory points, 1-8 for hot cues A-H)\n */\n hotCue: number;\n /**\n * Type of cue: 1 = simple position/cue, 2 = loop\n */\n type: 1 | 2;\n /**\n * Position in milliseconds from the start of the track\n */\n time: number;\n /**\n * For loops, the end position in milliseconds\n */\n loopTime?: number;\n /**\n * Color ID referencing the color table (for memory points/loops)\n */\n colorId?: number;\n /**\n * Color code for the hot cue palette (0x00 = default green, 0x01-0x3e = palette colors)\n */\n colorCode?: number;\n /**\n * RGB color values used to illuminate the player's RGB LEDs\n */\n colorRgb?: {r: number; g: number; b: number};\n /**\n * User-assigned comment text for the cue\n */\n comment?: string;\n /**\n * For quantized loops, the numerator of the loop size fraction (e.g., 4 for a 4-beat loop)\n */\n loopNumerator?: number;\n /**\n * For quantized loops, the denominator of the loop size fraction (e.g., 1 for a 4-beat loop)\n */\n loopDenominator?: number;\n}\n\n/**\n * A phrase within a track's song structure\n */\nexport interface Phrase {\n /**\n * Sequential phrase number starting from 1\n */\n index: number;\n /**\n * Beat number where this phrase begins\n */\n beat: number;\n /**\n * Raw phrase kind value from rekordbox\n */\n kind: number;\n /**\n * Human-readable phrase type (e.g., \"Intro\", \"Verse 1\", \"Chorus\")\n */\n phraseType: string;\n /**\n * Whether this phrase has a fill-in section (non-zero if present)\n */\n fill?: number;\n /**\n * Beat number where the fill-in begins (if present)\n */\n fillBeat?: number;\n}\n\n/**\n * Song structure / phrase analysis (PSSI tag from rekordbox).\n * Used by CDJ-3000 players for phrase-based navigation and lighting control.\n */\nexport interface SongStructure {\n /**\n * Overall mood classification of the track\n */\n mood: 'high' | 'mid' | 'low';\n /**\n * Stylistic bank assigned for lighting control\n */\n bank:\n | 'default'\n | 'cool'\n | 'natural'\n | 'hot'\n | 'subtle'\n | 'warm'\n | 'vivid'\n | 'club_1'\n | 'club_2';\n /**\n * Beat number where the last phrase ends (track may continue after this)\n */\n endBeat: number;\n /**\n * List of identified phrases in the track\n */\n phrases: Phrase[];\n}\n\n/**\n * 3-band color waveform preview (PWV6 tag from .2EX files).\n * Same resolution as PWV4 (typically 1200 entries) but with separate\n * low, mid, and high frequency band amplitudes.\n *\n * @see https://djl-analysis.deepsymmetry.org/djl-analysis/track-metadata.html#color-3band-preview-waveform\n */\nexport interface Waveform3BandPreview {\n numEntries: number;\n /** Raw interleaved bytes: numEntries × 3 (low, mid, high per entry) */\n data: Uint8Array;\n}\n\n/**\n * 3-band color detail waveform (PWV7 tag from .2EX files).\n * Higher resolution than PWV6, approximately 150 entries per second.\n *\n * @see https://djl-analysis.deepsymmetry.org/djl-analysis/track-metadata.html#color-3band-detail-waveform\n */\nexport interface Waveform3BandDetail {\n numEntries: number;\n /** Raw interleaved bytes: numEntries × 3 (low, mid, high per entry) */\n data: Uint8Array;\n}\n\n/**\n * Vocal detection configuration (PWVC tag from .2EX files).\n * Threshold values used to classify frequency content as vocal or non-vocal.\n *\n * Values are u16 but observed range across 192 real files is 80-159,\n * matching the 0-255 byte scale used by waveform band values.\n * Observed ranges: low 80-114, mid 80-146, high 98-159.\n */\nexport interface VocalConfig {\n thresholdLow: number;\n thresholdMid: number;\n thresholdHigh: number;\n}\n\n/**\n * Monochrome waveform preview data (PWAV/PWV2 tags).\n * PWAV contains 400 bytes, PWV2 contains 100 bytes.\n */\nexport interface WaveformPreviewData {\n /**\n * Raw waveform data - each byte encodes height and whiteness\n */\n data: Uint8Array;\n}\n\n/**\n * Represents the contents of a playlist\n */\nexport interface PlaylistContents {\n /**\n * The playlists in this playlist.\n */\n playlists: Playlist[];\n /**\n * The folders in this playlist.\n */\n folders: Playlist[];\n /**\n * The tracks in this playlist. This is an AsyncIterator as looking up track\n * metadata may be slow when connected to the remote database.\n */\n tracks: AsyncIterable<Track>;\n /**\n * The total number of tracks in this playlist.\n */\n totalTracks: number;\n}\n\nexport enum NetworkState {\n /**\n * The network is offline when we don't have an open connection to the network\n * (no connection to the announcement and or status UDP socket is present).\n */\n Offline,\n /**\n * The network is online when we have opened sockets to the network, but have\n * not yet started announcing ourselves as a virtual CDJ.\n */\n Online,\n /**\n * The network is connected once we have heard from another device on the network\n */\n Connected,\n /**\n * The network may have failed to connect if we aren't able to open the\n * announcement and or status UDP socket.\n */\n Failed,\n}\n\n/**\n * Mixstatus reporting modes specify how the mixstatus processor will determine when a new\n * track is 'now playing'.\n */\nexport enum MixstatusMode {\n /**\n * Tracks will be smartly marked as playing following rules:\n *\n * - The track that has been in the play state with the CDJ in the \"on air\" state\n * for the longest period of time (allowing for a configurable length of\n * interruption with allowedInterruptBeats) is considered to be the active\n * track that incoming tracks will be compared against.\n *\n * - A incoming track will immediately be reported as nowPlaying if it is on\n * air, playing, and the last active track has been cued.\n *\n * - A incoming track will be reported as nowPlaying if the active track has\n * not been on air or has not been playing for the configured\n * allowedInterruptBeats.\n *\n * - A incoming track will be reported as nowPlaying if it has played\n * consecutively (with allowedInterruptBeats honored for the incoming track)\n * for the configured beatsUntilReported.\n */\n SmartTiming,\n /**\n * Tracks will not be reported after the beatsUntilReported AND will ONLY\n * be reported if the other track has gone into a non-playing play state, or\n * taken off air (when useOnAirStatus is enabled).\n */\n WaitsForSilence,\n /**\n * The track will simply be reported only after the player becomes master.\n */\n FollowsMaster,\n}\n","module.exports = require(\"onelibrary-connect\");","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Check if module exists (development only)\n\tif (__webpack_modules__[moduleId] === undefined) {\n\t\tvar e = new Error(\"Cannot find module '\" + moduleId + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(\"./src/types.ts\");\n",""],"names":[],"ignoreList":[],"sourceRoot":""}
|
|
@@ -8,8 +8,13 @@ type DeviceID = number;
|
|
|
8
8
|
/**
|
|
9
9
|
* The highest device ID a CDJ will answer remotedb queries from.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* This limit applies to the device-ID byte inside the remotedb protocol (the
|
|
12
|
+
* Introduce message and every query header), NOT to the ID we announce on the
|
|
13
|
+
* network. Hardware-verified on a CDJ-3000 (2026-08-30, two players + DJM-V5,
|
|
14
|
+
* announced as VCDJ 7 throughout): queries carrying 1-6 are all answered —
|
|
15
|
+
* including IDs of absent players and the target's own ID — while 7 and above
|
|
16
|
+
* are silently ignored (the TCP connection and Introduce succeed, the query
|
|
17
|
+
* never gets a response). See {@link pickRemoteDbQueryId}.
|
|
13
18
|
*/
|
|
14
19
|
export declare const REMOTEDB_MAX_DEVICE_ID = 6;
|
|
15
20
|
/**
|
|
@@ -55,13 +60,15 @@ export interface DeviceLike {
|
|
|
55
60
|
export declare function playerNumberCeiling(devices: Iterable<DeviceLike>): number | undefined;
|
|
56
61
|
export interface PickDeviceIdOptions {
|
|
57
62
|
/**
|
|
58
|
-
* Prefer an ID in the 1-6 range,
|
|
59
|
-
* remotedb metadata
|
|
60
|
-
*
|
|
63
|
+
* Prefer an ID in the 1-6 range, the range real players number themselves
|
|
64
|
+
* in. Purely conventional: remotedb metadata no longer depends on our
|
|
65
|
+
* announced ID ({@link pickRemoteDbQueryId} chooses the in-protocol query
|
|
66
|
+
* ID independently), but sitting where gear expects players keeps us
|
|
67
|
+
* legible on link displays and avoids surprising other tooling.
|
|
61
68
|
*
|
|
62
69
|
* @default false
|
|
63
70
|
*/
|
|
64
|
-
|
|
71
|
+
preferPlayerRange?: boolean;
|
|
65
72
|
/**
|
|
66
73
|
* The highest player number the rig can hand out, from
|
|
67
74
|
* {@link playerNumberCeiling}. The search starts just above it, so the
|
|
@@ -83,5 +90,27 @@ export interface PickDeviceIdOptions {
|
|
|
83
90
|
* Returns null when every ID from 1 to 32 is occupied, in which case there is
|
|
84
91
|
* no safe ID and the caller must not join the network.
|
|
85
92
|
*/
|
|
86
|
-
export declare function pickAvailableDeviceId(usedIds: Iterable<DeviceID>, {
|
|
93
|
+
export declare function pickAvailableDeviceId(usedIds: Iterable<DeviceID>, { preferPlayerRange, playerCeiling }?: PickDeviceIdOptions): DeviceID | null;
|
|
94
|
+
/** The fields {@link pickRemoteDbQueryId} reads off a discovered device. */
|
|
95
|
+
export interface QueryIdDeviceLike {
|
|
96
|
+
id: DeviceID;
|
|
97
|
+
type: number;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Choose the device ID to carry inside remotedb messages when querying a CDJ.
|
|
101
|
+
*
|
|
102
|
+
* CDJs only answer remotedb queries whose in-protocol device-ID byte is in
|
|
103
|
+
* 1-6, but that byte is independent of the ID we announce on the network — so
|
|
104
|
+
* a virtual CDJ announced safely above the player range (say 7 behind a
|
|
105
|
+
* DJM-V10) can still query metadata by carrying an in-range ID here. Verified
|
|
106
|
+
* on a CDJ-3000 (2026-08-30): absent IDs and even the target's own ID are
|
|
107
|
+
* answered; 7+ is silently dropped.
|
|
108
|
+
*
|
|
109
|
+
* Older players are documented (dysentery, nexus era) as stricter: the byte
|
|
110
|
+
* had to be 1-4, belong to a player actually on the network, and not be the
|
|
111
|
+
* player being queried. The preference order below satisfies those rules
|
|
112
|
+
* whenever the rig makes it possible, then falls back through choices newer
|
|
113
|
+
* players accept.
|
|
114
|
+
*/
|
|
115
|
+
export declare function pickRemoteDbQueryId(targetId: DeviceID, devices: Iterable<QueryIdDeviceLike>): DeviceID;
|
|
87
116
|
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Socket } from 'dgram';
|
|
2
|
+
import DeviceManager from "../devices";
|
|
3
|
+
import { type Logger } from "../logger";
|
|
4
|
+
import { Device } from "../types";
|
|
5
|
+
/**
|
|
6
|
+
* Cadence of the Stagehand unicast keep-alive, in milliseconds.
|
|
7
|
+
*
|
|
8
|
+
* The real iOS Stagehand app heartbeats every discovered player and the mixer
|
|
9
|
+
* at ~4 Hz (250ms median inter-packet gap in captured traffic). This is the
|
|
10
|
+
* stream that keeps AlphaTheta hardware unicasting live state (`0x39` mixer
|
|
11
|
+
* fader/EQ, `0x58` VU on the mixer; `0x69` slim status, waveform families on
|
|
12
|
+
* the CDJs) to our IP — subnet-broadcast presence alone (the `0x06` keep-alive)
|
|
13
|
+
* is not enough to bootstrap it.
|
|
14
|
+
*/
|
|
15
|
+
export declare const STAGEHAND_HEARTBEAT_INTERVAL = 250;
|
|
16
|
+
/**
|
|
17
|
+
* Build the Stagehand mixer keep-alive (`0x3a`, 40 bytes).
|
|
18
|
+
*
|
|
19
|
+
* Verified byte-for-byte against the real iOS Stagehand app unicasting to a
|
|
20
|
+
* DJM-A9 at 4 Hz (Phase 4/5 captures §4.14.2). The 10-byte trailer
|
|
21
|
+
* `21 02 00 fe 00 04 00 1c 00 00` is constant across every observed sample —
|
|
22
|
+
* byte 33 (`0xfe`) is a VCDJ sentinel, not our runtime device number, so no
|
|
23
|
+
* per-device bytes vary here.
|
|
24
|
+
*/
|
|
25
|
+
export declare function makeStagehandMixerHeartbeat(vcdj: Device): Uint8Array;
|
|
26
|
+
/**
|
|
27
|
+
* Build the Stagehand player keep-alive (`0x68`, 36 bytes).
|
|
28
|
+
*
|
|
29
|
+
* Verified byte-for-byte against the real iOS Stagehand app unicasting to a
|
|
30
|
+
* CDJ-3000 at ~2-4 Hz. Body (offsets 30-35) is `03 01 00 3a 00 00`: `0x03`
|
|
31
|
+
* persona/channel, `0x01` device subkind, `0x00` flag, `0x3a` Stagehand
|
|
32
|
+
* model-code stamp, two reserved bytes. Constant across every observed sample.
|
|
33
|
+
*/
|
|
34
|
+
export declare function makeStagehandPlayerHeartbeat(vcdj: Device): Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* Unicasts Stagehand keep-alive frames to every discovered player and mixer so
|
|
37
|
+
* that AlphaTheta hardware begins (and keeps) pushing live state to our IP.
|
|
38
|
+
*
|
|
39
|
+
* The {@link StagehandAnnouncer} makes us *visible* on the network via subnet
|
|
40
|
+
* broadcast; this heartbeat is what makes hardware actually *talk back*. It
|
|
41
|
+
* targets the mixer with `0x3a` and each CDJ with `0x68`, mirroring the real
|
|
42
|
+
* iOS Stagehand app. Frames are sent from the status socket (port 50002) to
|
|
43
|
+
* each device's port 50002, which is where their unicast state replies land and
|
|
44
|
+
* where {@link StatusEmitter} listens for them.
|
|
45
|
+
*/
|
|
46
|
+
export declare class StagehandHeartbeat {
|
|
47
|
+
#private;
|
|
48
|
+
constructor(vcdj: Device, statusSocket: Socket, deviceManager: DeviceManager, logger?: Logger);
|
|
49
|
+
start(): void;
|
|
50
|
+
stop(): void;
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|