59856bd4fc
Services: Pipecat agent (LiveKit + Claude + Whisper STT + Piper TTS), SpeechBrain analysis service, Piper TTS service, web client (nginx). Includes LiveKit server config and docker-compose orchestration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
78 lines
2.3 KiB
JavaScript
Executable File
78 lines
2.3 KiB
JavaScript
Executable File
const LIVEKIT_URL = "wss://voice.hobbs.farm";
|
|
let room = null;
|
|
|
|
async function connect() {
|
|
setStatus("connecting", "Connecting...");
|
|
try {
|
|
// Request a token from the token service
|
|
const response = await fetch("/api/token", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ identity: "jeremy-" + Date.now() }),
|
|
});
|
|
if (!response.ok) throw new Error("Failed to get token");
|
|
const { token } = await response.json();
|
|
|
|
room = new LivekitClient.Room({
|
|
audioCaptureDefaults: {
|
|
echoCancellation: true,
|
|
noiseSuppression: true,
|
|
autoGainControl: true,
|
|
},
|
|
});
|
|
|
|
room.on(LivekitClient.RoomEvent.Connected, () => {
|
|
setStatus("connected", "Connected");
|
|
document.getElementById("connectBtn").style.display = "none";
|
|
document.getElementById("disconnectBtn").style.display = "block";
|
|
});
|
|
|
|
room.on(LivekitClient.RoomEvent.Disconnected, () => {
|
|
setStatus("disconnected", "Disconnected");
|
|
document.getElementById("connectBtn").style.display = "block";
|
|
document.getElementById("disconnectBtn").style.display = "none";
|
|
});
|
|
|
|
room.on(LivekitClient.RoomEvent.TrackSubscribed, (track) => {
|
|
if (track.kind === LivekitClient.Track.Kind.Audio) {
|
|
const element = track.attach();
|
|
document.body.appendChild(element);
|
|
element.play();
|
|
}
|
|
});
|
|
|
|
await room.connect(LIVEKIT_URL, token);
|
|
await room.localParticipant.setMicrophoneEnabled(true);
|
|
} catch (err) {
|
|
setStatus("disconnected", "Error: " + err.message);
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
async function disconnect() {
|
|
if (room) {
|
|
await room.disconnect();
|
|
room = null;
|
|
}
|
|
}
|
|
|
|
function setMode(mode) {
|
|
document.getElementById("modeDisplay").textContent = "Mode: " + mode;
|
|
if (room && room.localParticipant) {
|
|
const encoder = new TextEncoder();
|
|
room.localParticipant.publishData(
|
|
encoder.encode(JSON.stringify({ type: "mode_change", mode })),
|
|
{ reliable: true }
|
|
);
|
|
}
|
|
}
|
|
|
|
function setStatus(state, text) {
|
|
const el = document.getElementById("status");
|
|
el.className = "status " + state;
|
|
el.textContent = text;
|
|
}
|
|
|
|
document.getElementById("connectBtn").addEventListener("click", connect);
|
|
document.getElementById("disconnectBtn").addEventListener("click", disconnect);
|