alphatheta-connect 0.19.2 → 0.21.1

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Re-exports from onelibrary-connect
3
3
  */
4
- export { getEncryptionKey, openOneLibraryDb, OneLibraryAdapter, } from 'onelibrary-connect';
5
4
  export type { Category, DeviceProperty, HistorySession, HotCueBankList, MenuItem, MyTag, SortOption, } from 'onelibrary-connect';
5
+ export { getEncryptionKey, OneLibraryAdapter, openOneLibraryDb } from 'onelibrary-connect';
package/lib/network.d.ts CHANGED
@@ -3,9 +3,9 @@ import { NetworkInterfaceInfoIPv4 } from 'os';
3
3
  import Control from "./control";
4
4
  import Database from "./db";
5
5
  import DeviceManager from "./devices";
6
- import { type Logger } from "./logger";
7
6
  import LocalDatabase from "./localdb";
8
7
  import { DatabasePreference } from "./localdb/database-adapter";
8
+ import { type Logger } from "./logger";
9
9
  import { MixstatusProcessor } from "./mixstatus";
10
10
  import RemoteDatabase from "./remotedb";
11
11
  import StatusEmitter from "./status";
@@ -17,7 +17,7 @@ export interface NetworkConfig {
17
17
  */
18
18
  iface: NetworkInterfaceInfoIPv4;
19
19
  /**
20
- * The ID of the virtual CDJ to pose as.
20
+ * The ID of the virtual CDJ or Stagehand device to pose as.
21
21
  *
22
22
  * IMPORTANT:
23
23
  *
@@ -34,13 +34,13 @@ export interface NetworkConfig {
34
34
  * band of the networks remote database protocol, and is not limited by this
35
35
  * restriction.
36
36
  */
37
- vcdjId: number;
37
+ vcdjId?: number;
38
38
  /**
39
- * The name to announce the virtual CDJ as on the network.
39
+ * The name to announce the virtual CDJ or Stagehand device as on the network.
40
40
  *
41
41
  * This name will appear in device lists on other Pro DJ Link equipment.
42
42
  *
43
- * @default 'ProLink-Connect'
43
+ * @default 'ProLink-Connect' (or 'Stagehand' when connectMethod is 'stagehand')
44
44
  */
45
45
  vcdjName?: string;
46
46
  /**
@@ -82,6 +82,14 @@ export interface NetworkConfig {
82
82
  * If not provided, logging is silently discarded.
83
83
  */
84
84
  logger?: Logger;
85
+ /**
86
+ * Connection method to use.
87
+ * - 'active': Actively join as a Virtual CDJ player.
88
+ * - 'stagehand': Actively join posing as a Pioneer Stagehand iOS app device.
89
+ *
90
+ * @default 'active'
91
+ */
92
+ connectMethod?: 'active' | 'stagehand';
85
93
  }
