@viji-dev/core 0.3.26 → 0.3.27

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/dist/index.d.ts CHANGED
@@ -62,6 +62,33 @@ export declare interface AudioAPI {
62
62
  getWaveform: () => Float32Array;
63
63
  }
64
64
 
65
+ declare interface AudioStreamAPI {
66
+ isConnected: boolean;
67
+ volume: {
68
+ current: number;
69
+ peak: number;
70
+ smoothed: number;
71
+ };
72
+ bands: {
73
+ low: number;
74
+ lowMid: number;
75
+ mid: number;
76
+ highMid: number;
77
+ high: number;
78
+ lowSmoothed: number;
79
+ lowMidSmoothed: number;
80
+ midSmoothed: number;
81
+ highMidSmoothed: number;
82
+ highSmoothed: number;
83
+ };
84
+ spectral: {
85
+ brightness: number;
86
+ flatness: number;
87
+ };
88
+ getFrequencyData: () => Uint8Array;
89
+ getWaveform: () => Float32Array;
90
+ }
91
+
65
92
  /**
66
93
  * Manages audio analysis and processing for the host side.
67
94
  * Handles audio stream processing and provides real-time audio analysis.
@@ -80,24 +107,17 @@ export declare interface AudioAPI {
80
107
  */
81
108
  export declare class AudioSystem {
82
109
  private audioContext;
83
- private analyser;
84
- private mediaStreamSource;
85
- private currentStream;
86
- private analysisMode;
87
- private workletNode;
88
- private workletReady;
89
110
  private workletRegistered;
90
- private currentSampleRate;
91
- private bandNoiseFloor;
111
+ private mainChannel;
112
+ private additionalChannels;
113
+ private analysisInterval;
114
+ private analysisIntervalMs;
115
+ private stalenessTimer;
116
+ private static readonly STALENESS_THRESHOLD_MS;
92
117
  /** Tracks the last non-zero BPM chosen for output to avoid dropping to 0 */
93
118
  private lastNonZeroBpm;
94
119
  /** Tracks which source provided the current BPM (pll | tempo | carry | default) */
95
120
  private lastBpmSource;
96
- private workletFrameCount;
97
- private lastFrameTime;
98
- private stalenessTimer;
99
- private static readonly STALENESS_THRESHOLD_MS;
100
- private analysisTicks;
101
121
  private lastPhaseLogTime;
102
122
  private onsetLogBuffer;
103
123
  private debugMode;
@@ -108,18 +128,13 @@ export declare class AudioSystem {
108
128
  private pll;
109
129
  private stateManager;
110
130
  private lastKickDetected;
111
- private volumeAutoGain;
112
- private bandAutoGain;
113
- /** Raw band snapshot before auto-gain (for debug) */
131
+ /** Raw band snapshot before auto-gain (for debug, main channel only) */
114
132
  private rawBandsPreGain;
115
- /** Last dt used in analysis loop (ms) */
116
- private lastDtMs;
117
- /** Flag to track if we've logged detection method (to avoid spam) */
118
133
  private readonly bandNames;
119
134
  private essentiaBandHistories;
120
135
  private readonly essentiaHistoryWindowMs;
121
136
  private onsetTapManager;
122
- private envelopeFollowers;
137
+ private beatEnvelopeFollowers;
123
138
  private beatDetectionEnabled;
124
139
  private onsetDetectionEnabled;
125
140
  private autoGainEnabled;
@@ -139,29 +154,42 @@ export declare class AudioSystem {
139
154
  private getEssentiaBandHistory;
140
155
  private getEssentiaAllHistories;
141
156
  /**
142
- * Handle frames pushed from AudioWorklet
157
+ * Handle frames pushed from AudioWorklet for main channel
158
+ */
159
+ private handleMainWorkletFrame;
160
+ /**
161
+ * Handle frames pushed from AudioWorklet for an additional channel
162
+ */
163
+ private handleAdditionalWorkletFrame;
164
+ /**
165
+ * Common analysis pipeline for any channel (FFT, volume, bands, auto-gain, spectral, smoothing).
166
+ * Returns raw band energies (pre-auto-gain) for optional beat processing by the caller.
167
+ */
168
+ private analyzeChannelFrame;
169
+ /**
170
+ * Full analysis for the main channel (common analysis + beat pipeline).
143
171
  */
144
- private handleWorkletFrame;
172
+ private analyzeMainFrame;
145
173
  /**
146
- * Unified analysis pipeline (worklet and analyser paths)
174
+ * Lightweight analysis for additional/device channels (no beat detection).
147
175
  */
148
- private analyzeFrame;
176
+ private analyzeAdditionalFrame;
149
177
  /**
150
- * Compute FFT and derived arrays
178
+ * Compute FFT on a specific channel's buffers
151
179
  */
152
- private computeFFT;
180
+ private computeChannelFFT;
153
181
  /**
154
182
  * Calculate RMS/peak from float PCM frame (-1..1)
155
183
  */
156
184
  private calculateVolumeMetrics;
157
185
  /**
158
- * Calculate perceptual/log-ish bands (pre-gain), normalized by max magnitude
186
+ * Calculate perceptual/log-ish bands on a specific channel
159
187
  */
160
- private calculateFrequencyBandsFromMagnitude;
188
+ private calculateChannelBands;
161
189
  /**
162
- * Spectral features from magnitude spectrum
190
+ * Spectral features on a specific channel
163
191
  */
164
- private calculateSpectralFeaturesFromMagnitude;
192
+ private calculateChannelSpectral;
165
193
  /**
166
194
  * Run onset + beat detection pipeline and return BeatState
167
195
  */
@@ -171,28 +199,9 @@ export declare class AudioSystem {
171
199
  */
172
200
  private debugLog;
173
201
  private fftSize;
174
- private analysisInterval;
175
- private analysisIntervalMs;
176
- private frequencyData;
177
- private timeDomainData;
178
- private fftEngine;
179
- private fftInput;
180
- private fftOutput;
181
- private fftMagnitude;
182
- private fftMagnitudeDb;
183
- private fftPhase;
184
- private hannWindow;
185
- private audioState;
186
- private lastWaveformFrame;
187
- private analysisLoopId;
188
- private isAnalysisRunning;
189
- private lastAnalysisTimestamp;
202
+ private audioStateBeat;
190
203
  private sendAnalysisResults;
191
204
  constructor(sendAnalysisResultsCallback?: (data: any) => void);
192
- /**
193
- * Prepare FFT buffers and windowing for the selected fftSize
194
- */
195
- private refreshFFTResources;
196
205
  /**
197
206
  * Get the current audio analysis state (for host-side usage)
198
207
  */
@@ -215,20 +224,6 @@ export declare class AudioSystem {
215
224
  highMidSmoothed: number;
216
225
  highSmoothed: number;
217
226
  };
218
- beat: {
219
- kick: number;
220
- snare: number;
221
- hat: number;
222
- any: number;
223
- kickSmoothed: number;
224
- snareSmoothed: number;
225
- hatSmoothed: number;
226
- anySmoothed: number;
227
- events: BeatEvent[];
228
- bpm: number;
229
- confidence: number;
230
- isLocked: boolean;
231
- };
232
227
  spectral: {
233
228
  brightness: number;
234
229
  flatness: number;
@@ -244,37 +239,49 @@ export declare class AudioSystem {
244
239
  */
245
240
  waitForEssentia(timeoutMs?: number): Promise<boolean>;
246
241
  /**
247
- * Handle audio stream update (called from VijiCore)
242
+ * Handle audio stream update for main stream (called from VijiCore)
248
243
  */
249
244
  handleAudioStreamUpdate(data: {
250
245
  audioStream?: MediaStream | null;
251
246
  timestamp: number;
252
247
  }): void;
253
248
  /**
254
- * Set the audio stream for analysis
249
+ * Ensure the shared AudioContext is created and resumed.
250
+ */
251
+ private ensureAudioContext;
252
+ /**
253
+ * Connect a channel to Web Audio nodes (source, worklet/analyser).
254
+ * Used for both main and additional channels.
255
+ */
256
+ private connectChannel;
257
+ /**
258
+ * Set the main audio stream for analysis (preserves original public API surface).
255
259
  */
256
260
  private setAudioStream;
257
261
  /**
258
- * Disconnect current audio stream and clean up resources
262
+ * Disconnect the main audio stream (does NOT close AudioContext -- additional channels may still be active).
259
263
  */
260
- private disconnectAudioStream;
264
+ private disconnectMainStream;
261
265
  /**
262
- * Initialize audio worklet for high-quality capture (complex STFT path)
266
+ * Set up an AudioWorklet node for a specific channel.
267
+ * Registers the processor module once (shared), creates a new WorkletNode per channel.
263
268
  */
264
- private setupAudioWorklet;
269
+ private setupChannelWorklet;
265
270
  /**
266
- * Start the audio analysis loop at high speed (8ms intervals for transient capture)
271
+ * Ensure the shared analysis interval is running (for analyser-mode channels).
267
272
  */
268
- private startAnalysisLoop;
273
+ private ensureAnalysisLoop;
269
274
  /**
270
- * Stop the audio analysis loop
275
+ * Stop the shared analysis loop.
271
276
  */
272
277
  private stopAnalysisLoop;
278
+ /**
279
+ * Shared staleness timer: checks all channels for stale data.
280
+ */
273
281
  private startStalenessTimer;
274
282
  private stopStalenessTimer;
275
283
  /**
276
284
  * Pause audio analysis (for tests or temporary suspension)
277
- * The setInterval continues but performAnalysis() exits early
278
285
  */
279
286
  pauseAnalysis(): void;
280
287
  /**
@@ -282,31 +289,47 @@ export declare class AudioSystem {
282
289
  */
283
290
  resumeAnalysis(): void;
284
291
  /**
285
- * Perform audio analysis (called every frame)
286
- * Uses industry-grade 4-layer architecture:
287
- * Layer 1: MultiOnsetDetection (per-band onset detection)
288
- * Layer 2: TempoInduction (dual-method BPM detection)
289
- * Layer 3: PhaseLockedLoop (stable phase tracking)
290
- * Layer 4: BeatStateManager (state machine + confidence)
292
+ * Shared analysis loop callback: iterates main + additional channels in analyser mode.
291
293
  */
292
294
  private performAnalysis;
293
- private applyAutoGain;
295
+ private applyChannelAutoGain;
294
296
  /**
295
- * Update smooth versions of frequency bands
297
+ * Update smooth versions of frequency bands on a specific channel
296
298
  */
297
- private updateSmoothBands;
299
+ private updateChannelSmoothBands;
298
300
  /**
299
- * Send analysis results to worker
301
+ * Send analysis results for a specific channel to the worker.
302
+ * Tags with streamIndex and conditionally includes beat data (main channel only).
300
303
  */
301
- private sendAnalysisResultsToWorker;
304
+ private sendChannelResults;
302
305
  /**
303
- * Reset audio values to defaults
306
+ * Reset audio values to defaults (main channel + beat state)
304
307
  */
305
308
  private resetAudioValues;
306
309
  /**
307
- * Reset all audio state (called when destroying)
310
+ * Reset all audio state (called when destroying).
311
+ * Disconnects all channels, closes AudioContext, resets all modules.
308
312
  */
309
313
  resetAudioState(): void;
314
+ /**
315
+ * Add an additional audio stream (lightweight analysis, no beat detection).
316
+ * @param streamIndex Global stream index (from VijiCore: AUDIO_ADDITIONAL_BASE + n or AUDIO_DEVICE_BASE + n)
317
+ * @param stream The MediaStream to analyze
318
+ */
319
+ addStream(streamIndex: number, stream: MediaStream): Promise<void>;
320
+ /**
321
+ * Remove an additional audio stream.
322
+ */
323
+ removeStream(streamIndex: number): void;
324
+ /**
325
+ * Tear down all additional channels and rebuild with corrected indices.
326
+ * Mirrors video's reinitializeAdditionalCoordinators pattern.
327
+ */
328
+ reinitializeAdditionalChannels(streams: MediaStream[], baseIndex: number): Promise<void>;
329
+ /**
330
+ * Get the number of additional audio channels.
331
+ */
332
+ getChannelCount(): number;
310
333
  /**
311
334
  * Record a tap for the specified instrument onset
312
335
  */
@@ -675,6 +698,8 @@ declare interface DeviceState extends DeviceSensorState {
675
698
  name: string;
676
699
  /** Device camera video (null if not available) */
677
700
  video: VideoAPI | null;
701
+ /** Device audio stream (null if not available) */
702
+ audio: AudioStreamAPI | null;
678
703
  }
679
704
 
680
705
  declare interface FaceBlendshapes {
@@ -1211,8 +1236,8 @@ export declare interface VideoAPI {
1211
1236
  /**
1212
1237
  * Video stream categorization type
1213
1238
  * - main: Primary video with CV processing (viji.video)
1214
- * - additional: Host-provided MediaStreams without CV (viji.streams[])
1215
- * - directFrame: Injected frames from compositor pipeline (viji.streams[], after additional)
1239
+ * - additional: Host-provided MediaStreams without CV (viji.videoStreams[])
1240
+ * - directFrame: Injected frames from compositor pipeline (viji.videoStreams[], after additional)
1216
1241
  * - device: Device-provided camera streams (device.video)
1217
1242
  */
1218
1243
  export declare type VideoStreamType = 'main' | 'additional' | 'directFrame' | 'device';
@@ -1229,7 +1254,8 @@ export declare interface VijiAPI {
1229
1254
  fps: number;
1230
1255
  audio: AudioAPI;
1231
1256
  video: VideoAPI;
1232
- streams: VideoAPI[];
1257
+ videoStreams: VideoAPI[];
1258
+ audioStreams: AudioStreamAPI[];
1233
1259
  mouse: MouseAPI;
1234
1260
  keyboard: KeyboardAPI;
1235
1261
  touches: TouchAPI;
@@ -1277,8 +1303,14 @@ export declare class VijiCore {
1277
1303
  private static readonly ADDITIONAL_STREAM_BASE;
1278
1304
  private static readonly DEVICE_VIDEO_BASE;
1279
1305
  private static readonly DIRECT_FRAME_BASE;
1306
+ private static readonly AUDIO_ADDITIONAL_BASE;
1307
+ private static readonly AUDIO_DEVICE_BASE;
1308
+ private static readonly MAX_ADDITIONAL_AUDIO_STREAMS;
1309
+ private static readonly MAX_ADDITIONAL_VIDEO_STREAMS;
1280
1310
  private videoStream;
1281
1311
  private videoStreams;
1312
+ private audioStreams;
1313
+ private deviceAudioStreamIndices;
1282
1314
  private mainVideoCoordinator;
1283
1315
  private additionalCoordinators;
1284
1316
  private directFrameSlots;
@@ -1550,7 +1582,7 @@ export declare class VijiCore {
1550
1582
  */
1551
1583
  getVideoStream(): MediaStream | null;
1552
1584
  /**
1553
- * Adds an additional video stream (no CV). Returns its index in viji.streams[].
1585
+ * Adds an additional video stream (no CV). Returns its index in viji.videoStreams[].
1554
1586
  */
1555
1587
  addVideoStream(stream: MediaStream): Promise<number>;
1556
1588
  /**
@@ -1561,6 +1593,32 @@ export declare class VijiCore {
1561
1593
  * Gets the number of additional video streams.
1562
1594
  */
1563
1595
  getVideoStreamCount(): number;
1596
+ /**
1597
+ * Add an additional audio stream for lightweight analysis (no beat detection).
1598
+ * @param stream MediaStream with audio tracks
1599
+ * @returns Index of the new stream (0-based within additional streams)
1600
+ * @throws VijiCoreError if limit reached (max 8)
1601
+ */
1602
+ addAudioStream(stream: MediaStream): Promise<number>;
1603
+ /**
1604
+ * Remove an additional audio stream by index.
1605
+ * Triggers re-indexing of remaining streams.
1606
+ */
1607
+ removeAudioStream(index: number): Promise<void>;
1608
+ /**
1609
+ * Gets the number of additional audio streams.
1610
+ */
1611
+ getAudioStreamCount(): number;
1612
+ /**
1613
+ * Set audio stream from an external device.
1614
+ * @param deviceId Device identifier (must be registered via addExternalDevice)
1615
+ * @param stream MediaStream with audio tracks from the device
1616
+ */
1617
+ setDeviceAudio(deviceId: string, stream: MediaStream): Promise<void>;
1618
+ /**
1619
+ * Clear audio stream from a device.
1620
+ */
1621
+ clearDeviceAudio(deviceId: string): Promise<void>;
1564
1622
  /**
1565
1623
  * Reinitializes all additional coordinators after array mutation.
1566
1624
  */
@@ -1850,6 +1908,8 @@ export declare interface VijiCoreConfig {
1850
1908
  videoStream?: MediaStream;
1851
1909
  /** Additional video input streams (no CV processing) */
1852
1910
  videoStreams?: MediaStream[];
1911
+ /** Additional audio input streams (lightweight analysis, no beat detection) */
1912
+ audioStreams?: MediaStream[];
1853
1913
  /** Disable input processing (for preview instances) */
1854
1914
  noInputs?: boolean;
1855
1915
  /** Enable user interaction events (DOM event capture, disabled in headless by default) */
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A, V, a, b } from "./index-B8BYfP9z.js";
1
+ import { A, V, a, b } from "./index-9YDi9UBP.js";
2
2
  export {
3
3
  A as AudioSystem,
4
4
  V as VERSION,