-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathvoice.js
More file actions
129 lines (120 loc) · 7.46 KB
/
Copy pathvoice.js
File metadata and controls
129 lines (120 loc) · 7.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/////////////////////////////////////////////////////////////////////////////////////////////////
// VOICE (SST / TTS(
/////////////////////////////////////////////////////////////////////////////////////////////////
class Voice {
constructor() // CONSTRUCTOR
{
let i,_this=this;
this.listening=false; // Recognizing flag
this.getLastClause=false; // Get last clause
this.hasRecognition=false; // Assume no STT
this.thoughtBubbles=false; // Flag to show thought bubbles instead of TTS
try { // Try
this.tts=new SpeechSynthesisUtterance(); // Init TTS
this.mac=0; // Assume non-mac
if (window.navigator.userAgentData) // Exists?
this.mac=window.navigator.userAgentData.platform != "Windows"; // A mac?
this.femaleVoice=this.mac ? 0 : 1; // Female voice
this.maleVoice=this.mac ? 1 : 0; // Male voice
this.talking=0; // Talking flag to move mouth
this.voices=[]; // New array
speechSynthesis.onvoiceschanged=()=> { // React to voice init
this.voices=[]; // Clear list
speechSynthesis.getVoices().forEach(function(voice) { // For each voice
if (voice.lang == "en-US") _this.voices.push(voice); // Just look at English
if (voice.name == "Google UK English Female") _this.voices.push(voice),_this.instructorVoice=_this.voices.length-1; // Instructor's voice for all
if (voice.name.match(/Microsoft Mark/i)) _this.voices.push(voice),_this.maleVoice=_this.voices.length-1; // Male voice for PC
if (voice.name == "Google US English") _this.voices.push(voice),_this.femaleVoice=_this.voices.length-1; // Female voice for all
if (voice.name.match(/Alex/i)) _this.voices.push(voice),_this.maleVoice=_this.voices.length-1; // Male voice ofr
if (voice.name.match(/Aaron/i)) _this.voices.push(voice),_this.maleVoice=_this.voices.length-1; // Mac male voice
});
};
this.tts.onend=()=> { // ON TALKING END
this.talking=0; // Stop talking animation
if ((i=app.studex[app.curStudent])) // Valid student
app.sc.SetBone(app.students[i-1],"mouth",0,0,0); // Neutral mouth
$("#responseTextDiv").delay(3000).fadeOut(300); // Fade out response bubble
};
} catch(e) { trace("TTS error",e) }; // On error
try { // Try
var SpeechRecognition=SpeechRecognition || webkitSpeechRecognition; // Browser compatibility
this.recognition=new SpeechRecognition(); // Init STT
this.recognition.continuous=true; // Continual recognition off
this.recognition.interimResults=true; // Return interim results?
this.recognition.lang="en-US"; // US English
this.recognition.onstart=()=>{ this.started=true; }; // ON STT START SET STATUS FLAG
this.recognition.onend=()=> { this.started=false; if (this.listening) this.Listen() }; // ON STT END RE-LISTEN IF IN SIM
this.hasRecognition=true; // Has speechRecognition capabilities
this.recognition.onresult=(e)=>{ // ON RECOGNITION
for (i=e.resultIndex; i<e.results.length;++i) // For each result
if (e.results[i].isFinal) { // If final
app.said+=e.results[i][0].transcript; // Add to cache
if ((app.role == "Teacher") && e.results[0][0].transcript && app.multi) // If teacher talking in multiplayer mode
app.SendEvent(app.sessionId+"|"+app.curTime.toFixed(2)+"|ADMIN|INTERIM|Teacher|Class|"+e.results[0][0].transcript); // Send partial
$("#promptSpan").html(app.said ? "<span style='color:#006600'>"+app.said+"</span>" : ""); // Show portion to teacher as green
if (this.getLastClause) { // Get final utterance
app.OnPhrase(app.said); // React to remark
app.said=""; // Clear cache
this.listening=false; // Not listening anymore
this.getLastClause=false; // No last phrase anymore
this.recognition.stop(); // Stop recognition
}
}
else $("#promptSpan").html(e.results[0][0].transcript ? app.said+e.results[0][0].transcript+" " : e.results[0][0].transcript+" "); // Show portion to teacher
}
} catch(e) { trace("Voice error",e) }; // On error
}
Listen() // TURN ON SPEECH RECOGNITION
{
try {
this.getLastClause=false; // Get last phrase when transcribed
if (!this.started) this.recognition.start(); // Start recognition, unless already started
this.listening=true; // We're listening
} catch(e) { trace("Voice error",e) }; // On error
}
StopListening() // TURN OFF SPEECH RECOGNITION
{
this.getLastClause=true; // Get last phrase when transcribed
}
Talk(text, who, mp3File) // SAY SOMETHING
{
if (!text) return; // Nothing to say
text=text.replace(/\{.*?\}/g,""); // Remove any braced text
if (who == "Class") { // Show thought bubbles instead of TTS
Bubble(text); // Show
return;
}
try{ // Try
if (who != "Teacher") { // Student talking
this.talking=who; // Trigger mouth animation if a student
this.ShowSpeakerText(who,text); // Show text underneath student
}
if (mp3File) { // If an MP3 file to be played
let snd=new Audio(mp3File); // Use mp3 file
snd.play(); // Play it
snd.onended=()=>{ this.talking=0; } // Stop talking animation
return;
}
speechSynthesis.cancel(); // Clear current speech queue
if (who == "Teacher") this.tts.voice=this.voices[this.instructorVoice]; // Instructor's voice
else who=app.students.find(x => x.id == who).sex; // Get sex
if (who == "male") this.tts.voice=this.voices[this.maleVoice]; // Set male voice
else if (who == "female") this.tts.voice=this.voices[this.femaleVoice]; // Set female voice
this.tts.rate=1.1; // Set voice speed rate
// if (this.mac && (who == "male")) this.tts.rate=1.05; // Slow down mac for boys
this.tts.text=text; // Set text
speechSynthesis.speak(this.tts); // Speak
}
catch(e) { trace("Speech error",e) }; // Catch
}
ShowSpeakerText(student, msg) // SHOW SPEAKER'S TEXT
{
if (student == "Teacher") return; // Only for students
$("#responseTextDiv").remove(); // Kill old one, if any
let p=app.sc.GetScreenPos(app.sc.models[student].model); // Get pos of student
var str="<div id='responseTextDiv' style='position:absolute;font-size:14px;width:150px;text-align:center;";
str+="left:"+(p.x-75)+"px;top:"+(p.y+60)+"px'><b>"+student+"</b><br>"+msg+"</div>"; // Position and set msg
$("body").append(str); // Add popup to body
$("#responseTextDiv").fadeIn(500); // Animate in
}
} // VOICE CLOSURE