86
94
  interface ConstructOpts {
87
95
  config?: NetworkConfig;
@@ -108,6 +116,12 @@ export type ConnectedProlinkNetwork = ProlinkNetwork & {
108
116
  * This is the primary entrypoint for connecting to the prolink network.
109
117
  */
110
118
  export declare function bringOnline(config?: NetworkConfig): Promise<ProlinkNetwork>;
119
+ /**
120
+ * Brings the Prolink network online using the Pioneer Stagehand connection method.
121
+ *
122
+ * This connects to the network as a non-player Stagehand device using its abbreviated handshake.
123
+ */
124
+ export declare function bringOnlineStagehand(config?: Omit<NetworkConfig, 'connectMethod'>): Promise<ProlinkNetwork>;
111
125
  export declare class ProlinkNetwork {
112
126
  #private;
113
127
  /**
@@ -1,5 +1,3 @@
1
- import StrictEventEmitter from 'strict-event-emitter-types';
2
- import { EventEmitter } from 'events';
3
1
  import { CDJStatus, MediaSlotInfo } from "../types";
4
2
  import { PcapAdapter } from './pcap-adapter';
5
3
  interface StatusEvents {
@@ -15,8 +13,14 @@ interface StatusEvents {
15
13
  * Fired when the mixer broadcasts on-air channel status
16
14
  */
17
15
  onAir: (status: CDJStatus.OnAirStatus) => void;
16
+ /**
17
+ * Fired when the Stagehand-connected mixer reports fader/EQ/control positions.
18
+ * Part of the shared status event API (matching the active StatusEmitter) so
19
+ * the two are interchangeable to consumers; passive capture does not currently
20
+ * produce this event.
21
+ */
22
+ mixerState: (state: CDJStatus.MixerState) => void;
18
23
  }
19
- type Emitter = StrictEventEmitter<EventEmitter, StatusEvents>;
20
24
  /**
21
25
  * PassiveStatusEmitter reports CDJ status updates received via passive
22
26
  * packet capture instead of UDP sockets.
@@ -30,9 +34,9 @@ type Emitter = StrictEventEmitter<EventEmitter, StatusEvents>;
30
34
  export declare class PassiveStatusEmitter {
31
35
  #private;
32
36
  constructor(adapter: PcapAdapter);
33
- on: Emitter['on'];
34
- off: Emitter['off'];
35
- once: Emitter['once'];
37
+ on: <E extends keyof StatusEvents>(event: E, listener: StatusEvents[E]) => void;
38
+ off: <E extends keyof StatusEvents>(event: E, listener: StatusEvents[E]) => void;
39
+ once: <E extends keyof StatusEvents>(event: E, listener: StatusEvents[E]) => void;
36
40
  /**
37
41
  * Stop listening to the pcap adapter.
38
42
  */
@@ -65,7 +65,7 @@ export declare const UInt8: {
65
65
  data: Buffer;
66
66
  };
67
67
  type: NumberFieldType;
68
- bytesToRead: 1 | 2 | 4;
68
+ bytesToRead: 1 | 4 | 2;
69
69
  };
70
70
  /**
71
71
  * Field representing a UInt16
@@ -81,7 +81,7 @@ export declare const UInt16: {
81
81
  data: Buffer;
82
82
  };
83
83
  type: NumberFieldType;
84
- bytesToRead: 1 | 2 | 4;
84
+ bytesToRead: 1 | 4 | 2;
85
85
  };
86
86
  /**
87
87
  * Field representing a UInt32
@@ -97,7 +97,7 @@ export declare const UInt32: {
97
97
  data: Buffer;
98
98
  };
99
99
  type: NumberFieldType;
100
- bytesToRead: 1 | 2 | 4;
100
+ bytesToRead: 1 | 4 | 2;
101
101
  };
102
102
  /**
103
103
  * Field representing a null-terminated big endian UTF-16 string
@@ -131,7 +131,7 @@ declare const fieldMap: {
131
131
  data: Buffer;
132
132
  };
133
133
  type: NumberFieldType;
134
- bytesToRead: 1 | 2 | 4;
134
+ bytesToRead: 1 | 4 | 2;
135
135
  };
136
136
  readonly 16: {
137
137
  new (value: number | Buffer): {
@@ -144,7 +144,7 @@ declare const fieldMap: {
144
144
  data: Buffer;
145
145
  };
146
146
  type: NumberFieldType;
147
- bytesToRead: 1 | 2 | 4;
147
+ bytesToRead: 1 | 4 | 2;
148
148
  };
149
149
  readonly 17: {
150
150
  new (value: number | Buffer): {
@@ -157,7 +157,7 @@ declare const fieldMap: {
157
157
  data: Buffer;
158
158
  };
159
159
  type: NumberFieldType;
160
- bytesToRead: 1 | 2 | 4;
160
+ bytesToRead: 1 | 4 | 2;
161
161
  };
162
162
  readonly 20: typeof Binary;
163
163
  readonly 38: typeof String;
@@ -1,8 +1,8 @@
1
- import { TelemetrySpan as Span } from "../../utils/telemetry";
2
1
  import { PromiseReadable } from 'promise-readable';
3
2
  import { Field } from "../fields";
4
3
  import { responseTransform } from "./response";
5
4
  import { MessageType, Response } from "./types";
5
+ import { TelemetrySpan as Span } from "../../utils/telemetry";
6
6
  interface Options<T extends MessageType> {
7
7
  transactionId?: number;
8
8
  type: T;
@@ -1,6 +1,4 @@
1
- import StrictEventEmitter from 'strict-event-emitter-types';
2
1
  import { Socket } from 'dgram';
3
- import { EventEmitter } from 'events';
4
2
  import { CDJStatus, MediaSlotInfo } from "../types";
5
3
  import { makeMediaSlotRequest } from './media';
6
4
  interface StatusEvents {
@@ -16,8 +14,11 @@ interface StatusEvents {
16
14
  * Fired when the mixer broadcasts on-air channel status
17
15
  */
18
16
  onAir: (status: CDJStatus.OnAirStatus) => void;
17
+ /**
18
+ * Fired when the Stagehand-connected mixer reports fader/EQ/control positions
19
+ */
20
+ mixerState: (state: CDJStatus.MixerState) => void;
19
21
  }
20
- type Emitter = StrictEventEmitter<EventEmitter, StatusEvents>;
21
22
  type MediaSlotOptions = Parameters<typeof makeMediaSlotRequest>[0];
22
23
  /**
23
24
  * The status emitter will report every time a device status is received
@@ -28,9 +29,9 @@ declare class StatusEmitter {
28
29
  * @param statusSocket A UDP socket to receive CDJ status packets on
29
30
  */
30
31
  constructor(statusSocket: Socket);
31
- on: Emitter['on'];
32
- off: Emitter['off'];
33
- once: Emitter['once'];
32
+ on: <E extends keyof StatusEvents>(event: E, listener: StatusEvents[E]) => void;
33
+ off: <E extends keyof StatusEvents>(event: E, listener: StatusEvents[E]) => void;
34
+ once: <E extends keyof StatusEvents>(event: E, listener: StatusEvents[E]) => void;
34
35
  /**
35
36
  * Retrieve media slot status information.
36
37
  */
@@ -8,6 +8,10 @@ interface PositionEvents {
8
8
  * These packets are sent approximately every 30ms while a track is loaded.
9
9
  */
10
10
  position: (position: CDJStatus.PositionState) => void;
11
+ /**
12
+ * Fired when real-time VU levels are received from the mixer under Stagehand connection.
13
+ */
14
+ vu: (vu: CDJStatus.VUState) => void;
11
15
  }
12
16
  type Emitter = StrictEventEmitter<EventEmitter, PositionEvents>;
13
17
  /**
@@ -168,3 +168,90 @@ export interface OnAirStatus {
168
168
  */
169
169
  isSixChannel: boolean;
170
170
  }
171
+ /**
172
+ * State of a single mixer channel fader, EQ, trim and routing.
173
+ */
174
+ export interface ChannelState {
175
+ /**
176
+ * Input Trim level (0-255).
177
+ * Unity gain is typically 128 (0x80).
178
+ */
179
+ trim: number;
180
+ /**
181
+ * EQ High level (0-255).
182
+ * Fully cut at 0, unity at 128 (0x80), boosted to max at 255.
183
+ */
184
+ eqHi: number;
185
+ /**
186
+ * EQ Mid level (0-255).
187
+ * Fully cut at 0, unity at 128 (0x80), boosted to max at 255.
188
+ */
189
+ eqMid: number;
190
+ /**
191
+ * EQ Low level (0-255).
192
+ * Fully cut at 0, unity at 128 (0x80), boosted to max at 255.
193
+ */
194
+ eqLow: number;
195
+ /**
196
+ * Color FX knob position (0-255).
197
+ * Centered at 128 (0x80).
198
+ */
199
+ colorFx: number;
200
+ /**
201
+ * Channel fader position (0-255).
202
+ * 0 is completely closed, 255 is maximum level.
203
+ */
204
+ fader: number;
205
+ /**
206
+ * Crossfader assignment.
207
+ */
208
+ crossfaderAssign: 'thru' | 'A' | 'B';
209
+ }
210
+ /**
211
+ * Full mixer control state parsed from Stagehand unicast status (0x39 packets).
212
+ */
213
+ export interface MixerState {
214
+ /**
215
+ * The reporting device ID (typically 33).
216
+ */
217
+ deviceId: number;
218
+ /**
219
+ * Device name reported by the mixer (e.g. "DJM-A9").
220
+ */
221
+ deviceName: string;
222
+ /**
223
+ * State of mixer channels (1-4).
224
+ */
225
+ channels: Record<number, ChannelState>;
226
+ /**
227
+ * Crossfader position (0-255).
228
+ * 0 is full-left (A), 255 is full-right (B).
229
+ */
230
+ crossfader: number;
231
+ }
232
+ /**
233
+ * A single audio VU level frame.
234
+ */
235
+ export interface VUFrame {
236
+ /**
237
+ * Left channel level (0-65535).
238
+ */
239
+ left: number;
240
+ /**
241
+ * Right channel level (0-65535).
242
+ */
243
+ right: number;
244
+ }
245
+ /**
246
+ * Real-time sliding window audio level VU data parsed from Stagehand unicast packets (0x58).
247
+ */
248
+ export interface VUState {
249
+ /**
250
+ * The reporting device ID (typically 33).
251
+ */
252
+ deviceId: number;
253
+ /**
254
+ * Array of 15 sliding window stereo VU level frames per channel (1-4).
255
+ */
256
+ channels: Record<number, VUFrame[]>;
257
+ }
@@ -17,3 +17,12 @@ export declare function positionFromPacket(packet: Buffer): CDJStatus.PositionSt
17
17
  * - 6-channel: subtype 0x03, length 0x0011 (17 data bytes: F1 F2 F3 F4 00 00 00 00 00 F5 F6 00 30 00 00 00 00 00)
