Uses 911K of libraries. Click here for Pure Java version (5888L/34K).
1 | !7 |
2 | |
3 | cmodule CruddieSpeechDemo > DynPrintLogAndEnabled { |
4 | set flag NoNanoHTTPD. |
5 | !include #1029545 // API for Eleu |
6 | |
7 | switchable int vadUpdateInterval = 100; |
8 | switchable double listenTime = 3.0; // listen for 3 seconds after voice activity |
9 | switchable double listenTimeAfterActualSpeech = 10.0; // listen for 10 seconds after actual speech recognized |
10 | switchable double transcriptTitleShowTime = 5.0; // how long to show recognized text in window title |
11 | switchable bool showVadStatus; |
12 | switchable int initialHumVolume = 0; |
13 | |
14 | S myLink() { ret "https://cruddie.site/"; } |
15 | |
16 | S controls() { |
17 | ret " " + tag("button", "...", onclick := lineBreaksToSpaces([[ |
18 | startOrStopSpeechRecog(); |
19 | if (bigOn) { lastHadVoice = Date.now(); startVAD(); startUpdater(); humOn(); } |
20 | else stopVAD(); |
21 | ]]), type := 'button, class := 'speechOnBtn, disabled := 'disabled, display := 'inline) |
22 | + hdiv(hsnippetimg(#1102938, width := 24, height := 24, title := "Streaming audio to cloud"), style := "display: inline; visibility: hidden; margin-left: 10px", class := "listenStatus") |
23 | + (!showVadStatus ? "" : hdiv(hsnippetimg(#1102908, width := 24, height := 24, title := "Someone is speaking (either me or you)"), style := "display: inline; visibility: hidden; margin-left: 10px", class := "vadStatus")); |
24 | } |
25 | |
26 | O html(virtual Request request) { try { |
27 | S uri = cast get(request, 'uri); |
28 | SS params = cast get(request, 'params); |
29 | print(+params); |
30 | |
31 | S jsOnSpeech = [[ |
32 | if (transcript == 'stop listening') |
33 | stopVAD(); |
34 | else |
35 | window.submitAMsg(transcript); |
36 | lastHeard = transcript; |
37 | lastHeardWhen = Date.now(); |
38 | ]]; |
39 | |
40 | int humVolume = initialHumVolume; |
41 | |
42 | ret hhtml(hmobilefix() + hhead( |
43 | htitle("CRUDDIE Speech Recog Demo") |
44 | + hLoadJQuery2() |
45 | + hjs_humWithFade(humVolume/100.0)) |
46 | + hbody( |
47 | hdiv(controls()) |
48 | + hjs_focusEnd() |
49 | + hjs([[ |
50 | window.submitAMsg = function(msg) { |
51 | var ta = $("textarea[name=speechText]"); |
52 | var text = ta.val(); |
53 | ta.val((text && !text.endsWith("\n") ? text + "\n" : text) + msg); |
54 | ta.focusEnd(true); // true = noFocus |
55 | if (ta[0]) |
56 | ta[0].scrollTop = ta[0].scrollHeight; |
57 | }; |
58 | ]]) |
59 | + hdiv(htextarea("", name := "speechText", cols := 60, rows := 4)) |
60 | + p("Hum volume (sound when listening): " + htextinput("", humVolume, style := "width: 5ch", id := "humVolumeInput", onInput := "updateHumVolume(parseInt(this.value)/100.0)")) |
61 | + hSpeechRecognition(jsOnSpeech, true, "en-US", false, |
62 | noWebKit := p("Use Chrome if you want speech recognition")) |
63 | + hjavascript([[ |
64 | function say(text) { |
65 | console.log("Saying: " + text); |
66 | var u = new SpeechSynthesisUtterance(text); |
67 | u.lang = 'en-US'; |
68 | u.onstart = function() { console.log("speech start"); meSpeaking = true; }; |
69 | u.onend = function() { meSpeaking = false; }; |
70 | window.speechSynthesis.speak(u); |
71 | } |
72 | ]]) |
73 | + hVAD( |
74 | [[console.log("voice start"); $(".vadStatus").css("visibility", "visible");]], |
75 | [[console.log("voice stop"); $(".vadStatus").css("visibility", "hidden");]], |
76 | false) |
77 | + hjs_setTitleStatus() |
78 | + hjs(replaceDollarVars([[ |
79 | var updater; |
80 | var lastHadVoice = 0; |
81 | var lastHeard, lastHeardWhen = 0; |
82 | var meSpeaking = false; |
83 | |
84 | //audioMeterDebug = true; |
85 | |
86 | function startUpdater() { |
87 | if (updater) return; |
88 | console.log("Starting updater"); |
89 | updater = setInterval(vadMagicUpdate, $interval); |
90 | srPause = true; |
91 | } |
92 | |
93 | function stopUpdater() { |
94 | if (!updater) return; |
95 | console.log("Stopping updater"); |
96 | clearInterval(updater); |
97 | updater = null; |
98 | window.resetTitle(); |
99 | } |
100 | |
101 | function vadMagicUpdate() { |
102 | var now = Date.now(); |
103 | var hasVoice = vadHasVoice(); |
104 | var clipping = vadHasClipping(); |
105 | if (hasVoice) lastHadVoice = now; |
106 | var shouldListen1 = bigOn && (lastHadVoice >= now-$listenTime || lastHeardWhen >= now-$listenTimeAfterActualSpeech); |
107 | var shouldListen = !meSpeaking && shouldListen1; |
108 | var titleStatus = ""; |
109 | if (lastHeardWhen >= now-$transcriptTitleShowTime) |
110 | titleStatus = lastHeard + " |"; |
111 | else if (shouldListen) |
112 | titleStatus = $listeningSymbol; |
113 | else if (bigOn) |
114 | titleStatus = $ear; |
115 | if (clipping) |
116 | titleStatus = "! " + titleStatus; |
117 | window.setTitleStatus(titleStatus); |
118 | if (srPause != !shouldListen) { |
119 | console.log(shouldListen ? "Listening" : "Not listening"); |
120 | srPause = !shouldListen; |
121 | srUpdate(); |
122 | } |
123 | if (shouldListen1) humOn(); else humOff(); |
124 | if (!bigOn) { stopUpdater(); return; } |
125 | } |
126 | |
127 | // debug mic level |
128 | /*setInterval(function() { |
129 | if (audioMeter) |
130 | console.log("Mic level: " + audioMeter.absLevel); |
131 | }, 1000);*/ |
132 | ]], |
133 | interval := vadUpdateInterval, |
134 | listenTime := toMS(listenTime), |
135 | listenTimeAfterActualSpeech := toMS(listenTimeAfterActualSpeech), |
136 | transcriptTitleShowTime := toMS(transcriptTitleShowTime), |
137 | listeningSymbol := jsQuote(/*"[LISTENING]"*/unicode_cloud()), |
138 | ear := jsQuote(unicode_ear()))) |
139 | )); |
140 | } catch e { printStackTrace(e); throw rethrow(e); } |
141 | } |
142 | } |
Began life as a copy of #1028961
download show line numbers debug dex old transpilations
Travelled to 4 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt
No comments. add comment
Snippet ID: | #1030310 |
Snippet name: | Cruddie, only the speech recognition part [OK] |
Eternal ID of this version: | #1030310/19 |
Text MD5: | 2bbc8952c69d37324e03db1d1e472baa |
Transpilation MD5: | d570a1bc1a63ae45714c87ddfe0bf95d |
Author: | stefan |
Category: | javax |
Type: | JavaX source code (Dynamic Module) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2020-11-30 15:15:46 |
Source code size: | 5725 bytes / 142 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 209 / 22555 |
Version history: | 18 change(s) |
Referenced in: | [show references] |