Recover analyzer DSP and realtime pipeline corrections
This commit is contained in:
+277
-32
@@ -2,14 +2,26 @@
|
||||
// Wird von main.js mit `initAudio(env)` gestartet. Nutzt env.config (CONFIG) und env.meters.
|
||||
|
||||
import { applyPhoenixGlobalConfig, buildPhoenixGlobalConfigPayload, saveConfig } from './config.js';
|
||||
import { decodePhoenixSpectroBuffer, decodePhoenixVisualsBuffer } from './binary_protocol.js';
|
||||
import { getRtwCenters } from './rtw_centers.js';
|
||||
|
||||
// interner Zustand (pro App-Instanz)
|
||||
let phoenixSocket = null;
|
||||
let phoenixSpectroSocket = null;
|
||||
let phoenixSpectroRetryTimer = null;
|
||||
let pendingSpectroBuffer = null;
|
||||
let phoenixSpectroRaf = 0;
|
||||
let phoenixVisualsSocket = null;
|
||||
let phoenixVisualsRetryTimer = null;
|
||||
let pendingVisualsBuffer = null;
|
||||
let phoenixVisualsRaf = 0;
|
||||
let envRef = null;
|
||||
let lifecycleHandlersBound = false;
|
||||
let recoverTimer = null;
|
||||
let lastHardRecoverAt = 0;
|
||||
let pendingPhoenixFrame = null;
|
||||
let phoenixPacketRaf = 0;
|
||||
let phoenixPacketBusy = false;
|
||||
|
||||
const RMS_RING = { L: new Float32Array(512), R: new Float32Array(512), i: 0, n: 0 };
|
||||
const WAVEFORM_RING_SECONDS = 20;
|
||||
@@ -50,30 +62,65 @@ function normalizePhoenixBaseUrl(rawValue) {
|
||||
}
|
||||
}
|
||||
|
||||
function buildPhoenixWsUrl(baseUrl) {
|
||||
function buildPhoenixWsUrl(baseUrl, pathname = '/api/v1/metrics/ws') {
|
||||
try {
|
||||
const url = new URL(baseUrl);
|
||||
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
url.pathname = '/api/v1/metrics/ws';
|
||||
url.pathname = pathname;
|
||||
url.search = '';
|
||||
url.hash = '';
|
||||
return url.toString();
|
||||
} catch (_) {
|
||||
const fallback = defaultPhoenixBaseUrl();
|
||||
return fallback.replace(/^http:/i, 'ws:').replace(/^https:/i, 'wss:') + '/api/v1/metrics/ws';
|
||||
return fallback.replace(/^http:/i, 'ws:').replace(/^https:/i, 'wss:') + pathname;
|
||||
}
|
||||
}
|
||||
|
||||
function closePhoenixSocket() {
|
||||
if (!phoenixSocket) return;
|
||||
try {
|
||||
phoenixSocket.onopen = null;
|
||||
phoenixSocket.onmessage = null;
|
||||
phoenixSocket.onerror = null;
|
||||
phoenixSocket.onclose = null;
|
||||
phoenixSocket.close();
|
||||
} catch (_) {}
|
||||
if (phoenixSocket) {
|
||||
try {
|
||||
phoenixSocket.onopen = null;
|
||||
phoenixSocket.onmessage = null;
|
||||
phoenixSocket.onerror = null;
|
||||
phoenixSocket.onclose = null;
|
||||
phoenixSocket.close();
|
||||
} catch (_) {}
|
||||
}
|
||||
phoenixSocket = null;
|
||||
if (phoenixSpectroRetryTimer) clearTimeout(phoenixSpectroRetryTimer);
|
||||
phoenixSpectroRetryTimer = null;
|
||||
if (phoenixSpectroSocket) {
|
||||
try {
|
||||
phoenixSpectroSocket.onopen = null;
|
||||
phoenixSpectroSocket.onmessage = null;
|
||||
phoenixSpectroSocket.onerror = null;
|
||||
phoenixSpectroSocket.onclose = null;
|
||||
phoenixSpectroSocket.close();
|
||||
} catch (_) {}
|
||||
}
|
||||
phoenixSpectroSocket = null;
|
||||
pendingSpectroBuffer = null;
|
||||
if (phoenixSpectroRaf) cancelAnimationFrame(phoenixSpectroRaf);
|
||||
phoenixSpectroRaf = 0;
|
||||
if (phoenixVisualsRetryTimer) clearTimeout(phoenixVisualsRetryTimer);
|
||||
phoenixVisualsRetryTimer = null;
|
||||
if (phoenixVisualsSocket) {
|
||||
try {
|
||||
phoenixVisualsSocket.onopen = null;
|
||||
phoenixVisualsSocket.onmessage = null;
|
||||
phoenixVisualsSocket.onerror = null;
|
||||
phoenixVisualsSocket.onclose = null;
|
||||
phoenixVisualsSocket.close();
|
||||
} catch (_) {}
|
||||
}
|
||||
phoenixVisualsSocket = null;
|
||||
pendingVisualsBuffer = null;
|
||||
if (phoenixVisualsRaf) cancelAnimationFrame(phoenixVisualsRaf);
|
||||
phoenixVisualsRaf = 0;
|
||||
pendingPhoenixFrame = null;
|
||||
if (phoenixPacketRaf) cancelAnimationFrame(phoenixPacketRaf);
|
||||
phoenixPacketRaf = 0;
|
||||
phoenixPacketBusy = false;
|
||||
}
|
||||
|
||||
async function requestPhoenixRtaConfig(baseUrl, config) {
|
||||
@@ -164,14 +211,20 @@ function applyRmsActivity(env, rmsL, rmsR, sampleTs) {
|
||||
if (hit) env.audio.lastSignalTs = sampleTs;
|
||||
}
|
||||
|
||||
function copyPhoenixSpectroBins(audioState, spectro) {
|
||||
if (!audioState) return;
|
||||
const src = Array.isArray(spectro?.bins) ? spectro.bins : null;
|
||||
function copyPhoenixSpectroBins(audioState, spectro, frameDelta = 1) {
|
||||
if (!audioState) return false;
|
||||
const src = Array.isArray(spectro?.bins) || ArrayBuffer.isView(spectro?.bins)
|
||||
? spectro.bins
|
||||
: null;
|
||||
if (!src || !src.length) {
|
||||
audioState.phoenixSpectroBuffer = null;
|
||||
audioState.phoenixSpectroMeta = null;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
const sourceSeq = Number(spectro?.seq);
|
||||
const previousSourceSeq = Number(audioState.phoenixSpectroSourceSeq || 0);
|
||||
if (Number.isFinite(sourceSeq) && sourceSeq > 0
|
||||
&& previousSourceSeq > 0 && sourceSeq <= previousSourceSeq) return false;
|
||||
let target = audioState.phoenixSpectroBuffer;
|
||||
if (!(target instanceof Float32Array) || target.length !== src.length) {
|
||||
target = new Float32Array(src.length);
|
||||
@@ -190,7 +243,118 @@ function copyPhoenixSpectroBins(audioState, spectro) {
|
||||
fftSize,
|
||||
frequencyBinCount: target.length,
|
||||
};
|
||||
audioState.phoenixSpectroSeq = (audioState.phoenixSpectroSeq || 0) + 1;
|
||||
let delta = Number.isFinite(Number(frameDelta)) ? Math.max(1, Math.floor(Number(frameDelta))) : 1;
|
||||
if (Number.isFinite(sourceSeq) && sourceSeq > 0) {
|
||||
if (previousSourceSeq > 0) delta = Math.max(1, Math.floor(sourceSeq - previousSourceSeq));
|
||||
audioState.phoenixSpectroSourceSeq = sourceSeq;
|
||||
}
|
||||
audioState.phoenixSpectroSeq = (audioState.phoenixSpectroSeq || 0) + delta;
|
||||
return true;
|
||||
}
|
||||
|
||||
function openPhoenixSpectroSocket(baseUrl, env) {
|
||||
if (phoenixSpectroSocket && (
|
||||
phoenixSpectroSocket.readyState === WebSocket.OPEN
|
||||
|| phoenixSpectroSocket.readyState === WebSocket.CONNECTING
|
||||
)) return;
|
||||
const socket = new WebSocket(buildPhoenixWsUrl(baseUrl, '/api/v1/spectro/ws'));
|
||||
socket.binaryType = 'arraybuffer';
|
||||
phoenixSpectroSocket = socket;
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
if (!(event.data instanceof ArrayBuffer)) return;
|
||||
pendingSpectroBuffer = event.data;
|
||||
scheduleSpectroBufferPump(env);
|
||||
};
|
||||
|
||||
const retry = () => {
|
||||
if (phoenixSpectroSocket === socket) phoenixSpectroSocket = null;
|
||||
if (!phoenixSocket || phoenixSocket.readyState !== WebSocket.OPEN) return;
|
||||
if (phoenixSpectroRetryTimer) clearTimeout(phoenixSpectroRetryTimer);
|
||||
phoenixSpectroRetryTimer = setTimeout(() => {
|
||||
phoenixSpectroRetryTimer = null;
|
||||
openPhoenixSpectroSocket(baseUrl, env);
|
||||
}, 1000);
|
||||
};
|
||||
socket.onerror = retry;
|
||||
socket.onclose = retry;
|
||||
}
|
||||
|
||||
function scheduleSpectroBufferPump(env) {
|
||||
if (phoenixSpectroRaf || !pendingSpectroBuffer) return;
|
||||
phoenixSpectroRaf = requestAnimationFrame(() => {
|
||||
phoenixSpectroRaf = 0;
|
||||
const buffer = pendingSpectroBuffer;
|
||||
pendingSpectroBuffer = null;
|
||||
try {
|
||||
const decoded = decodePhoenixSpectroBuffer(buffer);
|
||||
if (!decoded) return;
|
||||
if (!copyPhoenixSpectroBins(env.audio, decoded)) return;
|
||||
env.requestRender?.('spectro');
|
||||
} catch (err) {
|
||||
console.warn('Phoenix spectrogram packet error:', err);
|
||||
} finally {
|
||||
if (pendingSpectroBuffer) scheduleSpectroBufferPump(env);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openPhoenixVisualsSocket(baseUrl, env) {
|
||||
if (phoenixVisualsSocket && (
|
||||
phoenixVisualsSocket.readyState === WebSocket.OPEN
|
||||
|| phoenixVisualsSocket.readyState === WebSocket.CONNECTING
|
||||
)) return;
|
||||
const socket = new WebSocket(buildPhoenixWsUrl(baseUrl, '/api/v1/visuals/ws'));
|
||||
socket.binaryType = 'arraybuffer';
|
||||
phoenixVisualsSocket = socket;
|
||||
socket.onmessage = (event) => {
|
||||
if (!(event.data instanceof ArrayBuffer)) return;
|
||||
pendingVisualsBuffer = event.data;
|
||||
scheduleVisualsBufferPump(env);
|
||||
};
|
||||
const retry = () => {
|
||||
if (phoenixVisualsSocket === socket) phoenixVisualsSocket = null;
|
||||
if (!phoenixSocket || phoenixSocket.readyState !== WebSocket.OPEN) return;
|
||||
if (phoenixVisualsRetryTimer) clearTimeout(phoenixVisualsRetryTimer);
|
||||
phoenixVisualsRetryTimer = setTimeout(() => {
|
||||
phoenixVisualsRetryTimer = null;
|
||||
openPhoenixVisualsSocket(baseUrl, env);
|
||||
}, 1000);
|
||||
};
|
||||
socket.onerror = retry;
|
||||
socket.onclose = retry;
|
||||
}
|
||||
|
||||
function scheduleVisualsBufferPump(env) {
|
||||
if (phoenixVisualsRaf || !pendingVisualsBuffer) return;
|
||||
phoenixVisualsRaf = requestAnimationFrame(() => {
|
||||
phoenixVisualsRaf = 0;
|
||||
const buffer = pendingVisualsBuffer;
|
||||
pendingVisualsBuffer = null;
|
||||
try {
|
||||
applyVisualsBuffer(env, buffer);
|
||||
} catch (err) {
|
||||
console.warn('Phoenix visual packet error:', err);
|
||||
} finally {
|
||||
if (pendingVisualsBuffer) scheduleVisualsBufferPump(env);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function applyVisualsBuffer(env, buffer) {
|
||||
const decoded = decodePhoenixVisualsBuffer(buffer);
|
||||
if (!decoded) return;
|
||||
const { seq, left, right, wave } = decoded;
|
||||
const previousSeq = Number(env.audio?.phoenixVisualsSeq || 0);
|
||||
if (previousSeq > 0 && seq <= previousSeq) return;
|
||||
if (left && right) {
|
||||
env.audio.xyL = left;
|
||||
env.audio.xyR = right;
|
||||
env.audio.xySeq = seq;
|
||||
}
|
||||
if (wave) updateWaveformEnvelopeStore(env.audio, wave);
|
||||
env.audio.phoenixVisualsSeq = seq;
|
||||
env.requestRender?.('visuals');
|
||||
}
|
||||
|
||||
async function updateActiveMeters(env, packet, CONFIG) {
|
||||
@@ -230,21 +394,33 @@ async function syncPhoenixGlobalConfig(env, revision) {
|
||||
async function applyIncomingAudioPacket(env, packet, CONFIG, sampleTs = performance.now()) {
|
||||
const d = packet || {};
|
||||
if (!env?.audio) return;
|
||||
const seq = Number(d.seq);
|
||||
const previousSeq = Number(env.audio.phoenixMetricsSeq || 0);
|
||||
if (Number.isFinite(seq) && seq > 0 && previousSeq > 0 && seq <= previousSeq) return;
|
||||
if (Number.isFinite(seq) && seq > 0) env.audio.phoenixMetricsSeq = seq;
|
||||
env.requestRender?.('audio');
|
||||
env.audio.lastSampleTs = sampleTs;
|
||||
env.audio.alive = true;
|
||||
if (Number.isFinite(Number(d.sampleRate)) && Number(d.sampleRate) > 0) {
|
||||
env.audio.sampleRate = Number(d.sampleRate);
|
||||
env.audio.nyq = Number(d.sampleRate) / 2;
|
||||
}
|
||||
if (d.xyL && d.xyR) {
|
||||
env.audio.xyL = d.xyL;
|
||||
env.audio.xyR = d.xyR;
|
||||
env.audio.xySeq = Number.isFinite(d.seq) ? d.seq : (env.audio.xySeq || 0);
|
||||
}
|
||||
if (typeof d.correlation === 'number') env.audio.correlation = d.correlation;
|
||||
if (typeof d.correlationNegativePeak === 'number') {
|
||||
env.audio.correlationNegativePeak = d.correlationNegativePeak;
|
||||
}
|
||||
if (d.waveL && env.audio.pushWaveSamples) {
|
||||
const channelCount = d.waveChannels || (d.waveR ? 2 : 1);
|
||||
env.audio.sampleRate = d.sampleRate || env.audio.sampleRate || 48000;
|
||||
env.audio.pushWaveSamples(d.waveL, d.waveR || null, channelCount, d.sampleRate);
|
||||
}
|
||||
if (d.rta) env.audio.rtaData = d.rta;
|
||||
if (d.spectro) copyPhoenixSpectroBins(env.audio, d.spectro);
|
||||
if (d.spectro) copyPhoenixSpectroBins(env.audio, d.spectro, d.spectroFrameDelta);
|
||||
if (typeof d.ppmDinL === 'number') env.audio.ppmDinL = d.ppmDinL;
|
||||
if (typeof d.ppmDinR === 'number') env.audio.ppmDinR = d.ppmDinR;
|
||||
if (typeof d.ppmEbuL === 'number') env.audio.ppmEbuL = d.ppmEbuL;
|
||||
@@ -294,6 +470,8 @@ function buildPhoenixMeterPacket(frame) {
|
||||
const lufsSR = Number(frame?.lufs_sr);
|
||||
const ppmBoxL = Number(frame?.ppm_box_l);
|
||||
const ppmBoxR = Number(frame?.ppm_box_r);
|
||||
const correlation = Number(frame?.correlation);
|
||||
const correlationNegativePeak = Number(frame?.correlation_negative_peak);
|
||||
const xyL = Array.isArray(frame?.xy_l) ? frame.xy_l : null;
|
||||
const xyR = Array.isArray(frame?.xy_r) ? frame.xy_r : null;
|
||||
const waveL = Array.isArray(frame?.wave_l) && frame.wave_l.length ? frame.wave_l : null;
|
||||
@@ -302,7 +480,11 @@ function buildPhoenixMeterPacket(frame) {
|
||||
const spectro = frame?.spectro && typeof frame.spectro === 'object' ? frame.spectro : null;
|
||||
const waveEnv = frame?.wave_env && typeof frame.wave_env === 'object' ? frame.wave_env : null;
|
||||
return {
|
||||
sampleRate: 48000,
|
||||
sampleRate: Number.isFinite(Number(frame?.sample_rate)) ? Number(frame.sample_rate) : 48000,
|
||||
correlation: Number.isFinite(correlation) ? Math.max(-1, Math.min(1, correlation)) : 0,
|
||||
correlationNegativePeak: Number.isFinite(correlationNegativePeak)
|
||||
? Math.max(-1, Math.min(1, correlationNegativePeak))
|
||||
: 0,
|
||||
rmsL: Number.isFinite(rmsL) ? rmsL : -120,
|
||||
rmsR: Number.isFinite(rmsR) ? rmsR : -120,
|
||||
tpL: Number.isFinite(tpL) ? tpL : -120,
|
||||
@@ -344,10 +526,14 @@ function buildPhoenixMeterPacket(frame) {
|
||||
sampleRate: Number.isFinite(Number(rta?.sample_rate)) ? Number(rta.sample_rate) : 48000,
|
||||
} : null,
|
||||
spectro: spectro ? {
|
||||
seq: Number.isFinite(Number(spectro?.seq)) ? Number(spectro.seq) : 0,
|
||||
bins: Array.isArray(spectro?.bins) ? spectro.bins : [],
|
||||
sampleRate: Number.isFinite(Number(spectro?.sample_rate)) ? Number(spectro.sample_rate) : 48000,
|
||||
fftSize: Number.isFinite(Number(spectro?.fft_size)) ? Number(spectro.fft_size) : 4096,
|
||||
} : null,
|
||||
spectroFrameDelta: Number.isFinite(Number(frame?.__spectro_frame_delta))
|
||||
? Math.max(1, Math.floor(Number(frame.__spectro_frame_delta)))
|
||||
: 1,
|
||||
waveEnv: waveEnv ? {
|
||||
data: Array.isArray(waveEnv?.data) ? waveEnv.data : [],
|
||||
columns: Number.isFinite(Number(waveEnv?.columns)) ? Number(waveEnv.columns) : 0,
|
||||
@@ -363,6 +549,57 @@ function buildPhoenixMeterPacket(frame) {
|
||||
};
|
||||
}
|
||||
|
||||
function mergePendingPhoenixFrame(previous, next) {
|
||||
if (!next) return previous;
|
||||
const previousSeq = Number(previous?.seq || 0);
|
||||
const nextSeq = Number(next?.seq || 0);
|
||||
if (previous && previousSeq > 0 && nextSeq > 0 && nextSeq <= previousSeq) return previous;
|
||||
if (!previous) {
|
||||
if (next?.spectro) next.__spectro_frame_delta = 1;
|
||||
return next;
|
||||
}
|
||||
const merged = next;
|
||||
if (!merged.wave_env && previous.wave_env) merged.wave_env = previous.wave_env;
|
||||
if ((!merged.xy_l || !merged.xy_l.length) && previous.xy_l?.length) {
|
||||
merged.xy_l = previous.xy_l;
|
||||
merged.xy_r = previous.xy_r;
|
||||
}
|
||||
if (!merged.rta && previous.rta) merged.rta = previous.rta;
|
||||
const previousSpectroCount = Math.max(0, Number(previous.__spectro_frame_delta) || 0);
|
||||
if (merged.spectro) {
|
||||
merged.__spectro_frame_delta = previousSpectroCount + 1;
|
||||
} else if (previous.spectro) {
|
||||
merged.spectro = previous.spectro;
|
||||
merged.__spectro_frame_delta = Math.max(1, previousSpectroCount);
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
function schedulePhoenixPacketPump(env, CONFIG) {
|
||||
if (phoenixPacketRaf || phoenixPacketBusy || !pendingPhoenixFrame) return;
|
||||
phoenixPacketRaf = requestAnimationFrame(async () => {
|
||||
phoenixPacketRaf = 0;
|
||||
const frame = pendingPhoenixFrame;
|
||||
pendingPhoenixFrame = null;
|
||||
if (!frame) return;
|
||||
phoenixPacketBusy = true;
|
||||
try {
|
||||
const packet = buildPhoenixMeterPacket(frame);
|
||||
await applyIncomingAudioPacket(env, packet, CONFIG, performance.now());
|
||||
} catch (err) {
|
||||
console.warn('Phoenix packet error:', err);
|
||||
} finally {
|
||||
phoenixPacketBusy = false;
|
||||
if (pendingPhoenixFrame) schedulePhoenixPacketPump(env, CONFIG);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function enqueuePhoenixFrame(env, CONFIG, frame) {
|
||||
pendingPhoenixFrame = mergePendingPhoenixFrame(pendingPhoenixFrame, frame);
|
||||
schedulePhoenixPacketPump(env, CONFIG);
|
||||
}
|
||||
|
||||
function bindLifecycleHandlers() {
|
||||
if (lifecycleHandlersBound) return;
|
||||
|
||||
@@ -625,33 +862,39 @@ function updateWaveformEnvelopeStore(audioState, payload) {
|
||||
appendWaveformEnvelope(audioState.waveEnvStore, payload);
|
||||
}
|
||||
|
||||
function buildRtaRuntimeConfig(CONFIG = {}) {
|
||||
const bpoMode = CONFIG.RTA_BPO_MODE || '1_6';
|
||||
export function buildRtaRuntimeConfig(CONFIG = {}) {
|
||||
const bpoMode = CONFIG.RTA_BPO_MODE || '1_3';
|
||||
const layout = CONFIG.RTA_BAR_LAYOUT === 'rtw' ? 'rtw' : 'iec';
|
||||
const runtimeBpoMode = layout === 'rtw' ? '1_12' : bpoMode;
|
||||
const rtwProfile = layout === 'rtw';
|
||||
const runtimeBpoMode = rtwProfile ? '1_3' : bpoMode;
|
||||
return {
|
||||
engine: CONFIG.RTA_ENGINE || 'fft',
|
||||
engine: rtwProfile ? 'iir' : (CONFIG.RTA_ENGINE || 'iir'),
|
||||
fftSize: CONFIG.FFT_SIZE || 4096,
|
||||
monoInput: !!CONFIG.MONO_INPUT,
|
||||
lrFractionalDelayEnabled: !!CONFIG.LR_FRACTIONAL_DELAY_ENABLED,
|
||||
lrFractionalDelaySamples: Number.isFinite(CONFIG.LR_FRACTIONAL_DELAY_SAMPLES) ? CONFIG.LR_FRACTIONAL_DELAY_SAMPLES : 0,
|
||||
bpo: runtimeBpoMode,
|
||||
freqRange: CONFIG.RTA_FREQ_RANGE || 'norm',
|
||||
freqRange: rtwProfile ? 'norm' : (CONFIG.RTA_FREQ_RANGE || 'norm'),
|
||||
weighting: CONFIG.RTA_WEIGHTING || 'z',
|
||||
order: CONFIG.RTA_IIR_ORDER || 4,
|
||||
order: rtwProfile ? 6 : (CONFIG.RTA_IIR_ORDER || 6),
|
||||
tauFast: CONFIG.RTA_IIR_TAU_FAST || 0.12,
|
||||
tauSlow: CONFIG.RTA_IIR_TAU_SLOW || 1.0,
|
||||
integration: CONFIG.RTA_INTEGRATION || 'fast',
|
||||
layout,
|
||||
inputOffsetDbL: Number.isFinite(CONFIG.INPUT_OFFSET_DB_L) ? CONFIG.INPUT_OFFSET_DB_L : -5,
|
||||
inputOffsetDbR: Number.isFinite(CONFIG.INPUT_OFFSET_DB_R) ? CONFIG.INPUT_OFFSET_DB_R : -5,
|
||||
ppmDinAttackMs: Number.isFinite(CONFIG.PPM_DIN_ATTACK_MS) ? CONFIG.PPM_DIN_ATTACK_MS : 5,
|
||||
ppmDinDecayDbPerS: Number.isFinite(CONFIG.PPM_DIN_DECAY_DB_PER_S) ? CONFIG.PPM_DIN_DECAY_DB_PER_S : (20 / 1.7),
|
||||
ppmDinAttackMs: 10,
|
||||
ppmDinDecayDbPerS: 20 / 1.5,
|
||||
ppmDinFastAttack: !!CONFIG.PPM_DIN_FAST_ATTACK,
|
||||
ppmEbuAttackMs: Number.isFinite(CONFIG.PPM_EBU_ATTACK_MS) ? CONFIG.PPM_EBU_ATTACK_MS : 10,
|
||||
ppmEbuDecayDbPerS: Number.isFinite(CONFIG.PPM_EBU_DECAY_DB_PER_S) ? CONFIG.PPM_EBU_DECAY_DB_PER_S : (24 / 2.8),
|
||||
ppmEbuAttackMs: 10,
|
||||
ppmEbuDecayDbPerS: 24 / 2.8,
|
||||
lufsIWindowMin: Number.isFinite(CONFIG.LUFS_I_WINDOW_MIN) ? CONFIG.LUFS_I_WINDOW_MIN : 4,
|
||||
lufsINormEnabled: !!CONFIG.LUFS_I_NORM_ENABLED,
|
||||
correlationResponseS: Number(CONFIG.CORR_RESPONSE_S) >= 1.75 ? 2.5 : 1.0,
|
||||
correlationResetToken: Math.max(0, Math.floor(Number(CONFIG.CORR_RESET_TOKEN) || 0)),
|
||||
xyPoints: [128, 256, 512, 1024, 2048].includes(Number(CONFIG.XY_POINTS))
|
||||
? Number(CONFIG.XY_POINTS)
|
||||
: 1024,
|
||||
rtwCenters: layout === 'rtw' ? getRtwCenters(runtimeBpoMode) : null,
|
||||
};
|
||||
}
|
||||
@@ -700,6 +943,9 @@ async function initPhoenixAudio(env) {
|
||||
env.audio.phoenixSpectroScratch = null;
|
||||
env.audio.phoenixSpectroMeta = null;
|
||||
env.audio.phoenixSpectroSeq = 0;
|
||||
env.audio.phoenixSpectroSourceSeq = 0;
|
||||
env.audio.phoenixVisualsSeq = 0;
|
||||
env.audio.phoenixMetricsSeq = 0;
|
||||
env.audio.rmsDb = { L: -120, R: -120, mono: -120 };
|
||||
|
||||
const phoenixAnalyser = {
|
||||
@@ -852,16 +1098,15 @@ async function initPhoenixAudio(env) {
|
||||
};
|
||||
|
||||
socket.onopen = () => {
|
||||
openPhoenixSpectroSocket(baseUrl, env);
|
||||
openPhoenixVisualsSocket(baseUrl, env);
|
||||
finish(true);
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
try {
|
||||
const frame = JSON.parse(event.data);
|
||||
const packet = buildPhoenixMeterPacket(frame);
|
||||
applyIncomingAudioPacket(env, packet, CONFIG, performance.now()).catch((err) => {
|
||||
console.warn('Phoenix packet error:', err);
|
||||
});
|
||||
enqueuePhoenixFrame(env, CONFIG, frame);
|
||||
} catch (err) {
|
||||
console.warn('Phoenix metrics parse error:', err);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user