/** * Audio Levels Store * Svelte 5 store for VU meter / audio level monitoring */ import type { IngestControllerV2 } from "@livepeer-frameworks/streamcrafter-core"; export interface AudioLevelsState { level: number; // Current level 0-1 peakLevel: number; // Peak level with decay 0-1 isMonitoring: boolean; } export interface AudioLevelsStore { subscribe: (fn: (state: AudioLevelsState) => void) => () => void; startMonitoring: () => void; stopMonitoring: () => void; destroy: () => void; } /** * Create an audio levels store for VU meter * * @example * ```svelte * * *
*
*
* ``` */ export function createAudioLevelsStore( getController: () => IngestControllerV2 | null, options: { autoStart?: boolean } = {} ): AudioLevelsStore { const { autoStart = true } = options; const state = $state({ level: 0, peakLevel: 0, isMonitoring: false, }); const subscribers = new Set<(state: AudioLevelsState) => void>(); let unsubscribeFromMixer: (() => void) | null = null; function notify() { const snapshot = { ...state }; subscribers.forEach((fn) => fn(snapshot)); } function startMonitoring() { const controller = getController(); if (!controller) return; const audioMixer = controller.getAudioMixer(); if (!audioMixer) return; // Subscribe to level updates unsubscribeFromMixer = audioMixer.on("levelUpdate", (event) => { state.level = event.level; state.peakLevel = event.peakLevel; notify(); }); // Start the audio mixer's level monitoring audioMixer.startLevelMonitoring(); state.isMonitoring = true; notify(); } function stopMonitoring() { const controller = getController(); if (controller) { const audioMixer = controller.getAudioMixer(); if (audioMixer) { audioMixer.stopLevelMonitoring(); } } if (unsubscribeFromMixer) { unsubscribeFromMixer(); unsubscribeFromMixer = null; } state.level = 0; state.peakLevel = 0; state.isMonitoring = false; notify(); } return { subscribe(fn) { subscribers.add(fn); fn({ ...state }); // Auto-start if configured and not already monitoring if (autoStart && !state.isMonitoring) { // Delay to allow controller to be ready setTimeout(() => { if (!state.isMonitoring && getController()) { startMonitoring(); } }, 100); } return () => { subscribers.delete(fn); }; }, startMonitoring, stopMonitoring, destroy() { stopMonitoring(); subscribers.clear(); }, }; }