class VoiceTrack extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.recording = false;
this.mediaRecorder = null;
this.audioChunks = [];
this.analysisResult = null;
}
connectedCallback() {
this.shadowRoot.innerHTML = `
Record your voice to analyze pronunciation and confidence levels.
00:00
Analysis Results
Pronunciation Accuracy
0%
Confidence Level
0%
Fluency
0%
Clarity
0%
`;
this.recordBtn = this.shadowRoot.getElementById('recordBtn');
this.analyzeBtn = this.shadowRoot.getElementById('analyzeBtn');
this.timer = this.shadowRoot.getElementById('timer');
this.results = this.shadowRoot.getElementById('results');
this.setupEventListeners();
}
setupEventListeners() {
this.recordBtn.addEventListener('click', () => {
if (this.recording) {
this.stopRecording();
} else {
this.startRecording();
}
});
this.analyzeBtn.addEventListener('click', () => {
this.analyzeRecording();
});
}
async startRecording() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.mediaRecorder = new MediaRecorder(stream);
this.audioChunks = [];
this.mediaRecorder.ondataavailable = event => {
this.audioChunks.push(event.data);
};
this.mediaRecorder.onstop = () => {
this.recording = false;
this.recordBtn.classList.remove('recording');
this.analyzeBtn.disabled = false;
clearInterval(this.timerInterval);
};
this.mediaRecorder.start();
this.recording = true;
this.recordBtn.classList.add('recording');
this.analyzeBtn.disabled = true;
this.results.style.display = 'none';
// Start timer
let seconds = 0;
this.timerInterval = setInterval(() => {
seconds++;
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
this.timer.textContent = `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
}, 1000);
} catch (error) {
console.error('Error accessing microphone:', error);
alert('Could not access microphone. Please check permissions.');
}
}
stopRecording() {
if (this.mediaRecorder && this.recording) {
this.mediaRecorder.stop();
this.recording = false;
this.mediaRecorder.stream.getTracks().forEach(track => track.stop());
}
}
async analyzeRecording() {
if (this.audioChunks.length === 0) return;
this.analyzeBtn.disabled = true;
this.analyzeBtn.innerHTML = ' Analyzing...';
const audioBlob = new Blob(this.audioChunks, { type: 'audio/wav' });
try {
// Simulate API response for demo
await new Promise(resolve => setTimeout(resolve, 1500));
// Check if we're in a game context
const isGamePage = window.location.pathname.includes('gamequest');
if (isGamePage) {
// Game-specific analysis with higher scores
this.analysisResult = {
pronunciation: Math.floor(Math.random() * 20) + 80,
confidence: Math.floor(Math.random() * 20) + 80,
fluency: Math.floor(Math.random() * 20) + 80,
clarity: Math.floor(Math.random() * 20) + 80
};
// Update game score if on game page
if (typeof updateScore === 'function') {
const points = Math.floor(this.analysisResult.confidence / 10);
updateScore(points);
}
} else {
// Regular analysis
this.analysisResult = {
pronunciation: Math.floor(Math.random() * 30) + 70,
confidence: Math.floor(Math.random() * 30) + 70,
fluency: Math.floor(Math.random() * 30) + 70,
clarity: Math.floor(Math.random() * 30) + 70
};
}
this.displayResults();
} catch (error) {
console.error('Error analyzing recording:', error);
alert('Error analyzing recording. Please try again.');
} finally {
this.analyzeBtn.disabled = false;
this.analyzeBtn.innerHTML = ' Analyze';
}
}
displayResults() {
this.results.style.display = 'block';
this.shadowRoot.getElementById('pronunciationScore').textContent = `${this.analysisResult.pronunciation}%`;
this.shadowRoot.getElementById('pronunciationBar').style.width = `${this.analysisResult.pronunciation}%`;
this.shadowRoot.getElementById('confidenceScore').textContent = `${this.analysisResult.confidence}%`;
this.shadowRoot.getElementById('confidenceBar').style.width = `${this.analysisResult.confidence}%`;
this.shadowRoot.getElementById('fluencyScore').textContent = `${this.analysisResult.fluency}%`;
this.shadowRoot.getElementById('fluencyBar').style.width = `${this.analysisResult.fluency}%`;
this.shadowRoot.getElementById('clarityScore').textContent = `${this.analysisResult.clarity}%`;
this.shadowRoot.getElementById('clarityBar').style.width = `${this.analysisResult.clarity}%`;
}
}
customElements.define('voice-track', VoiceTrack);