18
18
  */
19
19
  export declare function onAirFromPacket(packet: Buffer): CDJStatus.OnAirStatus | undefined;
20
+ /**
21
+ * Parse unicast mixer state packet from DJM-A9 (packet type 0x39 on port 50002).
22
+ */
23
+ export declare function mixerStateFromPacket(packet: Buffer): CDJStatus.MixerState | undefined;
24
+ /**
25
+ * Parse unicast VU meter packet from DJM-A9 (packet type 0x58 on port 50001).
26
+ * Contains 15 sample-tuples (16-bit BE left/right levels) per channel.
27
+ */
28
+ export declare function vuFromPacket(packet: Buffer): CDJStatus.VUState | undefined;
package/lib/types.d.ts CHANGED
@@ -4,8 +4,8 @@ export * as CDJStatus from "./status/types";
4
4
  /**
5
5
  * Re-export various types for the types only compile target
6
6
  */
7
- export type { Album, Artist, Artwork, Color, Genre, Key, Label, Playlist, Track, } from './entities';
8
7
  export type { TrackAnalysis } from './db/getTrackAnalysis';
8
+ export type { Album, Artist, Artwork, Color, Genre, Key, Label, Playlist, Track, } from './entities';
9
9
  export type { HydrationProgress } from './localdb/rekordbox';
10
10
  export type { MixstatusConfig, MixstatusProcessor } from './mixstatus';
11
11
  export type { ConnectedProlinkNetwork, NetworkConfig } from './network';
@@ -16,7 +16,8 @@ export type { FetchProgress } from './nfs';
16
16
  export declare enum DeviceType {
17
17
  CDJ = 1,
18
18
  Mixer = 3,
19
- Rekordbox = 4
19
+ Rekordbox = 4,
20
+ Stagehand = 5
20
21
  }
21
22
  /**
22
23
  * The 8-bit identifier of the device on the network
@@ -204,8 +205,8 @@ export interface Waveforms {
204
205
  /**
205
206
  * Re-export cue types from onelibrary-connect
206
207
  */
207
- export { CueColor, HotcueButton } from 'onelibrary-connect';
208
208
  export type { CueAndLoop, CuePoint, Hotcue, Hotloop, Loop } from 'onelibrary-connect';
209
+ export { CueColor, HotcueButton } from 'onelibrary-connect';
209
210
  /**
210
211
  * Extended cue with color and comment support (PCO2 tag from rekordbox).
211
212
  * Includes additional metadata like RGB colors, comments, and quantized loop information.
package/lib/types.js CHANGED
@@ -93,6 +93,7 @@ var DeviceType;
93
93
  DeviceType[DeviceType["CDJ"] = 1] = "CDJ";
94
94
  DeviceType[DeviceType["Mixer"] = 3] = "Mixer";
95
95
  DeviceType[DeviceType["Rekordbox"] = 4] = "Rekordbox";
96
+ DeviceType[DeviceType["Stagehand"] = 5] = "Stagehand";
96
97
  })(DeviceType || (exports.DeviceType = DeviceType = {}));
97
98
  var MediaColor;
98
99
  (function (MediaColor) {
@@ -133,9 +134,6 @@ var TrackType;
133
134
  TrackType[TrackType["AudioCD"] = 5] = "AudioCD";
134
135
  TrackType[TrackType["Streaming"] = 6] = "Streaming";
135
136
  })(TrackType || (exports.TrackType = TrackType = {}));
136
- /**
137
- * Re-export cue types from onelibrary-connect
138
- */
139
137
  var onelibrary_connect_1 = __webpack_require__(/*! onelibrary-connect */ "onelibrary-connect");
