Demo Update 22

This commit is contained in:
2025-03-30 09:17:31 -04:00
parent 8695dd0297
commit e69d9c5da1
2 changed files with 496 additions and 463 deletions

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Live Voice Assistant with CSM</title>
<title>Real-Time Voice Assistant</title>
<script src="https://cdn.socket.io/4.6.0/socket.io.min.js"></script>
<style>
body {
@@ -89,33 +89,39 @@
transition: all 0.3s ease;
}
#talkButton {
#micButton {
background-color: #4CAF50;
color: white;
width: 200px;
box-shadow: 0 4px 8px rgba(76, 175, 80, 0.3);
}
#talkButton:hover {
#micButton:hover {
background-color: #45a049;
transform: translateY(-2px);
}
#talkButton.recording {
background-color: #f44336;
#micButton.listening {
background-color: #4CAF50;
box-shadow: 0 0 0 rgba(76, 175, 80, 0.4);
animation: pulse 1.5s infinite;
}
#micButton.speaking {
background-color: #f44336;
box-shadow: 0 0 0 rgba(244, 67, 54, 0.4);
animation: pulse 1.5s infinite;
box-shadow: 0 4px 8px rgba(244, 67, 54, 0.3);
}
@keyframes pulse {
0% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.4);
}
50% {
transform: scale(1.05);
70% {
box-shadow: 0 0 0 15px rgba(76, 175, 80, 0);
}
100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(76, 175, 80, 0);
}
}
@@ -126,45 +132,24 @@
color: #657786;
}
.hidden {
display: none;
}
.transcription-info {
font-size: 0.8em;
color: #888;
margin-top: 4px;
text-align: right;
}
.text-only-indicator {
font-size: 0.8em;
color: #e74c3c;
margin-top: 4px;
font-style: italic;
}
.status-message {
text-align: center;
padding: 8px;
margin: 10px 0;
background-color: #f8f9fa;
border-radius: 5px;
color: #666;
font-size: 0.9em;
}
/* Audio visualizer styles */
.visualizer-container {
width: 100%;
height: 120px;
height: 100px;
margin: 15px 0;
border-radius: 10px;
overflow: hidden;
background-color: #000;
background-color: #1a1a1a;
position: relative;
}
.visualizer-container.user {
border: 2px solid #4CAF50;
}
.visualizer-container.ai {
border: 2px solid #2196F3;
}
#visualizer {
width: 100%;
height: 100%;
@@ -176,122 +161,59 @@
top: 10px;
left: 10px;
color: white;
font-size: 0.8em;
font-size: 0.9em;
background-color: rgba(0, 0, 0, 0.5);
padding: 4px 8px;
border-radius: 4px;
}
/* Real-time transcription */
.live-transcription {
position: absolute;
bottom: 10px;
left: 10px;
right: 10px;
color: white;
font-size: 0.9em;
background-color: rgba(0, 0, 0, 0.5);
padding: 8px;
border-radius: 4px;
text-align: center;
max-height: 60px;
overflow-y: auto;
font-style: italic;
}
/* Wave animation for active speaker */
.speaking-wave {
.speech-indicator {
display: inline-block;
margin-left: 5px;
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 5px;
vertical-align: middle;
}
.speaking-wave span {
display: inline-block;
width: 3px;
height: 12px;
margin: 0 1px;
background-color: currentColor;
border-radius: 1px;
animation: speakingWave 1s infinite ease-in-out;
}
.speaking-wave span:nth-child(2) {
animation-delay: 0.1s;
}
.speaking-wave span:nth-child(3) {
animation-delay: 0.2s;
}
.speaking-wave span:nth-child(4) {
animation-delay: 0.3s;
}
@keyframes speakingWave {
0%, 100% {
height: 4px;
}
50% {
height: 12px;
}
}
/* Modern switch for visualizer toggle */
.switch-container {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 10px;
}
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
margin-left: 10px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
.user-speaking {
background-color: #4CAF50;
animation: blink 1s infinite;
}
input:checked + .slider:before {
transform: translateX(26px);
.ai-speaking {
background-color: #2196F3;
animation: blink 1s infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
.connection-status {
padding: 6px 10px;
border-radius: 4px;
font-size: 0.8em;
margin-top: 10px;
text-align: center;
}
.connection-status.connected {
background-color: #d4edda;
color: #155724;
}
.connection-status.connecting {
background-color: #fff3cd;
color: #856404;
}
.connection-status.disconnected {
background-color: #f8d7da;
color: #721c24;
}
/* Toast notification for feedback */
.toast {
position: fixed;
bottom: 20px;
@@ -328,38 +250,27 @@
</style>
</head>
<body>
<h1>Live Voice Assistant with CSM</h1>
<h1>Real-Time Voice Assistant</h1>
<div id="conversation"></div>
<div class="switch-container">
<span>Audio Visualizer</span>
<label class="switch">
<input type="checkbox" id="visualizerToggle" checked>
<span class="slider"></span>
</label>
</div>
<div class="visualizer-container" id="visualizerContainer">
<div class="visualizer-container user" id="visualizerContainer">
<canvas id="visualizer"></canvas>
<div class="visualizer-label" id="visualizerLabel">Listening...</div>
<div class="live-transcription" id="liveTranscription"></div>
</div>
<div id="controls">
<button id="talkButton">Press to Talk</button>
<button id="micButton">Press to Talk</button>
</div>
<div id="status">Connecting to server...</div>
<script>
const socket = io();
const talkButton = document.getElementById('talkButton');
const micButton = document.getElementById('micButton');
const conversation = document.getElementById('conversation');
const status = document.getElementById('status');
const visualizerToggle = document.getElementById('visualizerToggle');
const visualizerContainer = document.getElementById('visualizerContainer');
const visualizerLabel = document.getElementById('visualizerLabel');
const liveTranscription = document.getElementById('liveTranscription');
const canvas = document.getElementById('visualizer');
const canvasCtx = canvas.getContext('2d');
@@ -386,19 +297,6 @@
canvas.height = visualizerContainer.offsetHeight;
}
// Handle visualizer toggle
visualizerToggle.addEventListener('change', function() {
visualizerActive = this.checked;
visualizerContainer.style.display = visualizerActive ? 'block' : 'none';
if (!visualizerActive && visualizerAnimationId) {
cancelAnimationFrame(visualizerAnimationId);
visualizerAnimationId = null;
} else if (visualizerActive && audioAnalyser) {
drawVisualizer();
}
});
// Connect to server
socket.on('connect', () => {
status.textContent = 'Connected to server';
@@ -491,8 +389,8 @@
setupScriptProcessor(stream);
}
// Setup talk button
talkButton.addEventListener('click', toggleTalking);
// Setup mic button
micButton.addEventListener('click', toggleTalking);
// Setup keyboard shortcuts
document.addEventListener('keydown', (e) => {
@@ -618,8 +516,8 @@
if (!sessionActive || isAITalking) return;
isStreaming = true;
talkButton.classList.add('recording');
talkButton.textContent = 'Release to Stop';
micButton.classList.add('listening');
micButton.textContent = 'Release to Stop';
status.textContent = 'Listening...';
visualizerLabel.textContent = 'You are speaking...';
@@ -630,10 +528,6 @@
// Tell server we're starting to speak
socket.emit('start_speaking');
// Clear previous transcriptions
liveTranscription.textContent = '';
liveTranscription.classList.remove('hidden');
}
// Stop talking to the assistant
@@ -641,15 +535,12 @@
if (!isStreaming) return;
isStreaming = false;
talkButton.classList.remove('recording');
talkButton.textContent = 'Press to Talk';
micButton.classList.remove('listening');
micButton.textContent = 'Press to Talk';
status.textContent = 'Processing...';
// Tell server we're done speaking
socket.emit('stop_speaking');
// Hide live transcription temporarily
liveTranscription.classList.add('hidden');
}
// Send audio chunk to server
@@ -702,8 +593,7 @@
// Handle real-time transcription
socket.on('live_transcription', (data) => {
liveTranscription.textContent = data.text || '...';
liveTranscription.classList.remove('hidden');
visualizerLabel.textContent = data.text || '...';
});
// Handle final transcription
@@ -749,8 +639,8 @@
speakingWave.remove();
}
// Re-enable talk button if it was disabled
talkButton.disabled = false;
// Re-enable mic button if it was disabled
micButton.disabled = false;
});
// Legacy handler for text-only responses