diff --git a/game.js b/game.js
index aa4c81c..5b67ec3 100644
--- a/game.js
+++ b/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';
diff --git a/index.html b/index.html
index 3b45c69..0a78f09 100644
--- a/index.html
+++ b/index.html
@@ -92,6 +92,6 @@
-
+