diff --git a/assets/js/voice.js b/assets/js/voice.js index 610337a..ca2b539 100644 --- a/assets/js/voice.js +++ b/assets/js/voice.js @@ -24,7 +24,7 @@ class VoiceChannel { this.pollInterval = null; this.canSpeak = true; this.remoteAudios = {}; // userId -> Audio element - this.isMuted = false; + this.isSelfMuted = false; this.isDeafened = false; this.audioContext = null; @@ -125,7 +125,7 @@ class VoiceChannel { } this.localStream = await navigator.mediaDevices.getUserMedia(constraints); console.log('Microphone access granted'); - this.setMute(true); + this.setMute(false); // Join unmuted by default (self-mute off) // Always setup VOX logic for volume meter and detection this.setupVOX(); @@ -166,7 +166,7 @@ class VoiceChannel { if (!this.myPeerId || !this.currentChannelId) return; try { - const resp = await fetch(`api_v1_voice.php?action=poll&room=${this.currentChannelId}&peer_id=${this.myPeerId}&is_muted=${this.isMuted ? 1 : 0}&is_deafened=${this.isDeafened ? 1 : 0}`); + const resp = await fetch(`api_v1_voice.php?action=poll&room=${this.currentChannelId}&peer_id=${this.myPeerId}&is_muted=${this.isSelfMuted ? 1 : 0}&is_deafened=${this.isDeafened ? 1 : 0}`); const data = await resp.json(); if (data.success) { @@ -424,12 +424,11 @@ class VoiceChannel { console.log('updateMuteState: shouldTalk =', shouldTalk, 'mode =', this.settings.mode, 'canSpeak =', this.canSpeak); if (this.isTalking !== shouldTalk) { this.isTalking = shouldTalk; - this.setMute(!shouldTalk); + this.applyAudioState(); this.updateSpeakingUI(window.currentUserId, shouldTalk); // Notify others const msg = { type: 'voice_speaking', channel_id: this.currentChannelId, user_id: window.currentUserId, speaking: shouldTalk }; - // ... (rest of method remains same, but I'll update it for clarity) if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify(msg)); } else { @@ -440,18 +439,25 @@ class VoiceChannel { } } - setMute(mute) { - this.isMuted = mute; + applyAudioState() { if (this.localStream) { - console.log('Setting mute to:', mute); - this.localStream.getAudioTracks().forEach(track => { track.enabled = !mute; }); + const shouldTransmit = !this.isSelfMuted && this.isTalking && this.canSpeak; + console.log('applyAudioState: transmitting =', shouldTransmit, '(selfMuted=', this.isSelfMuted, 'talking=', this.isTalking, 'canSpeak=', this.canSpeak, ')'); + this.localStream.getAudioTracks().forEach(track => { + track.enabled = shouldTransmit; + }); } this.updateUserPanelButtons(); } + setMute(mute) { + this.isSelfMuted = mute; + this.applyAudioState(); + } + toggleMute() { if (this.canSpeak === false) return; - this.setMute(!this.isMuted); + this.setMute(!this.isSelfMuted); } toggleDeafen() { @@ -462,13 +468,10 @@ class VoiceChannel { if (!this.isDeafened) audio.volume = this.settings.outputVolume || 1.0; }); // If we deafen, we usually also mute in Discord - if (this.isDeafened && !this.isMuted) { + if (this.isDeafened && !this.isSelfMuted) { this.setMute(true); - } else if (!this.isDeafened && this.isMuted) { - // Not necessarily unmute when undeafen, but often expected - // Let's just update UI } - this.updateUserPanelButtons(); + this.applyAudioState(); } setOutputVolume(vol) { @@ -480,14 +483,11 @@ class VoiceChannel { setInputVolume(vol) { this.settings.inputVolume = parseFloat(vol); - // We could use a GainNode here, but for simplicity we'll just store it. - // If we want to actually change the transmitted volume, we need to insert a GainNode in the stream. } async setInputDevice(deviceId) { this.settings.inputDevice = deviceId; if (this.currentChannelId && this.localStream) { - // Re-join or switch track const constraints = { audio: { echoCancellation: this.settings.echoCancellation, @@ -510,7 +510,7 @@ class VoiceChannel { this.localStream.getTracks().forEach(t => t.stop()); this.localStream = newStream; this.setupVOX(); - this.setMute(this.isMuted); + this.applyAudioState(); } } @@ -549,7 +549,7 @@ class VoiceChannel { this.localStream.getTracks().forEach(t => t.stop()); this.localStream = newStream; this.setupVOX(); - this.setMute(this.isMuted); + this.applyAudioState(); } catch (e) { console.error('Failed to update audio constraints:', e); } @@ -560,7 +560,7 @@ class VoiceChannel { const btnMute = document.getElementById('btn-panel-mute'); const btnDeafen = document.getElementById('btn-panel-deafen'); - let displayMuted = this.isMuted; + let displayMuted = this.isSelfMuted; if (this.canSpeak === false) { displayMuted = true; } diff --git a/assets/pasted-20260219-144524-4a4fe8eb.png b/assets/pasted-20260219-144524-4a4fe8eb.png new file mode 100644 index 0000000..40f7482 Binary files /dev/null and b/assets/pasted-20260219-144524-4a4fe8eb.png differ diff --git a/assets/pasted-20260219-145037-ae47b380.png b/assets/pasted-20260219-145037-ae47b380.png new file mode 100644 index 0000000..89d2522 Binary files /dev/null and b/assets/pasted-20260219-145037-ae47b380.png differ diff --git a/index.php b/index.php index a2bc25e..cd3e91d 100644 --- a/index.php +++ b/index.php @@ -319,7 +319,7 @@ if ($is_dm_view) { // Fetch voice sessions for the sidebar $stmt_vs = db()->prepare(" - SELECT vs.channel_id, vs.user_id, u.username, u.display_name, u.avatar_url + SELECT vs.channel_id, vs.user_id, vs.is_muted, vs.is_deafened, u.username, u.display_name, u.avatar_url FROM voice_sessions vs JOIN users u ON vs.user_id = u.id WHERE vs.last_seen > ? @@ -573,7 +573,12 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';