140
138
  Object.defineProperty(exports, "CueColor", ({ enumerable: true, get: function () { return onelibrary_connect_1.CueColor; } }));
141
139
  Object.defineProperty(exports, "HotcueButton", ({ enumerable: true, get: function () { return onelibrary_connect_1.HotcueButton; } }));
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,UAIX;AAJD,WAAY,UAAU;IACpB,yCAAU;IACV,6CAAY;IACZ,qDAAgB;AAClB,CAAC,EAJW,UAAU,0BAAV,UAAU,QAIrB;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;AA8FD;;GAEG;AACH,+FAA0D;AAAlD,uHAAQ;AAAE,+HAAY;AAyL9B,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;;;;;;;;;;;ACleD,+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","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 {\n Album,\n Artist,\n Artwork,\n Color,\n Genre,\n Key,\n Label,\n Playlist,\n Track,\n} from './entities';\nexport type {TrackAnalysis} from './db/getTrackAnalysis';\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}\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 {CueColor, HotcueButton} from 'onelibrary-connect';\nexport type {CueAndLoop, CuePoint, Hotcue, Hotloop, Loop} 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,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":""}
@@ -37,3 +37,4 @@ export declare class Announcer {
37
37
  start(): void;
38
38
  stop(): void;
39
39
  }
40
+ export * from './stagehand';
@@ -0,0 +1,51 @@
1
+ import { Socket } from 'dgram';
2
+ import { NetworkInterfaceInfoIPv4 } from 'os';
3
+ import DeviceManager from "../devices";
4
+ import { type Logger } from "../logger";
5
+ import { Device, DeviceID } from "../types";
6
+ export declare const STAGEHAND_STARTUP_INTERVAL = 305;
7
+ export declare const STAGEHAND_KEEP_ALIVE_INTERVAL = 2000;
8
+ export declare enum StagehandStartupStage {
9
+ InitialAnnounce = 10,
10
+ SecondStageClaim = 2,
11
+ KeepAlive = 6
12
+ }
13
+ /**
14
+ * Generates a randomized MAC address with the AlphaTheta OUI (c8:3d:fc).
15
+ */
16
+ export declare function generateStagehandMac(): Uint8Array;
17
+ /**
18
+ * Generates a random Stagehand device ID in the observed range of 141 to 211.
19
+ */
20
+ export declare function generateStagehandDeviceId(): number;
21
+ /**
22
+ * Constructs a virtual Stagehand Device.
23
+ *
24
+ * @param iface - The network interface to use
25
+ * @param id - The device ID to use (defaults to random Stagehand ID)
26
+ * @param name - The device name (defaults to 'Stagehand')
27
+ * @param macAddr - The optional randomized MAC address
28
+ */
29
+ export declare const getVirtualStagehand: (iface: NetworkInterfaceInfoIPv4, id?: DeviceID, name?: string, macAddr?: Uint8Array<ArrayBufferLike>) => Device;
30
+ /**
31
+ * Build Stagehand stage 0x0a packet: Initial announcement.
32
+ * Sent 3 times at 305ms intervals.
33
+ */
34
+ export declare function makeStagehand0aPacket(device: Device): Uint8Array;
35
+ /**
36
+ * Build Stagehand stage 0x02 packet: Second-stage device number claim.
37
+ * Sent 3 times at 305ms intervals with counter N (1, 2, 3).
38
+ */
39
+ export declare function makeStagehand02Packet(device: Device, mac: Uint8Array, counter: number): Uint8Array;
40
+ /**
41
+ * Build Stagehand stage 0x06 packet: Keep-alive.
42
+ * Sent every 2.0s after startup complete.
43
+ */
44
+ export declare function makeStagehand06Packet(device: Device, mac: Uint8Array): Uint8Array;
45
+ export declare class StagehandAnnouncer {
46
+ #private;
47
+ constructor(vcdj: Device, announceSocket: Socket, deviceManager: DeviceManager, iface: NetworkInterfaceInfoIPv4, logger?: Logger);
48
+ get ready(): Promise<void>;
49
+ start(): void;
50
+ stop(): void;
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alphatheta-connect",
3
- "version": "0.19.2",
3
+ "version": "0.21.1",
4
4
  "main": "lib/index.js",
5
5
  "author": "Evan Purkhiser <evanpurkhiser@gmail.com>",
6
6
  "contributors": [