intiial
Some checks failed
Deploy Dev / Build (pull_request) Successful in 1m19s
Deploy Dev / Push (pull_request) Failing after 1s
Deploy Dev / Deploy dev (pull_request) Has been skipped

This commit is contained in:
Egor Matveev
2025-11-24 02:36:02 +03:00
commit aaaa29a5a5
8 changed files with 435 additions and 0 deletions

129
templates/call.html Normal file
View File

@@ -0,0 +1,129 @@
<html>
<head>
<script>
microOn = false;
function changeMicro() {
microOn = !microOn;
const micOn = document.getElementById("micOn");
const micOff = document.getElementById("micOff");
if (microOn) {
micOn.hidden = false;
micOff.hidden = true;
} else {
micOn.hidden = true;
micOff.hidden = false;
}
}
async function getMicrophoneStream() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: false
});
console.log("Microphone access granted, stream object:", stream);
return stream;
} catch (err) {
console.error(`Error accessing microphone: ${err}`);
// Handle cases where the user denies permission or no mic is available
}
}
async function connect() {
const urlParams = new URLSearchParams(window.location.search);
const callId = urlParams.get('call_id');
const socket = new WebSocket("/connect?call_id=" + callId);
socket.addEventListener("open", async () => {
console.log("WebSocket открыт");
// Запрашиваем доступ к микрофону
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
// Создаём AudioContext
const audioContext = new AudioContext();
// Создаём источник из микрофона
const source = audioContext.createMediaStreamSource(stream);
// ScriptProcessorNode является устаревшим, но всё ещё работает
const processor = audioContext.createScriptProcessor(4096, 1, 1);
source.connect(processor);
processor.connect(audioContext.destination); // необязательно, но нужно для запуска
processor.onaudioprocess = (audioEvent) => {
const input = audioEvent.inputBuffer.getChannelData(0); // Float32Array
// Конвертируем в 16-bit PCM (часто требуется серверам)
const pcm = floatTo16BitPCM(input);
// Отправляем через WebSocket
if (socket.readyState === WebSocket.OPEN) {
socket.send(pcm);
}
};
});
socket.binaryType = "arraybuffer";
socket.onmessage = (msg) => {
const int16 = new Int16Array(msg.data);
playPCM(int16);
};
// Функция конвертации Float32 → Int16
function floatTo16BitPCM(float32Array) {
const buffer = new ArrayBuffer(float32Array.length * 2);
const view = new DataView(buffer);
for (let i = 0; i < float32Array.length; i++) {
let s = Math.max(-1, Math.min(1, float32Array[i]));
view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
return buffer;
}
const audioContext = new AudioContext();
// Очередь буферов, чтобы звук шел плавно
const playQueue = [];
// Функция для воспроизведения PCM
function playPCM(int16Array) {
const float32 = new Float32Array(int16Array.length);
for (let i = 0; i < int16Array.length; i++) {
float32[i] = int16Array[i] / 0x7fff;
}
const audioBuffer = audioContext.createBuffer(1, float32.length, audioContext.sampleRate);
audioBuffer.getChannelData(0).set(float32);
playQueue.push(audioBuffer);
if (playQueue.length === 1) {
playNext();
}
}
function playNext() {
if (playQueue.length === 0) return;
const buffer = playQueue[0];
const source = audioContext.createBufferSource();
source.buffer = buffer;
source.connect(audioContext.destination);
source.onended = () => {
playQueue.shift();
playNext();
};
source.start();
}
}
connect();
</script>
</head>
<body>
<p id="micOff">Micro is off</p> <p hidden id="micOn">Micro is on</p> <button onclick="changeMicro();">change</button>
</body>
</html>

25
templates/index.html Normal file
View File

@@ -0,0 +1,25 @@
<html>
<head>
<script>
function startCall() {
fetch("/call", {
method: "POST",
}).then(response => response.json()).then(
(data) => {
const element = document.getElementById("call");
element.href = "/call?call_id=" + data.call_id;
element.hidden = false;
}
)
}
</script>
</head>
<body>
<button onclick="startCall();">
Start call
</button>
<a hidden id="call">
Go to call
</a>
</body>
</html>