XiaoBai1221's picture
Done
6c78660
class SpeechRecognition {
constructor() {
this.recognition = null;
this.isListening = false;
this.isSupported = false;
this.onResult = null;
this.onError = null;
this.onStart = null;
this.onEnd = null;
this.context = {
userLocation: null,
recentQueries: [],
currentSession: null
};
this.initRecognition();
}
initRecognition() {
if ('webkitSpeechRecognition' in window) {
this.recognition = new webkitSpeechRecognition();
this.isSupported = true;
} else if ('SpeechRecognition' in window) {
this.recognition = new SpeechRecognition();
this.isSupported = true;
} else {
console.warn('瀏覽器不支援語音識別');
return;
}
this.recognition.continuous = false;
this.recognition.interimResults = true;
this.recognition.lang = 'zh-TW';
this.recognition.maxAlternatives = 3; // 增加候選結果數量
this.recognition.onstart = () => {
this.isListening = true;
if (this.onStart) this.onStart();
};
this.recognition.onresult = (event) => {
let finalTranscript = '';
let interimTranscript = '';
for (let i = event.resultIndex; i < event.results.length; i++) {
const transcript = event.results[i][0].transcript;
if (event.results[i].isFinal) {
finalTranscript += transcript;
} else {
interimTranscript += transcript;
}
}
let enhancedFinal = finalTranscript;
let enhancedInterim = interimTranscript;
if (window.speechEnhancer) {
const context = this.getContext();
if (finalTranscript) {
enhancedFinal = window.speechEnhancer.enhance(finalTranscript, context);
}
if (interimTranscript) {
enhancedInterim = window.speechEnhancer.enhance(interimTranscript, context);
}
}
if (this.onResult) {
this.onResult(enhancedFinal, enhancedInterim);
}
};
this.recognition.onerror = (event) => {
console.error('🎤 語音識別錯誤:', event.error);
this.isListening = false;
if (this.onError) this.onError(event.error);
};
this.recognition.onend = () => {
this.isListening = false;
if (this.onEnd) this.onEnd();
};
}
start() {
if (!this.isSupported) {
console.error('瀏覽器不支援語音識別');
return false;
}
if (this.isListening) {
console.warn('語音識別已在進行中');
return false;
}
try {
this.recognition.start();
return true;
} catch (error) {
console.error('啟動語音識別失敗:', error);
return false;
}
}
stop() {
if (this.recognition && this.isListening) {
this.recognition.stop();
}
}
abort() {
if (this.recognition && this.isListening) {
this.recognition.abort();
}
}
setUserLocation(location) {
this.context.userLocation = location;
}
addRecentQuery(query) {
if (query && typeof query === 'string') {
this.context.recentQueries.unshift(query);
if (this.context.recentQueries.length > 10) {
this.context.recentQueries = this.context.recentQueries.slice(0, 10);
}
}
}
getContext() {
return {
...this.context,
timestamp: Date.now()
};
}
clearContext() {
this.context = {
userLocation: null,
recentQueries: [],
currentSession: null
};
}
}
class TextToSpeech {
constructor() {
this.synth = window.speechSynthesis;
this.isSupported = 'speechSynthesis' in window;
this.isSpeaking = false;
this.currentUtterance = null;
}
speak(text, options = {}) {
if (!this.isSupported) {
console.error('瀏覽器不支援語音合成');
return false;
}
this.stop();
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = options.lang || 'zh-TW';
utterance.rate = options.rate || 1.0;
utterance.pitch = options.pitch || 1.0;
utterance.volume = options.volume || 1.0;
utterance.onstart = () => {
this.isSpeaking = true;
};
utterance.onend = () => {
this.isSpeaking = false;
this.currentUtterance = null;
};
utterance.onerror = (event) => {
console.error('🔊 語音合成錯誤:', event.error);
this.isSpeaking = false;
this.currentUtterance = null;
};
this.currentUtterance = utterance;
this.synth.speak(utterance);
return true;
}
stop() {
if (this.synth.speaking) {
this.synth.cancel();
}
this.isSpeaking = false;
this.currentUtterance = null;
}
pause() {
if (this.synth.speaking && !this.synth.paused) {
this.synth.pause();
}
}
resume() {
if (this.synth.paused) {
this.synth.resume();
}
}
}
window.speechRecognition = new SpeechRecognition();
window.textToSpeech = new TextToSpeech();