VOX a moitié fonctionnel
This commit is contained in:
parent
04cad1c49b
commit
24671bdbc7
@ -36,9 +36,15 @@ class VoiceChannel {
|
||||
// Ignore if in input field
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||||
|
||||
if (this.settings.mode === 'ptt' && e.key.toLowerCase() === this.settings.pttKey.toLowerCase()) {
|
||||
if (this.settings.mode !== 'ptt') return;
|
||||
|
||||
const isMatch = e.key.toLowerCase() === this.settings.pttKey.toLowerCase() ||
|
||||
(e.code && e.code.toLowerCase() === this.settings.pttKey.toLowerCase()) ||
|
||||
(this.settings.pttKey === '0' && e.code === 'Numpad0');
|
||||
|
||||
if (isMatch) {
|
||||
if (!this.pttPressed) {
|
||||
console.log('PTT Key Pressed:', e.key);
|
||||
console.log('PTT Key Pressed:', e.key, e.code, 'Expected:', this.settings.pttKey);
|
||||
this.pttPressed = true;
|
||||
this.updateMuteState();
|
||||
}
|
||||
@ -46,8 +52,14 @@ class VoiceChannel {
|
||||
});
|
||||
|
||||
window.addEventListener('keyup', (e) => {
|
||||
if (this.settings.mode === 'ptt' && e.key.toLowerCase() === this.settings.pttKey.toLowerCase()) {
|
||||
console.log('PTT Key Released:', e.key);
|
||||
if (this.settings.mode !== 'ptt') return;
|
||||
|
||||
const isMatch = e.key.toLowerCase() === this.settings.pttKey.toLowerCase() ||
|
||||
(e.code && e.code.toLowerCase() === this.settings.pttKey.toLowerCase()) ||
|
||||
(this.settings.pttKey === '0' && e.code === 'Numpad0');
|
||||
|
||||
if (isMatch) {
|
||||
console.log('PTT Key Released:', e.key, e.code, 'Expected:', this.settings.pttKey);
|
||||
this.pttPressed = false;
|
||||
this.updateMuteState();
|
||||
}
|
||||
@ -253,38 +265,59 @@ class VoiceChannel {
|
||||
}
|
||||
|
||||
setupVOX() {
|
||||
if (this.audioContext) this.audioContext.close();
|
||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
this.analyser = this.audioContext.createAnalyser();
|
||||
this.microphone = this.audioContext.createMediaStreamSource(this.localStream);
|
||||
this.scriptProcessor = this.audioContext.createScriptProcessor(2048, 1, 1);
|
||||
this.microphone.connect(this.analyser);
|
||||
this.analyser.connect(this.scriptProcessor);
|
||||
this.scriptProcessor.connect(this.audioContext.destination);
|
||||
|
||||
this.scriptProcessor.onaudioprocess = () => {
|
||||
const array = new Uint8Array(this.analyser.frequencyBinCount);
|
||||
this.analyser.getByteFrequencyData(array);
|
||||
let values = 0;
|
||||
for (let i = 0; i < array.length; i++) values += array[i];
|
||||
const average = values / array.length;
|
||||
|
||||
// Log sometimes for debugging VOX
|
||||
if (Math.random() < 0.01) console.log('VOX Avg:', average, 'Threshold:', this.settings.voxThreshold * 255);
|
||||
|
||||
if (average > (this.settings.voxThreshold * 255)) {
|
||||
this.lastVoiceTime = Date.now();
|
||||
if (!this.voxActive) {
|
||||
this.voxActive = true;
|
||||
this.updateMuteState();
|
||||
}
|
||||
} else {
|
||||
if (this.voxActive && Date.now() - this.lastVoiceTime > this.voxHoldTime) {
|
||||
this.voxActive = false;
|
||||
this.updateMuteState();
|
||||
}
|
||||
if (this.audioContext) {
|
||||
if (this.audioContext.state === 'suspended') {
|
||||
this.audioContext.resume();
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
this.analyser = this.audioContext.createAnalyser();
|
||||
this.analyser.fftSize = 512;
|
||||
this.microphone = this.audioContext.createMediaStreamSource(this.localStream);
|
||||
this.scriptProcessor = this.audioContext.createScriptProcessor(2048, 1, 1);
|
||||
|
||||
this.microphone.connect(this.analyser);
|
||||
this.analyser.connect(this.scriptProcessor);
|
||||
this.scriptProcessor.connect(this.audioContext.destination);
|
||||
|
||||
this.currentVolume = 0;
|
||||
|
||||
this.scriptProcessor.onaudioprocess = () => {
|
||||
const array = new Uint8Array(this.analyser.frequencyBinCount);
|
||||
this.analyser.getByteFrequencyData(array);
|
||||
let values = 0;
|
||||
for (let i = 0; i < array.length; i++) values += array[i];
|
||||
const average = values / array.length;
|
||||
this.currentVolume = average / 255;
|
||||
|
||||
if (this.settings.mode !== 'vox') {
|
||||
this.voxActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentVolume > this.settings.voxThreshold) {
|
||||
this.lastVoiceTime = Date.now();
|
||||
if (!this.voxActive) {
|
||||
this.voxActive = true;
|
||||
this.updateMuteState();
|
||||
}
|
||||
} else {
|
||||
if (this.voxActive && Date.now() - this.lastVoiceTime > this.voxHoldTime) {
|
||||
this.voxActive = false;
|
||||
this.updateMuteState();
|
||||
}
|
||||
}
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('Failed to setup VOX:', e);
|
||||
}
|
||||
}
|
||||
|
||||
getVolume() {
|
||||
return this.currentVolume || 0;
|
||||
}
|
||||
|
||||
updateMuteState() {
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
{"from":"a0645f38fb2bbdb5","to":"1ca650259dcce60e","data":{"type":"voice_speaking","channel_id":"22","user_id":3,"speaking":true},"time":1771339536024}
|
||||
{"from":"a0645f38fb2bbdb5","to":"1ca650259dcce60e","data":{"type":"voice_speaking","channel_id":"22","user_id":3,"speaking":false},"time":1771339536956}
|
||||
@ -1 +1 @@
|
||||
[]
|
||||
{"45a8f0c9dde7c4a2":{"id":"45a8f0c9dde7c4a2","user_id":2,"name":"swefpifh ᵇʰᶠʳ","avatar_url":"","last_seen":1771340822998}}
|
||||
@ -1,8 +0,0 @@
|
||||
{"from":"9afce7ba24e9091b","to":"a39fca0ca87f4e62","data":{"type":"offer","offer":{"type":"offer","sdp":"v=0\r\no=mozilla...THIS_IS_SDPARTA-99.0 2993514939591859431 0 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=sendrecv\r\na=fingerprint:sha-256 E9:89:B8:DE:41:F8:AD:79:17:A8:4D:03:2D:53:FC:15:5A:3B:B4:CA:45:A6:F7:EB:C7:F0:01:7C:0B:29:91:F1\r\na=group:BUNDLE 0\r\na=ice-options:trickle\r\na=msid-semantic:WMS *\r\nm=audio 9 UDP\/TLS\/RTP\/SAVPF 109 9 0 8 101\r\nc=IN IP4 0.0.0.0\r\na=sendrecv\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2\/recvonly urn:ietf:params:rtp-hdrext:csrc-audio-level\r\na=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap-allow-mixed\r\na=fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\na=fmtp:101 0-15\r\na=ice-pwd:cacccb2562e0f73ce1667f9773fb670d\r\na=ice-ufrag:2b377091\r\na=mid:0\r\na=msid:{5d44acc3-9af3-4110-98ed-2de218fc7450} {ed9d31cb-0fc7-497e-8c79-4cc9159d5f97}\r\na=rtcp-mux\r\na=rtpmap:109 opus\/48000\/2\r\na=rtpmap:9 G722\/8000\/1\r\na=rtpmap:0 PCMU\/8000\r\na=rtpmap:8 PCMA\/8000\r\na=rtpmap:101 telephone-event\/8000\r\na=setup:actpass\r\na=ssrc:2872346815 cname:{74df363a-4160-4031-a110-5285ddfe55ff}\r\n"}},"time":1771339604154}
|
||||
{"from":"9afce7ba24e9091b","to":"a39fca0ca87f4e62","data":{"type":"ice_candidate","candidate":{"candidate":"candidate:0 1 UDP 2122252543 192.168.26.26 61807 typ host","sdpMLineIndex":0,"sdpMid":"0","usernameFragment":"2b377091"}},"time":1771339604155}
|
||||
{"from":"9afce7ba24e9091b","to":"a39fca0ca87f4e62","data":{"type":"ice_candidate","candidate":{"candidate":"candidate:2 1 TCP 2105524479 192.168.26.26 9 typ host tcptype active","sdpMLineIndex":0,"sdpMid":"0","usernameFragment":"2b377091"}},"time":1771339604161}
|
||||
{"from":"9afce7ba24e9091b","to":"a39fca0ca87f4e62","data":{"type":"ice_candidate","candidate":{"candidate":"candidate:2 2 TCP 2105524478 192.168.26.26 9 typ host tcptype active","sdpMLineIndex":0,"sdpMid":"0","usernameFragment":"2b377091"}},"time":1771339604164}
|
||||
{"from":"9afce7ba24e9091b","to":"a39fca0ca87f4e62","data":{"type":"ice_candidate","candidate":{"candidate":"candidate:0 2 UDP 2122252542 192.168.26.26 61808 typ host","sdpMLineIndex":0,"sdpMid":"0","usernameFragment":"2b377091"}},"time":1771339604170}
|
||||
{"from":"9afce7ba24e9091b","to":"a39fca0ca87f4e62","data":{"type":"ice_candidate","candidate":{"candidate":"","sdpMLineIndex":0,"sdpMid":"0","usernameFragment":"2b377091"}},"time":1771339604171}
|
||||
{"from":"9afce7ba24e9091b","to":"a39fca0ca87f4e62","data":{"type":"ice_candidate","candidate":{"candidate":"candidate:1 1 UDP 1686052863 78.246.210.10 30532 typ srflx raddr 192.168.26.26 rport 61807","sdpMLineIndex":0,"sdpMid":"0","usernameFragment":"2b377091"}},"time":1771339604172}
|
||||
{"from":"9afce7ba24e9091b","to":"a39fca0ca87f4e62","data":{"type":"ice_candidate","candidate":{"candidate":"candidate:1 2 UDP 1686052862 78.246.210.10 30534 typ srflx raddr 192.168.26.26 rport 61808","sdpMLineIndex":0,"sdpMid":"0","usernameFragment":"2b377091"}},"time":1771339604175}
|
||||
@ -1 +1 @@
|
||||
{"1e0fff021b7ad021":{"id":"1e0fff021b7ad021","user_id":3,"name":"swefheim","avatar_url":"","last_seen":1771339631904},"9afce7ba24e9091b":{"id":"9afce7ba24e9091b","user_id":2,"name":"swefpifh ᵇʰᶠʳ","avatar_url":"","last_seen":1771339632155}}
|
||||
{"1e0897d1dcb980dc":{"id":"1e0897d1dcb980dc","user_id":3,"name":"swefheim","avatar_url":"","last_seen":1771340822155}}
|
||||
88
index.php
88
index.php
@ -1232,10 +1232,14 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<div id="vox-settings-container" style="<?php echo ($user['voice_mode'] ?? 'vox') == 'vox' ? '' : 'display: none;'; ?>">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-white fw-bold">Input Sensitivity</label>
|
||||
<input type="range" name="voice_vox_threshold" class="form-range" min="0" max="1" step="0.01" value="<?php echo $user['voice_vox_threshold'] ?? 0.1; ?>">
|
||||
<div class="voice-meter-container mb-2" style="height: 8px; background: #1e1f22; border-radius: 4px; overflow: hidden; position: relative;">
|
||||
<div id="voice-meter-bar" style="height: 100%; width: 0%; background: #23a559; transition: width 0.1s;"></div>
|
||||
<div id="voice-meter-threshold" style="position: absolute; top: 0; bottom: 0; width: 2px; background: #f23f43; z-index: 2;"></div>
|
||||
</div>
|
||||
<input type="range" name="voice_vox_threshold" id="vox_threshold_input" class="form-range" min="0" max="1" step="0.01" value="<?php echo $user['voice_vox_threshold'] ?? 0.1; ?>">
|
||||
<div class="d-flex justify-content-between small text-muted mt-1">
|
||||
<span>Loud</span>
|
||||
<span>Quiet</span>
|
||||
<span>Sensitive</span>
|
||||
<span>Loud Only</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1311,6 +1315,57 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
pttInput.value = e.key;
|
||||
});
|
||||
}
|
||||
|
||||
// Voice meter update
|
||||
const voxThresholdInput = document.getElementById('vox_threshold_input');
|
||||
const meterThreshold = document.getElementById('voice-meter-threshold');
|
||||
const meterBar = document.getElementById('voice-meter-bar');
|
||||
|
||||
// Handle voice tab activation for mic preview
|
||||
const voiceTabBtn = document.querySelector('[data-bs-target="#settings-voice"]');
|
||||
if (voiceTabBtn) {
|
||||
voiceTabBtn.addEventListener('shown.bs.tab', async () => {
|
||||
if (window.voiceHandler && !window.voiceHandler.localStream) {
|
||||
try {
|
||||
console.log('Voice tab active, requesting mic for preview...');
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
window.voiceHandler.localStream = stream;
|
||||
window.voiceHandler.setupVOX();
|
||||
} catch (e) {
|
||||
console.error('Failed to get mic for preview:', e);
|
||||
}
|
||||
} else if (window.voiceHandler && window.voiceHandler.localStream) {
|
||||
window.voiceHandler.setupVOX();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (voxThresholdInput && meterThreshold) {
|
||||
const updateThresholdPos = () => {
|
||||
// Threshold input is 0 (loud) to 1 (quiet)
|
||||
// But actually 1.0 means high threshold, so quiet needs more voice
|
||||
// 0.1 means low threshold, easy to trigger.
|
||||
// Meter is 0 to 100%.
|
||||
meterThreshold.style.left = (voxThresholdInput.value * 100) + '%';
|
||||
};
|
||||
voxThresholdInput.addEventListener('input', updateThresholdPos);
|
||||
updateThresholdPos();
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
if (window.voiceHandler && meterBar && document.getElementById('settings-voice').classList.contains('active')) {
|
||||
const volume = window.voiceHandler.getVolume(); // 0 to 1
|
||||
meterBar.style.width = (volume * 100) + '%';
|
||||
|
||||
// Color feedback
|
||||
const threshold = parseFloat(voxThresholdInput.value);
|
||||
if (volume > threshold) {
|
||||
meterBar.style.backgroundColor = '#23a559'; // Green
|
||||
} else {
|
||||
meterBar.style.backgroundColor = '#4f545c'; // Grey
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
|
||||
function handlePTTKeyCapture(e, input) {
|
||||
@ -1352,7 +1407,32 @@ async function handleSaveUserSettings(btn) {
|
||||
|
||||
if (result.success) {
|
||||
btn.innerHTML = '<i class="fa-solid fa-check me-2"></i> Saved!';
|
||||
setTimeout(() => window.location.reload(), 500);
|
||||
|
||||
// Update local voiceHandler settings without reload
|
||||
if (window.voiceHandler) {
|
||||
const mode = formData.get('voice_mode');
|
||||
const pttKey = formData.get('voice_ptt_key');
|
||||
const voxThreshold = parseFloat(formData.get('voice_vox_threshold'));
|
||||
|
||||
window.voiceHandler.settings.mode = mode;
|
||||
window.voiceHandler.settings.pttKey = pttKey;
|
||||
window.voiceHandler.settings.voxThreshold = voxThreshold;
|
||||
|
||||
console.log('Voice settings updated locally:', window.voiceHandler.settings);
|
||||
|
||||
if (mode === 'vox' && !window.voiceHandler.audioContext) {
|
||||
window.voiceHandler.setupVOX();
|
||||
}
|
||||
|
||||
window.voiceHandler.updateVoiceUI();
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
btn.innerHTML = originalContent;
|
||||
btn.disabled = false;
|
||||
// Optional: close modal after save?
|
||||
// bootstrap.Modal.getInstance(document.getElementById('userSettingsModal')).hide();
|
||||
}, 1500);
|
||||
} else {
|
||||
alert('Error: ' + (result.error || 'Unknown error'));
|
||||
btn.disabled = false;
|
||||
|
||||
24
requests.log
24
requests.log
@ -582,3 +582,27 @@
|
||||
2026-02-17 14:46:17 - GET /index.php?server_id=1&channel_id=22 - POST: []
|
||||
{"date":"2026-02-17 14:46:39","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.1","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
2026-02-17 14:46:40 - GET /index.php?server_id=1&channel_id=22 - POST: []
|
||||
{"date":"2026-02-17 14:48:43","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
2026-02-17 14:48:44 - GET /index.php?server_id=1&channel_id=22 - POST: []
|
||||
{"date":"2026-02-17 14:48:59","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"1","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
2026-02-17 14:48:59 - GET /index.php?server_id=1&channel_id=22 - POST: []
|
||||
2026-02-17 14:59:20 - GET / - POST: []
|
||||
2026-02-17 14:59:50 - GET /?fl_project=38443 - POST: []
|
||||
2026-02-17 14:59:50 - GET /?fl_project=38443 - POST: []
|
||||
2026-02-17 15:02:24 - GET /index.php?server_id=1&channel_id=22 - POST: []
|
||||
2026-02-17 15:02:30 - GET /index.php?server_id=1&channel_id=1 - POST: []
|
||||
{"date":"2026-02-17 15:02:45","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
2026-02-17 15:02:57 - GET /index.php - POST: []
|
||||
2026-02-17 15:03:02 - GET /index.php - POST: []
|
||||
{"date":"2026-02-17 15:03:19","method":"POST","post":{"avatar_url":"","display_name":"swefheim","theme":"light","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.06","dnd_mode":"0","sound_notifications":"0"},"session":{"user_id":3},"user_id":3,"db_success":true}
|
||||
2026-02-17 15:03:33 - GET /index.php?server_id=1&channel_id=1 - POST: []
|
||||
{"date":"2026-02-17 15:03:41","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.01","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
{"date":"2026-02-17 15:03:44","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.01","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
{"date":"2026-02-17 15:04:13","method":"POST","post":{"avatar_url":"","display_name":"swefheim","theme":"light","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.38","dnd_mode":"0","sound_notifications":"0"},"session":{"user_id":3},"user_id":3,"db_success":true}
|
||||
2026-02-17 15:04:30 - GET /index.php?server_id=1&channel_id=1 - POST: []
|
||||
{"date":"2026-02-17 15:04:48","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.06","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
2026-02-17 15:04:52 - GET /index.php?server_id=1&channel_id=1 - POST: []
|
||||
{"date":"2026-02-17 15:05:02","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.06","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
{"date":"2026-02-17 15:05:57","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.06","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
2026-02-17 15:06:05 - GET /index.php?server_id=1&channel_id=1 - POST: []
|
||||
2026-02-17 15:06:09 - GET /index.php?server_id=1&channel_id=1 - POST: []
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user