vikramronavrsc's picture
Rename voice-component.py to voice_component.py
cc3dcb6 verified
raw
history blame
7.95 kB
# voice_component.py
import streamlit as st
import streamlit.components.v1 as components
def voice_input_component():
"""
Creates a voice input component that uses the browser's Web Speech API.
Returns the transcribed text from voice input.
"""
# HTML component for voice input
voice_html = """
<div class="voice-input-container">
<button id="start-voice" class="voice-button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mic-icon"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="22"></line></svg>
Start Voice Input
</button>
<div id="voice-status" class="voice-status">Click to start speaking</div>
<div id="transcript" class="transcript"></div>
<button id="send-transcript" class="send-button" style="display:none;">
Send
</button>
</div>
<style>
.voice-input-container {
padding: 15px;
border-radius: 8px;
background-color: #f8f9fa;
margin-bottom: 15px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.voice-button {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
background-color: #ff4b4b;
color: white;
border: none;
padding: 10px 15px;
border-radius: 20px;
cursor: pointer;
font-weight: 500;
transition: background-color 0.3s;
}
.voice-button:hover {
background-color: #e63e3e;
}
.voice-button.listening {
background-color: #ff7a7a;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 75, 75, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(255, 75, 75, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 75, 75, 0); }
}
.voice-status {
margin-top: 10px;
font-size: 14px;
color: #6c757d;
}
.transcript {
margin-top: 15px;
min-height: 60px;
padding: 10px;
border-radius: 4px;
border: 1px solid #dee2e6;
display: none;
}
.send-button {
margin-top: 10px;
background-color: #4CAF50;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
.send-button:hover {
background-color: #45a049;
}
</style>
<script>
// Variable to store transcript
let finalTranscript = '';
let recognizing = false;
let recognition;
// Check if browser supports speech recognition
function checkSpeechRecognitionSupport() {
return 'webkitSpeechRecognition' in window || 'SpeechRecognition' in window;
}
// Initialize speech recognition
function initSpeechRecognition() {
if (!checkSpeechRecognitionSupport()) {
document.getElementById('voice-status').textContent = 'Speech recognition not supported in this browser';
document.getElementById('start-voice').disabled = true;
return;
}
// Create speech recognition object
recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
// Configure settings
recognition.continuous = true;
recognition.interimResults = true;
recognition.lang = 'en-US'; // Can be made configurable
// Handle results
recognition.onresult = function(event) {
let interimTranscript = '';
for (let i = event.resultIndex; i < event.results.length; ++i) {
if (event.results[i].isFinal) {
finalTranscript += event.results[i][0].transcript;
} else {
interimTranscript += event.results[i][0].transcript;
}
}
const transcriptElement = document.getElementById('transcript');
transcriptElement.textContent = finalTranscript + interimTranscript;
transcriptElement.style.display = 'block';
// Show send button if we have text
if (finalTranscript.trim() !== '') {
document.getElementById('send-transcript').style.display = 'block';
}
// Send transcript to Streamlit component
window.parent.postMessage({
type: 'voice-transcript',
transcript: finalTranscript
}, '*');
};
// Handle errors
recognition.onerror = function(event) {
if (event.error === 'no-speech') {
document.getElementById('voice-status').textContent = 'No speech detected. Try again.';
} else {
document.getElementById('voice-status').textContent = 'Error: ' + event.error;
console.error('Speech recognition error:', event.error);
}
stopRecognition();
};
// Handle end of speech recognition
recognition.onend = function() {
stopRecognition();
};
}
// Start recognition
function startRecognition() {
if (recognizing) return;
try {
recognition.start();
recognizing = true;
finalTranscript = '';
document.getElementById('voice-status').textContent = 'Listening...';
document.getElementById('start-voice').textContent = 'Stop Listening';
document.getElementById('start-voice').classList.add('listening');
} catch (e) {
console.error('Error starting speech recognition:', e);
document.getElementById('voice-status').textContent = 'Error starting speech recognition';
}
}
// Stop recognition
function stopRecognition() {
if (!recognizing) return;
recognition.stop();
recognizing = false;
document.getElementById('voice-status').textContent = 'Click to start speaking';
document.getElementById('start-voice').textContent = 'Start Voice Input';
document.getElementById('start-voice').classList.remove('listening');
}
// Toggle recognition
function toggleRecognition() {
if (recognizing) {
stopRecognition();
} else {
startRecognition();
}
}
// Send transcript to Streamlit
function sendTranscript() {
if (finalTranscript.trim() === '') return;
window.parent.postMessage({
type: 'streamlit:setComponentValue',
value: finalTranscript,
dataType: 'str'
}, '*');
// Reset UI
finalTranscript = '';
document.getElementById('transcript').textContent = '';
document.getElementById('transcript').style.display = 'none';
document.getElementById('send-transcript').style.display = 'none';
}
// Initialize after DOM loaded
document.addEventListener('DOMContentLoaded', function() {
// Initialize speech recognition
initSpeechRecognition();
// Add event listeners
document.getElementById('start-voice').addEventListener('click', toggleRecognition);
document.getElementById('send-transcript').addEventListener('click', sendTranscript);
});
</script>
"""
# Generate a unique key for the component
key = "voice_input"
# Initialize session state for voice input if not exists
if key not in st.session_state:
st.session_state[key] = ""
# Render the component
components.html(voice_html, height=200)
# Return the last transcript
return st.session_state.get(key, "")