[fix](trx-frontend): restore non-sdr signal graph and audio meter
Keep the non-SDR signal graph visible and drive the audio level bar from decoded sample levels instead of packet size. Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -277,12 +277,8 @@ function applyCapabilities(caps) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Spectrum panel (SDR-only)
|
// Spectrum panel (SDR-only)
|
||||||
const signalVisualBlock = document.querySelector(".signal-visual-block");
|
|
||||||
const spectrumPanel = document.getElementById("spectrum-panel");
|
const spectrumPanel = document.getElementById("spectrum-panel");
|
||||||
const centerFreqField = document.getElementById("center-freq-field");
|
const centerFreqField = document.getElementById("center-freq-field");
|
||||||
if (signalVisualBlock) {
|
|
||||||
signalVisualBlock.style.display = caps.filter_controls ? "" : "none";
|
|
||||||
}
|
|
||||||
if (spectrumPanel) {
|
if (spectrumPanel) {
|
||||||
if (caps.filter_controls) {
|
if (caps.filter_controls) {
|
||||||
spectrumPanel.style.display = "";
|
spectrumPanel.style.display = "";
|
||||||
@@ -2714,6 +2710,32 @@ const MAX_RX_BUFFER_SECS = 0.25;
|
|||||||
const TARGET_RX_BUFFER_SECS = 0.04;
|
const TARGET_RX_BUFFER_SECS = 0.04;
|
||||||
const MIN_RX_JITTER_SAMPLES = 512;
|
const MIN_RX_JITTER_SAMPLES = 512;
|
||||||
|
|
||||||
|
function setAudioLevel(levelPct) {
|
||||||
|
if (!audioLevelFill) return;
|
||||||
|
const clamped = Math.max(0, Math.min(100, Number.isFinite(levelPct) ? levelPct : 0));
|
||||||
|
audioLevelFill.style.width = `${clamped}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function levelFromChannels(channels, frameCount) {
|
||||||
|
if (!Array.isArray(channels) || channels.length === 0 || !Number.isFinite(frameCount) || frameCount <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let sumSquares = 0;
|
||||||
|
let samples = 0;
|
||||||
|
for (const channel of channels) {
|
||||||
|
if (!channel) continue;
|
||||||
|
const limit = Math.min(frameCount, channel.length);
|
||||||
|
for (let i = 0; i < limit; i++) {
|
||||||
|
const sample = channel[i];
|
||||||
|
sumSquares += sample * sample;
|
||||||
|
}
|
||||||
|
samples += limit;
|
||||||
|
}
|
||||||
|
if (samples <= 0) return 0;
|
||||||
|
const rms = Math.sqrt(sumSquares / samples);
|
||||||
|
return Math.min(100, rms * 220);
|
||||||
|
}
|
||||||
|
|
||||||
if (wfmAudioModeEl) {
|
if (wfmAudioModeEl) {
|
||||||
wfmAudioModeEl.value = loadSetting("wfmAudioMode", "stereo");
|
wfmAudioModeEl.value = loadSetting("wfmAudioMode", "stereo");
|
||||||
wfmAudioModeEl.addEventListener("change", () => {
|
wfmAudioModeEl.addEventListener("change", () => {
|
||||||
@@ -2812,6 +2834,7 @@ function configureRxStream(nextInfo) {
|
|||||||
}
|
}
|
||||||
rxGainNode.gain.value = rxVolSlider.value / 100;
|
rxGainNode.gain.value = rxVolSlider.value / 100;
|
||||||
rxActive = true;
|
rxActive = true;
|
||||||
|
setAudioLevel(0);
|
||||||
rxAudioBtn.style.borderColor = "#00d17f";
|
rxAudioBtn.style.borderColor = "#00d17f";
|
||||||
rxAudioBtn.style.color = "#00d17f";
|
rxAudioBtn.style.color = "#00d17f";
|
||||||
audioStatus.textContent = "RX";
|
audioStatus.textContent = "RX";
|
||||||
@@ -2878,14 +2901,6 @@ function startRxAudio() {
|
|||||||
if (!audioCtx) return;
|
if (!audioCtx) return;
|
||||||
const data = new Uint8Array(evt.data);
|
const data = new Uint8Array(evt.data);
|
||||||
|
|
||||||
// Throttle level indicator updates to max 10/sec
|
|
||||||
const now = Date.now();
|
|
||||||
if (now - lastLevelUpdate >= 100) {
|
|
||||||
const level = Math.min(100, (data.length / 120) * 100);
|
|
||||||
audioLevelFill.style.width = `${level}%`;
|
|
||||||
lastLevelUpdate = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use WebCodecs AudioDecoder for Opus if available
|
// Use WebCodecs AudioDecoder for Opus if available
|
||||||
if (typeof AudioDecoder !== "undefined" && !opusDecoder) {
|
if (typeof AudioDecoder !== "undefined" && !opusDecoder) {
|
||||||
try {
|
try {
|
||||||
@@ -2894,6 +2909,11 @@ function startRxAudio() {
|
|||||||
opusDecoder = new AudioDecoder({
|
opusDecoder = new AudioDecoder({
|
||||||
output: (frame) => {
|
output: (frame) => {
|
||||||
const frameChannels = extractAudioFrameChannels(frame);
|
const frameChannels = extractAudioFrameChannels(frame);
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastLevelUpdate >= 50) {
|
||||||
|
setAudioLevel(levelFromChannels(frameChannels, frame.numberOfFrames));
|
||||||
|
lastLevelUpdate = now;
|
||||||
|
}
|
||||||
const forceMono = frame.numberOfChannels >= 2
|
const forceMono = frame.numberOfChannels >= 2
|
||||||
&& wfmAudioModeEl
|
&& wfmAudioModeEl
|
||||||
&& wfmAudioModeEl.value === "mono"
|
&& wfmAudioModeEl.value === "mono"
|
||||||
@@ -2967,7 +2987,7 @@ function startRxAudio() {
|
|||||||
rxAudioBtn.style.borderColor = "";
|
rxAudioBtn.style.borderColor = "";
|
||||||
rxAudioBtn.style.color = "";
|
rxAudioBtn.style.color = "";
|
||||||
audioStatus.textContent = "Off";
|
audioStatus.textContent = "Off";
|
||||||
audioLevelFill.style.width = "0%";
|
setAudioLevel(0);
|
||||||
rxGainNode = null;
|
rxGainNode = null;
|
||||||
if (opusDecoder) {
|
if (opusDecoder) {
|
||||||
try { opusDecoder.close(); } catch(e) {}
|
try { opusDecoder.close(); } catch(e) {}
|
||||||
@@ -2996,7 +3016,7 @@ function stopRxAudio() {
|
|||||||
rxAudioBtn.style.borderColor = "";
|
rxAudioBtn.style.borderColor = "";
|
||||||
rxAudioBtn.style.color = "";
|
rxAudioBtn.style.color = "";
|
||||||
audioStatus.textContent = "Off";
|
audioStatus.textContent = "Off";
|
||||||
audioLevelFill.style.width = "0%";
|
setAudioLevel(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startTxAudio() {
|
function startTxAudio() {
|
||||||
|
|||||||
Reference in New Issue
Block a user