added support for microphone
This commit is contained in:
+76
-3
@@ -69,7 +69,7 @@
|
||||
<div id="hint">Click to capture mouse | ESC to release</div>
|
||||
<div id="faq">
|
||||
<h2>FAQ</h2>
|
||||
<div class="faq-item">
|
||||
<div class="faq-item">
|
||||
<h3>What is this?</h3>
|
||||
<p>This is the unloze webclient. You can play Zombie escape, Minigame and Zombie Riot with it. Visit our website at: https://unloze.com</p>
|
||||
</div>
|
||||
@@ -93,6 +93,7 @@
|
||||
<div class="faq-item">
|
||||
<h3>Keybinds</h3>
|
||||
<p>v: crouch key currently</p>
|
||||
<p>k: microphone key. Browser will ask for microphone permission on first use</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -107,12 +108,73 @@
|
||||
let sessionId = null;
|
||||
let pointerLocked = false;
|
||||
|
||||
// ── Microphone / PTT ──────────────────────────────────────────────────
|
||||
let micStream = null;
|
||||
let audioContext = null;
|
||||
let micProcessor = null;
|
||||
let micActive = false;
|
||||
|
||||
async function initMic() {
|
||||
if (micStream) return true;
|
||||
try {
|
||||
micStream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
sampleRate: 48000,
|
||||
channelCount: 1,
|
||||
echoCancellation: true,
|
||||
noiseSuppression: true,
|
||||
},
|
||||
video: false,
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('[mic] getUserMedia failed:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function startMic() {
|
||||
if (micActive) return;
|
||||
const ok = await initMic();
|
||||
if (!ok) return;
|
||||
|
||||
audioContext = new AudioContext({ sampleRate: 48000 });
|
||||
const source = audioContext.createMediaStreamSource(micStream);
|
||||
|
||||
micProcessor = audioContext.createScriptProcessor(4096, 1, 1);
|
||||
micProcessor.onaudioprocess = e => {
|
||||
if (!micActive) return;
|
||||
const float32 = e.inputBuffer.getChannelData(0);
|
||||
const int16 = new Int16Array(float32.length);
|
||||
for (let i = 0; i < float32.length; i++) {
|
||||
int16[i] = Math.max(-32768, Math.min(32767, float32[i] * 32768));
|
||||
}
|
||||
if (sigWs && sigWs.readyState === WebSocket.OPEN) {
|
||||
sigWs.send(int16.buffer);
|
||||
}
|
||||
};
|
||||
|
||||
source.connect(micProcessor);
|
||||
micProcessor.connect(audioContext.destination);
|
||||
micActive = true;
|
||||
}
|
||||
|
||||
function stopMic() {
|
||||
if (!micActive) return;
|
||||
micActive = false;
|
||||
if (micProcessor) { micProcessor.disconnect(); micProcessor = null; }
|
||||
if (audioContext) { audioContext.close(); audioContext = null; }
|
||||
if (sigWs && sigWs.readyState === WebSocket.OPEN) {
|
||||
sigWs.send(JSON.stringify({ type: 'mic_stop' }));
|
||||
}
|
||||
}
|
||||
|
||||
// ── WebRTC ────────────────────────────────────────────────────────────
|
||||
function setStatus(msg) {
|
||||
statusEl.textContent = msg;
|
||||
console.log('[client]', msg);
|
||||
}
|
||||
|
||||
// ── WebRTC ────────────────────────────────────────────────────────────
|
||||
async function setupPeerConnection() {
|
||||
pc = new RTCPeerConnection({
|
||||
iceServers: [{ urls: 'stun:stun.cloudflare.com:3478' }],
|
||||
@@ -154,6 +216,7 @@
|
||||
};
|
||||
|
||||
sigWs.onmessage = async e => {
|
||||
if (e.data instanceof Blob) return;
|
||||
const msg = JSON.parse(e.data);
|
||||
|
||||
if (msg.type === 'session_created') {
|
||||
@@ -185,7 +248,10 @@
|
||||
};
|
||||
|
||||
sigWs.onerror = () => setStatus('Connection error');
|
||||
sigWs.onclose = () => setStatus('Disconnected');
|
||||
sigWs.onclose = () => {
|
||||
stopMic();
|
||||
setStatus('Disconnected');
|
||||
};
|
||||
}
|
||||
|
||||
function waitForIce() {
|
||||
@@ -228,6 +294,7 @@
|
||||
window.addEventListener('blur', () => {
|
||||
heldKeys.forEach(key => sendInput({ type: 'key_up', key }));
|
||||
heldKeys.clear();
|
||||
stopMic();
|
||||
});
|
||||
|
||||
let lastMouseMove = 0;
|
||||
@@ -296,6 +363,9 @@
|
||||
if (!pointerLocked) return;
|
||||
e.preventDefault();
|
||||
const key = KEY_MAP[e.code] || e.code.toLowerCase();
|
||||
if (e.code === 'KeyK' && !e.repeat) {
|
||||
startMic();
|
||||
}
|
||||
heldKeys.add(key);
|
||||
sendInput({ type: 'key_down', key });
|
||||
});
|
||||
@@ -308,6 +378,9 @@
|
||||
if (!pointerLocked) return;
|
||||
e.preventDefault();
|
||||
const key = KEY_MAP[e.code] || e.code.toLowerCase();
|
||||
if (e.code === 'KeyK') {
|
||||
stopMic();
|
||||
}
|
||||
heldKeys.delete(key);
|
||||
sendInput({ type: 'key_up', key });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user