71 lines
3.1 KiB
HTML
71 lines
3.1 KiB
HTML
<!DOCTYPE html>
|
|
<html><head><title>Voice Test</title></head><body>
|
|
<h1>Voice Capture Test</h1>
|
|
<button id="btn" style="padding:20px;font-size:24px;background:#2ecc71;color:#fff;border:none;border-radius:12px;cursor:pointer;">Start Mic</button>
|
|
<div id="log" style="font-family:monospace;white-space:pre;margin-top:20px;"></div>
|
|
<script src="https://cdn.socket.io/4.7.4/socket.io.min.js"></script>
|
|
<script>
|
|
const log = document.getElementById('log');
|
|
function addLog(msg) { log.textContent += msg + '\n'; console.log(msg); }
|
|
|
|
let voiceStream, audioCtx, voiceProcessor, voiceSocket;
|
|
let debugCount = 0;
|
|
|
|
document.getElementById('btn').onclick = async () => {
|
|
try {
|
|
addLog('1. Requesting mic...');
|
|
voiceStream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true } });
|
|
addLog('2. Got stream: ' + voiceStream.getTracks().map(t => t.label + ' ' + t.readyState).join(', '));
|
|
|
|
audioCtx = new AudioContext({ sampleRate: 24000 });
|
|
if (audioCtx.state === 'suspended') await audioCtx.resume();
|
|
addLog('3. AudioContext: state=' + audioCtx.state + ' sampleRate=' + audioCtx.sampleRate);
|
|
|
|
const source = audioCtx.createMediaStreamSource(voiceStream);
|
|
voiceProcessor = audioCtx.createScriptProcessor(2048, 1, 1);
|
|
addLog('4. ScriptProcessor created');
|
|
|
|
voiceProcessor.onaudioprocess = (e) => {
|
|
debugCount++;
|
|
const pcm = e.inputBuffer.getChannelData(0);
|
|
const maxVal = Math.max(...Array.from(pcm).map(Math.abs));
|
|
if (debugCount <= 10) addLog('5. onaudioprocess #' + debugCount + ' samples=' + pcm.length + ' max=' + maxVal.toFixed(4));
|
|
|
|
if (!voiceSocket || !voiceSocket.connected) return;
|
|
const int16 = new Int16Array(pcm.length);
|
|
for (let i = 0; i < pcm.length; i++) {
|
|
const s = Math.max(-1, Math.min(1, pcm[i]));
|
|
int16[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
|
|
}
|
|
voiceSocket.emit('voice_data', int16.buffer);
|
|
};
|
|
|
|
const silentGain = audioCtx.createGain();
|
|
silentGain.gain.value = 0;
|
|
source.connect(voiceProcessor);
|
|
voiceProcessor.connect(silentGain);
|
|
silentGain.connect(audioCtx.destination);
|
|
addLog('6. Audio chain connected: source→processor→gain(0)→destination');
|
|
|
|
addLog('7. Connecting to voice server...');
|
|
voiceSocket = io('https://voicegrech.mkn8n.ru', { transports: ['websocket'] });
|
|
voiceSocket.on('connect', () => {
|
|
addLog('8. Socket connected: ' + voiceSocket.id);
|
|
voiceSocket.emit('voice_join', { world_id: 'test', x: 0, y: 0, name: 'Tester' });
|
|
addLog('9. Sent voice_join. Speak into mic — watch onaudioprocess logs above!');
|
|
});
|
|
voiceSocket.on('connect_error', (err) => addLog('ERROR: ' + err.message));
|
|
|
|
voiceSocket.on('voice_in', (payload) => {
|
|
addLog('VOICE_IN from ' + payload.meta.name + ' vol=' + payload.volume + ' bytes=' + payload.data.byteLength);
|
|
});
|
|
|
|
document.getElementById('btn').textContent = 'Listening...';
|
|
document.getElementById('btn').style.background = '#e74c3c';
|
|
} catch(e) {
|
|
addLog('ERROR: ' + e.message + '\n' + e.stack);
|
|
}
|
|
};
|
|
</script>
|
|
</body></html>
|