Prima release ufficiale di Web-DMR

This commit is contained in:
2026-08-29 12:16:50 +02:00
commit 70cc349b70
8 changed files with 322803 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
let ws = null;
let audioCtx = null;
let gainNode = null; // Il controller del volume
let nextStartTime = 0;
const connectBtn = document.getElementById('connectBtn');
const tgInput = document.getElementById('tgInput');
const setTgBtn = document.getElementById('setTgBtn');
const displayScreen = document.getElementById('displayScreen');
const statusText = document.getElementById('statusText');
// --- CONTROLLI VOLUME ---
const volumeSlider = document.getElementById('volumeSlider');
const volumeValue = document.getElementById('volumeValue');
volumeSlider.addEventListener('input', (e) => {
const val = parseFloat(e.target.value);
volumeValue.innerText = Math.round(val * 100) + '%';
// Modifica il volume in tempo reale
if (gainNode) {
gainNode.gain.value = val;
}
});
// --- RIPRODUZIONE AUDIO PCM ---
function playPCM320(arrayBuffer) {
if (!audioCtx || !gainNode) return;
const int16Array = new Int16Array(arrayBuffer);
const float32Array = new Float32Array(int16Array.length);
for (let i = 0; i < int16Array.length; i++) {
float32Array[i] = int16Array[i] / 32768.0;
}
const buffer = audioCtx.createBuffer(1, float32Array.length, 8000);
buffer.getChannelData(0).set(float32Array);
const source = audioCtx.createBufferSource();
source.buffer = buffer;
// Colleghiamo l'audio al controller del volume invece che direttamente all'uscita
source.connect(gainNode);
const currentTime = audioCtx.currentTime;
if (nextStartTime < currentTime) {
nextStartTime = currentTime + 0.02;
}
source.start(nextStartTime);
nextStartTime += buffer.duration;
}
// --- CONNESSIONE WEBSOCKET ---
function startAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// Creazione del nodo Volume
gainNode = audioCtx.createGain();
gainNode.connect(audioCtx.destination);
gainNode.gain.value = parseFloat(volumeSlider.value);
}
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
ws = new WebSocket(`${protocol}//${window.location.host}/ws`);
ws.binaryType = 'arraybuffer';
ws.onopen = () => {
connectBtn.style.display = 'none';
inviaCambioTG(tgInput.value);
};
ws.onmessage = (event) => {
if (event.data instanceof ArrayBuffer) {
playPCM320(event.data);
} else if (typeof event.data === 'string') {
const data = JSON.parse(event.data);
// --- GESTIONE ERRORE SERVER PIENO ---
if (data.action === "error") {
displayScreen.innerText = "SERVER PIENO";
displayScreen.style.background = "#e74c3c"; // Rosso[cite: 5]
statusText.innerText = "❌ " + data.text;
statusText.style.color = "#e74c3c";
return;
}
if (data.action === "display") {
displayScreen.innerText = data.text;
statusText.style.color = "#bdc3c7"; // Ripristina il colore di default del testo[cite: 5]
if (data.active) {
displayScreen.style.background = "#e67e22"; // Arancione durante RX[cite: 5]
} else {
displayScreen.style.background = "#1abc9c"; // Verde in Standby[cite: 5]
}
}
}
};
ws.onclose = () => {
// Se il display non è in stato di errore "SERVER PIENO", mostra la riconnessione standard[cite: 5]
if (displayScreen.innerText !== "SERVER PIENO") {
statusText.innerText = "Connessione persa. Riconnessione...";
statusText.style.color = "#bdc3c7";
connectBtn.style.display = 'block';
connectBtn.innerText = "Riconnetti Ricevitore";
}
};
}
// --- FUNZIONE PER CAMBIARE TG ---
function inviaCambioTG(nuovoTG) {
if (!nuovoTG || nuovoTG <= 0) return;
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ action: "cambia_tg", tg: nuovoTG }));
displayScreen.innerText = "SINTONIA...";
statusText.innerText = "Sintonizzato su TG " + nuovoTG;
} else {
statusText.innerText = "Pronto a sintonizzare TG " + nuovoTG;
}
}
// Scelta Rapida dai tasti prefissati
function setQuickTg(valore) {
tgInput.value = valore;
inviaCambioTG(valore);
}
// --- EVENTI ---
connectBtn.addEventListener('click', startAudio);
setTgBtn.addEventListener('click', () => inviaCambioTG(tgInput.value));
// Invio anche premendo "Invio" sulla tastiera dentro la casella di testo
tgInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
inviaCambioTG(tgInput.value);
}
});