223 lines
6.8 KiB
TypeScript
223 lines
6.8 KiB
TypeScript
// state.ts - Peak Holder с фиксированным временем удержания
|
||
|
||
interface AppState {
|
||
maxPoints: number;
|
||
signalHistory: number[];
|
||
lastBufferSize: number;
|
||
|
||
// Peak Holder для амплитуды
|
||
peakValue: number; // Текущее удерживаемое пиковое значение амплитуды
|
||
currentValue: number; // Текущий максимум из буфера
|
||
peakTimer: number | null; // Таймер для сброса пика амплитуды
|
||
peakHoldTime: number; // Время удержания пика в миллисекундах
|
||
peakLocked: boolean; // Заблокирован ли пик (не сбрасывается до таймера)
|
||
|
||
// Peak Holder для уровня сигнала
|
||
peakSignalLevel: number;
|
||
currentSignalLevel: number;
|
||
signalPeakTimer: number | null;
|
||
signalPeakHoldTime: number;
|
||
signalPeakLocked: boolean;
|
||
|
||
hasPeak: boolean;
|
||
lastMaxSignal: number;
|
||
}
|
||
|
||
type PeakHolderListener = () => void;
|
||
|
||
export const state: AppState = {
|
||
maxPoints: 1000,
|
||
signalHistory: [],
|
||
lastBufferSize: 0,
|
||
|
||
peakValue: 0,
|
||
currentValue: 0,
|
||
peakTimer: null,
|
||
peakHoldTime: 3000,
|
||
peakLocked: false,
|
||
|
||
peakSignalLevel: 0,
|
||
currentSignalLevel: 0,
|
||
signalPeakTimer: null,
|
||
signalPeakHoldTime: 3000,
|
||
signalPeakLocked: false,
|
||
|
||
hasPeak: false,
|
||
lastMaxSignal: 0,
|
||
};
|
||
|
||
let peakHolderListener: PeakHolderListener | null = null;
|
||
|
||
// ============================================
|
||
// ПУБЛИЧНЫЕ ФУНКЦИИ
|
||
// ============================================
|
||
|
||
export function setPeakHolderListener(listener: PeakHolderListener | null): void {
|
||
peakHolderListener = listener;
|
||
}
|
||
|
||
function notifyPeakHolderChange(): void {
|
||
if (peakHolderListener) {
|
||
peakHolderListener();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Обновление пикового значения амплитуды
|
||
* @param currentMax - текущий максимум из буфера (в процентах 0-100)
|
||
* @returns пиковое значение
|
||
*/
|
||
export function updatePeakHolder(currentMax: number): number {
|
||
state.currentValue = currentMax;
|
||
state.lastMaxSignal = currentMax;
|
||
|
||
// Если пик заблокирован - ничего не меняем, только обновляем currentValue
|
||
if (state.peakLocked) {
|
||
return state.peakValue;
|
||
}
|
||
|
||
// Если новое значение БОЛЬШЕ текущего пика - обновляем пик и блокируем
|
||
if (currentMax > state.peakValue) {
|
||
state.peakValue = currentMax;
|
||
state.hasPeak = true;
|
||
state.peakLocked = true;
|
||
scheduleAmplitudePeakRelease();
|
||
notifyPeakHolderChange();
|
||
return state.peakValue;
|
||
}
|
||
|
||
// Если новое значение МЕНЬШЕ или РАВНО - ничего не делаем
|
||
return state.peakValue;
|
||
}
|
||
|
||
/**
|
||
* Обновление пикового уровня сигнала
|
||
* @param currentMaxLevel - текущий максимум уровня из буфера (0-3)
|
||
* @returns пиковый уровень
|
||
*/
|
||
export function updateSignalPeakHolder(currentMaxLevel: number): number {
|
||
state.currentSignalLevel = currentMaxLevel;
|
||
|
||
if (state.signalPeakLocked) {
|
||
return state.peakSignalLevel;
|
||
}
|
||
|
||
if (currentMaxLevel > state.peakSignalLevel) {
|
||
state.peakSignalLevel = currentMaxLevel;
|
||
state.hasPeak = true;
|
||
state.signalPeakLocked = true;
|
||
scheduleSignalPeakRelease();
|
||
notifyPeakHolderChange();
|
||
return state.peakSignalLevel;
|
||
}
|
||
|
||
return state.peakSignalLevel;
|
||
}
|
||
|
||
// ============================================
|
||
// ВНУТРЕННИЕ ФУНКЦИИ
|
||
// ============================================
|
||
|
||
function scheduleAmplitudePeakRelease(): void {
|
||
if (state.peakTimer !== null) {
|
||
clearTimeout(state.peakTimer);
|
||
state.peakTimer = null;
|
||
}
|
||
|
||
state.peakTimer = window.setTimeout(() => {
|
||
// Сбрасываем пик до текущего значения
|
||
state.peakValue = state.currentValue;
|
||
state.peakTimer = null;
|
||
state.hasPeak = false;
|
||
state.peakLocked = false;
|
||
notifyPeakHolderChange();
|
||
}, state.peakHoldTime);
|
||
}
|
||
|
||
function scheduleSignalPeakRelease(): void {
|
||
if (state.signalPeakTimer !== null) {
|
||
clearTimeout(state.signalPeakTimer);
|
||
state.signalPeakTimer = null;
|
||
}
|
||
|
||
state.signalPeakTimer = window.setTimeout(() => {
|
||
state.peakSignalLevel = state.currentSignalLevel;
|
||
state.signalPeakTimer = null;
|
||
state.hasPeak = false;
|
||
state.signalPeakLocked = false;
|
||
notifyPeakHolderChange();
|
||
}, state.signalPeakHoldTime);
|
||
}
|
||
|
||
// ============================================
|
||
// ОСТАЛЬНЫЕ ФУНКЦИИ
|
||
// ============================================
|
||
|
||
export function setSignalPeakHoldTime(ms: number): void {
|
||
state.signalPeakHoldTime = ms;
|
||
}
|
||
|
||
export function getSignalPeakInfo(): { peak: number; current: number; timerActive: boolean } {
|
||
return {
|
||
peak: state.peakSignalLevel,
|
||
current: state.currentSignalLevel,
|
||
timerActive: state.signalPeakTimer !== null,
|
||
};
|
||
}
|
||
|
||
export function addSignal(value: number): void {
|
||
state.signalHistory.push(value);
|
||
if (state.signalHistory.length > state.maxPoints) {
|
||
state.signalHistory = state.signalHistory.slice(-state.maxPoints);
|
||
}
|
||
}
|
||
|
||
export function forceResetSignalPeak(): void {
|
||
if (state.signalPeakTimer !== null) {
|
||
clearTimeout(state.signalPeakTimer);
|
||
state.signalPeakTimer = null;
|
||
}
|
||
state.peakSignalLevel = 0;
|
||
state.currentSignalLevel = 0;
|
||
state.hasPeak = false;
|
||
state.lastMaxSignal = 0;
|
||
state.signalPeakLocked = false;
|
||
notifyPeakHolderChange();
|
||
}
|
||
|
||
export function forceResetPeak(): void {
|
||
if (state.peakTimer !== null) {
|
||
clearTimeout(state.peakTimer);
|
||
state.peakTimer = null;
|
||
}
|
||
state.peakValue = 0;
|
||
state.currentValue = 0;
|
||
state.hasPeak = false;
|
||
state.lastMaxSignal = 0;
|
||
state.peakLocked = false;
|
||
forceResetSignalPeak();
|
||
notifyPeakHolderChange();
|
||
}
|
||
|
||
export function addSignals(values: number[], bufferSize: number): void {
|
||
if (!Array.isArray(values) || values.length === 0) return;
|
||
|
||
const newBytes = bufferSize - state.lastBufferSize;
|
||
|
||
if (newBytes > 0 && newBytes <= values.length) {
|
||
const fresh = values.slice(-newBytes);
|
||
const decoded = fresh.map(b => b & 0x3F);
|
||
state.signalHistory.push(...decoded);
|
||
}
|
||
|
||
state.lastBufferSize = bufferSize;
|
||
|
||
if (state.signalHistory.length > state.maxPoints) {
|
||
state.signalHistory = state.signalHistory.slice(-state.maxPoints);
|
||
}
|
||
}
|
||
|
||
export function resetBufferTracking(): void {
|
||
state.lastBufferSize = 0;
|
||
forceResetPeak();
|
||
} |