fix: voice chat — fade in/out chunks, bigger buffer, volume boost to reduce bubbling
This commit is contained in:
parent
a59c84535a
commit
7eef966f6e
47
game.js
47
game.js
|
|
@ -1355,8 +1355,8 @@ function customConfirm(msg, onYes) {
|
|||
console.log('[voice] AudioContext state:', audioCtx.state, 'sampleRate:', audioCtx.sampleRate);
|
||||
|
||||
const source = audioCtx.createMediaStreamSource(voiceStream);
|
||||
voiceProcessor = audioCtx.createScriptProcessor(2048, 1, 1);
|
||||
console.log('[voice] ScriptProcessor created, bufferSize=2048');
|
||||
voiceProcessor = audioCtx.createScriptProcessor(4096, 1, 1);
|
||||
console.log('[voice] ScriptProcessor created, bufferSize=4096');
|
||||
|
||||
voiceProcessor.onaudioprocess = (e) => {
|
||||
if (!voiceActive) return;
|
||||
|
|
@ -1394,25 +1394,44 @@ function customConfirm(msg, onYes) {
|
|||
console.error('[voice] Socket connect error:', err.message);
|
||||
});
|
||||
|
||||
voiceSocket.on('voice_in', (payload) => {
|
||||
// Воспроизводим входящий голос — raw PCM int16
|
||||
const { data, meta, volume } = payload;
|
||||
if (!audioCtx || audioCtx.state === 'closed') return;
|
||||
console.log('[voice] voice_in from', meta.name, 'volume:', volume, 'bytes:', data.byteLength);
|
||||
|
||||
const int16 = new Int16Array(data);
|
||||
const float32 = new Float32Array(int16.length);
|
||||
for (let i = 0; i < int16.length; i++) {
|
||||
float32[i] = int16[i] / (int16[i] < 0 ? 0x8000 : 0x7FFF) * (volume || 1);
|
||||
// Очередь воспроизведения голоса — склеиваем чанки без щелчков
|
||||
const voiceQueue = [];
|
||||
let voicePlaying = false;
|
||||
function playVoiceChunk(float32, volume) {
|
||||
const FADE = 64; // сэмплов для плавного перехода
|
||||
// Fade in начало
|
||||
for (let i = 0; i < FADE && i < float32.length; i++) {
|
||||
float32[i] *= i / FADE;
|
||||
}
|
||||
// Fade out конец
|
||||
for (let i = 0; i < FADE && i < float32.length; i++) {
|
||||
float32[float32.length - 1 - i] *= i / FADE;
|
||||
}
|
||||
const buf = audioCtx.createBuffer(1, float32.length, 24000);
|
||||
buf.getChannelData(0).set(float32);
|
||||
const src = audioCtx.createBufferSource();
|
||||
src.buffer = buf;
|
||||
const gain = audioCtx.createGain();
|
||||
gain.gain.value = 1;
|
||||
gain.gain.value = Math.min(1, volume * 1.5); // усилить тихий голос
|
||||
src.connect(gain).connect(audioCtx.destination);
|
||||
src.start();
|
||||
// Склеиваем: начинаем сразу после предыдущего чанка
|
||||
const when = voicePlaying ? audioCtx.currentTime + 0.02 : audioCtx.currentTime;
|
||||
voicePlaying = true;
|
||||
src.start(when);
|
||||
src.onended = () => { voicePlaying = false; };
|
||||
}
|
||||
|
||||
voiceSocket.on('voice_in', (payload) => {
|
||||
// Воспроизводим входящий голос — raw PCM int16
|
||||
const { data, meta, volume } = payload;
|
||||
if (!audioCtx || audioCtx.state === 'closed') return;
|
||||
|
||||
const int16 = new Int16Array(data);
|
||||
const float32 = new Float32Array(int16.length);
|
||||
for (let i = 0; i < int16.length; i++) {
|
||||
float32[i] = int16[i] / (int16[i] < 0 ? 0x8000 : 0x7FFF);
|
||||
}
|
||||
playVoiceChunk(float32, volume || 1);
|
||||
|
||||
// Индикатор
|
||||
speakingIndicator.style.display = 'block';
|
||||
|
|
|
|||
|
|
@ -92,6 +92,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="game.js?v=16"></script>
|
||||
<script src="game.js?v=17"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue