You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ws-client.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. // #region web speech recognition api
  2. var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition;
  3. var SpeechGrammarList = SpeechGrammarList || webkitSpeechGrammarList;
  4. var SpeechRecognitionEvent = SpeechRecognitionEvent || webkitSpeechRecognitionEvent;
  5. // #endregion
  6. // #region state management
  7. var state = '';
  8. var question = 0;
  9. var rePrompt = false;
  10. var partTwo = false;
  11. var questionThreeCount = 0;
  12. var strike = 0;
  13. // #endregion
  14. // #region questions
  15. const QUESTION_ONE = 'Ich werde Ihnen jetzt langsam eine Liste mit Worten vorlesen. Danach wiederholen Sie bitte möglichst viele dieser Worte. Auf die Reihenfolge kommt es nicht an.';
  16. const QUESTION_ONE_PT2 = 'Vielen Dank. Nun nenne ich Ihnen die gleichen 10 Worte ein zweites mal. Auch danach sollen Sie wieder möglichst viele Worte wiederholen';
  17. const QUESTION_TWO = 'Nennen Sie mir bitte so viel Dinge wie möglich, die man im Supermarkt kaufen kann. Sie haben dafür eine Minute Zeit. Und Los';
  18. const QUESTION_THREE = 'Ich werde Ihnen jetzt eine Zahlenreihe nennen, die Sie mir dann bitte in umgekehrter Reihenfolge wiederholen sollen. Wenn ich beispielsweise, vier - fünf sage, dann sagen Sie bitte, fünf - vier.';
  19. const QUESTION_FOUR = 'Wir kommen nun zur vierten Frage. Bitte zählen Sie von 76 an rückwärts. Nennen Sie die Zahlen abwechselnd als ganze Zahlen und als einzelne Ziffern. Zum Beispiel:';
  20. const QUESTION_FOUR_PT2 = ['34', '3, 3', '32', '3, 1'];
  21. // #endregion
  22. // #region intents
  23. const WELCOME_INTENT = 'Default Welcome Intent';
  24. const WELCOME_FOLLOWUP_YES = 'Default Welcome Intent - yes';
  25. const WELCOME_FOLLOWUP_NO = 'Default Welcome Intent - no';
  26. const MORE_TIME = 'Add Time Intent';
  27. const MORE_TIME_YES = 'Add Time Intent - yes';
  28. const MORE_TIME_NO = 'Add Time Intent - no';
  29. const QUIT_INTENT = 'Quit Intent';
  30. const FALLBACK_INTENT = 'Default Fallback Intent';
  31. const HELP_INTENT = 'Help Intent';
  32. const CHOOSE_QUESTION = 'Frage_Starten';
  33. const NEXT_QUESTION = 'Nächste Frage';
  34. // #endregion
  35. // #region questions and expected results
  36. const QUESTION_ONE_ANSWERS = { 'teller': 1, 'hund': 1, 'lampe': 1, 'brief': 1, 'apfel': 1, 'apfelwiese': 2, 'apfelbaum': 2, 'und': 1, 'hose': 1, 'tisch': 1, 'wiese': 1, 'glas': 1, 'baum': 1 };
  37. const QUESTION_ONE_QUESTIONS = ['teller', 'hund', 'lampe', 'brief', 'apfel', 'hose', 'tisch', 'wiese', 'glas', 'baum'];
  38. const QUESTION_TWO_ANSWERS = {};
  39. var QUESTION_TWO_QUESTIONS;
  40. const QUESTION_THREE_QUESTIONS_PT1 = ['7, 2', '4, 7, 9', '5, 4, 9, 6', '2, 7, 5, 3, 6', '8, 1, 3, 5, 4, 2'];
  41. const QUESTION_THREE_QUESTIONS_PT2 = ['8, 6', '3, 1, 5', '1, 9, 7, 4', '1, 3, 5, 4, 8', '4, 1, 2, 7, 9, 5'];
  42. const QUESTION_THREE_ANSWERS_PT1 = ['27', '974', '6945', '63572', '245318'];
  43. const QUESTION_THREE_ANSWERS_PT2 = ['68', '513', '4791', '84531', '597214'];
  44. LoadQuestionTwo();
  45. function LoadQuestionTwo () {
  46. var xmlhttp;
  47. if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
  48. xmlhttp = new XMLHttpRequest();
  49. } else { // code for IE6, IE5
  50. xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
  51. }
  52. xmlhttp.onreadystatechange = function () {
  53. if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
  54. var text = xmlhttp.responseText.toLowerCase();
  55. // Now convert it into array using regex
  56. QUESTION_TWO_QUESTIONS = text.split('\r\n');
  57. for (let word of QUESTION_TWO_QUESTIONS) {
  58. QUESTION_TWO_ANSWERS[word] = 1;
  59. }
  60. }
  61. };
  62. xmlhttp.open('GET', 'lebensmittel.txt', true);
  63. xmlhttp.send();
  64. }
  65. // #endregion
  66. // #region points
  67. const questionPoints = {
  68. 1: 0,
  69. 2: 0,
  70. 3: 0,
  71. 4: 0,
  72. 5: 0 };
  73. // #endregion
  74. // tokenization
  75. const separators = [' ', '\\\+', '-', '\\\(', '\\\)', '\\*', '/', ':', '\\\?'];
  76. // Timers
  77. var timerId;
  78. // #region html elements
  79. var serverPara = document.querySelector('.server');
  80. var diagnosticPara = document.querySelector('.output');
  81. var testBtn = document.querySelector('button');
  82. var testBtn2 = document.getElementById('speechBtn');
  83. var infoPara = document.getElementById('info');
  84. var questionNumDisplay = document.querySelector('.quest');
  85. // #endregion
  86. // websocket to communicate with the server
  87. var ws = new WebSocket('ws://' + window.location.host + window.location.pathname + 'ws');
  88. // #region speech recognition initialization
  89. var recognition = new SpeechRecognition();
  90. recognition.lang = 'de-DE';
  91. // recognition.interimResults = false;
  92. recognition.maxAlternatives = 1;
  93. recognition.continuous = true;
  94. var answerQuery = '';
  95. var skipRecording = false;
  96. // #endregion
  97. // #region speech synthesis initialization
  98. var speechsynth = new SpeechSynthesisUtterance();
  99. var listSpeechsynth = new SpeechSynthesisUtterance();
  100. var voices;
  101. // #endregion
  102. // #region speech events
  103. window.speechSynthesis.onvoiceschanged = function () {
  104. voices = window.speechSynthesis.getVoices();
  105. voices.forEach(element => {
  106. if (element.name === 'Google Deutsch') {
  107. speechsynth.voice = element;
  108. listSpeechsynth.voice = element;
  109. }
  110. });
  111. speechsynth.rate = 0.88;
  112. listSpeechsynth.rate = 0.7;
  113. };
  114. speechsynth.onend = function (event) {
  115. switch (question) {
  116. case 1:
  117. break;
  118. case 2:
  119. break;
  120. case 3:
  121. break;
  122. case 4:
  123. break;
  124. case 5:
  125. break;
  126. }
  127. if (!skipRecording) {
  128. recognition.start();
  129. console.log('reocgnition started. Question: ' + question);
  130. }
  131. skipRecording = false;
  132. diagnosticPara.textContent = '';
  133. console.log('global speech end');
  134. };
  135. // #endregion
  136. // #region websocket events
  137. ws.onopen = function () {
  138. serverPara.style.background = 'green';
  139. serverPara.innerHTML = 'Server online';
  140. };
  141. ws.onmessage = function (payload) {
  142. var dialogflowResult = JSON.parse(payload.data);
  143. checkIntent(dialogflowResult);
  144. // document.querySelector('h1').innerHTML = dialogflowResult.intent.displayName;
  145. };
  146. // #endregion
  147. // INTENT HANDLING
  148. function checkIntent (result) {
  149. switch (result.intent.displayName) {
  150. case QUIT_INTENT:
  151. state = 'quit';
  152. if (timerId !== undefined) {
  153. clearTimeout(timerId);
  154. }
  155. skipRecording = true;
  156. speak('Beende die Durchführung.');
  157. break;
  158. case WELCOME_INTENT:
  159. state = 'detect';
  160. speak(result.fulfillmentText);
  161. break;
  162. case WELCOME_FOLLOWUP_YES:
  163. startQuestion(1);
  164. break;
  165. case WELCOME_FOLLOWUP_NO:
  166. skipRecording = true;
  167. speak('Okay, Danke fürs Benutzen.');
  168. break;
  169. case MORE_TIME:
  170. state = 'detect';
  171. speak('Brauchen Sie noch etwas Zeit?');
  172. break;
  173. case MORE_TIME_YES:
  174. rePrompt = true;
  175. state = 'answer';
  176. speak('Alles klar');
  177. break;
  178. case MORE_TIME_NO:
  179. skipRecording = true;
  180. state = 'answer';
  181. speak('Verstanden');
  182. recognition.stop();
  183. ws.send(answerQuery);
  184. break;
  185. case CHOOSE_QUESTION:
  186. question = result.parameters.fields.num.numberValue;
  187. state = 'answer';
  188. handleQuestion();
  189. break;
  190. case FALLBACK_INTENT:
  191. // if (state === 'answer') {
  192. // handleAnswer(result.queryText)
  193. // }
  194. break;
  195. default:
  196. break;
  197. }
  198. }
  199. // #region question handling
  200. function startQuestion (number) {
  201. question = number;
  202. state = 'answer';
  203. questionNumDisplay.textContent = 'Question: ' + question;
  204. handleQuestion();
  205. }
  206. function handleQuestion () {
  207. switch (question) {
  208. case 1:
  209. skipRecording = true;
  210. speak(QUESTION_ONE);
  211. readQuestionOne();
  212. break;
  213. case 2:
  214. readQuestionTwo();
  215. break;
  216. case 3:
  217. readQuestionThree();
  218. break;
  219. case 4:
  220. readQuestionFour();
  221. break;
  222. case 5:
  223. break;
  224. }
  225. }
  226. function readQuestionOne () {
  227. for (let i = 0; i < QUESTION_ONE_QUESTIONS.length; i++) {
  228. let utterance = new SpeechSynthesisUtterance();
  229. utterance.voice = voices[2];
  230. utterance.rate = 0.75;
  231. utterance.text = QUESTION_ONE_QUESTIONS[i];
  232. window.speechSynthesis.speak(utterance);
  233. if (i === 9) {
  234. utterance.onend = function (event) {
  235. recognition.start();
  236. console.log('reocgnition started. Question: ' + question);
  237. };
  238. }
  239. }
  240. }
  241. function readQuestionTwo () {
  242. let utterance = new SpeechSynthesisUtterance();
  243. utterance.voice = voices[2];
  244. utterance.text = QUESTION_TWO;
  245. utterance.rate = 0.8;
  246. window.speechSynthesis.speak(utterance);
  247. utterance.onend = function (event) {
  248. window.setTimeout(
  249. function () {
  250. recognition.stop();
  251. window.setTimeout(
  252. function () {
  253. handleAnswer(answerQuery);
  254. answerQuery = '';
  255. }, 3000);
  256. }, 6000);
  257. recognition.start();
  258. console.log('reocgnition started. Question: ' + question);
  259. };
  260. }
  261. function readQuestionThree () {
  262. skipRecording = true;
  263. speak('Dankeschön, weiter geht es mit der nächsten Frage.');
  264. let utterance = new SpeechSynthesisUtterance();
  265. utterance.voice = voices[2];
  266. utterance.text = QUESTION_THREE;
  267. utterance.rate = 0.88;
  268. window.speechSynthesis.speak(utterance);
  269. utterance.onend = function (event) {
  270. speak(QUESTION_THREE_QUESTIONS_PT1[questionThreeCount]);
  271. };
  272. }
  273. function readQuestionFour () {
  274. // skipRecording = true;
  275. // speak(QUESTION_FOUR);
  276. for (let i = 0; i < QUESTION_FOUR_PT2.length; i++) {
  277. let utterance = new SpeechSynthesisUtterance();
  278. utterance.voice = voices[2];
  279. utterance.rate = 0.75;
  280. utterance.text = QUESTION_FOUR_PT2[i];
  281. window.speechSynthesis.speak(utterance);
  282. }
  283. // speak('Sie haben hierfür 1 Minute Zeit.');
  284. }
  285. function handleAnswer (query) {
  286. switch (question) {
  287. case 1:
  288. handleAnswerToFirstQuestion(query);
  289. break;
  290. case 2:
  291. handleAnswerToSecondQuestion(query);
  292. break;
  293. case 3:
  294. handleAnswerToThirdQuestion(query);
  295. break;
  296. case 4:
  297. break;
  298. case 5:
  299. break;
  300. }
  301. }
  302. function handleAnswerToFirstQuestion (answer) {
  303. var tokens = answer.split(new RegExp(separators.join('|'), 'g'));
  304. questionPoints[question] += calculatePoints(tokens, QUESTION_ONE_ANSWERS);
  305. if (partTwo) {
  306. partTwo = false;
  307. console.log('question 1 points: ' + questionPoints[question]);
  308. skipRecording = true;
  309. speak('Vielen Dank, nun geht es weiter mit der nächsten Frage');
  310. startQuestion(2);
  311. // state = 'detect'
  312. } else {
  313. rePrompt = false;
  314. skipRecording = true;
  315. speak(QUESTION_ONE_PT2);
  316. readQuestionOne(QUESTION_ONE);
  317. partTwo = true;
  318. }
  319. }
  320. function handleAnswerToSecondQuestion (answer) {
  321. var tokens = answer.split(new RegExp(separators.join('|'), 'g'));
  322. questionPoints[question] = calculatePoints(tokens, QUESTION_TWO_ANSWERS);
  323. console.log('question 2 points: ' + questionPoints[question]);
  324. startQuestion(3);
  325. // state = 'detect'
  326. }
  327. function handleAnswerToThirdQuestion (query) {
  328. speechsynth.rate = 0.88;
  329. query = query.replace(' ', '');
  330. let answerArray;
  331. let questionArray;
  332. if (!partTwo) {
  333. answerArray = QUESTION_THREE_ANSWERS_PT1;
  334. } else {
  335. answerArray = QUESTION_THREE_ANSWERS_PT2;
  336. }
  337. if (query === answerArray[questionThreeCount]) {
  338. strike = 0;
  339. partTwo = false;
  340. questionThreeCount++;
  341. questionPoints[question] = questionThreeCount + 1;
  342. questionArray = QUESTION_THREE_QUESTIONS_PT1;
  343. } else {
  344. strike++;
  345. partTwo = true;
  346. questionArray = QUESTION_THREE_QUESTIONS_PT2;
  347. }
  348. if (strike === 2 || questionThreeCount === 5) {
  349. speechsynth.rate = 0.88;
  350. console.log('question 3 points: ' + questionPoints[question]);
  351. skipRecording = true;
  352. speak('weiter geht es mit der Nächsten Frage');
  353. startQuestion(4);
  354. return;
  355. }
  356. speak(questionArray[questionThreeCount]);
  357. console.log('count: ' + questionThreeCount + ', strike: ' + strike + ', points: ' + questionPoints[question]);
  358. }
  359. // #endregion
  360. // function recognizeSpeech () {
  361. // if (state === 'answer') {
  362. // var arr;
  363. // switch (question) {
  364. // case 1:
  365. // arr = QUESTION_ONE_QUESTIONS;
  366. // break;
  367. // case 2:
  368. // // arr = QUESTION_TWO_QUESTIONS;
  369. // break;
  370. // case 3:
  371. // arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  372. // break;
  373. // case 4:
  374. // break;
  375. // case 5:
  376. // break;
  377. // }
  378. // // var grammar = '#JSGF V1.0; grammar colors; public <color> = ' + arr.join(' | ') + ' ;';
  379. // // var speechRecognitionList = new SpeechGrammarList();
  380. // // speechRecognitionList.addFromString(grammar, 1);
  381. // // recognition.grammars = speechRecognitionList;
  382. // }
  383. // #region speech recognition event
  384. recognition.onresult = function (event) {
  385. var last = event.results.length - 1;
  386. var speechResult = event.results[last][0].transcript.toLowerCase();
  387. diagnosticPara.textContent += speechResult + ' ';
  388. // console.log('Confidence: ' + event.results[0][0].confidence)
  389. console.log('process: ' + speechResult);
  390. processSpeech(speechResult);
  391. // testBtn.disabled = false
  392. // testBtn.textContent = 'record...'
  393. };
  394. recognition.onspeechend = function () {
  395. // recognition.stop();
  396. // testBtn.disabled = false;
  397. // testBtn.textContent = 'Start new test';
  398. };
  399. recognition.onerror = function (event) {
  400. testBtn.disabled = false;
  401. testBtn.textContent = 'Start new test';
  402. diagnosticPara.textContent = 'Error occurred in recognition: ' + event.error;
  403. };
  404. recognition.onaudiostart = function (event) {
  405. // Fired when the user agent has started to capture audio.
  406. };
  407. recognition.onaudioend = function (event) {
  408. };
  409. recognition.onend = function (event) {
  410. // Fired when the speech recognition service has disconnected.
  411. };
  412. recognition.onnomatch = function (event) {
  413. // Fired when the speech recognition service returns a final result with no significant recognition. This may involve some degree of recognition, which doesn't meet or exceed the confidence threshold.
  414. // console.log('SpeechRecognition.onnomatch')
  415. };
  416. recognition.onsoundstart = function (event) {
  417. // Fired when any sound — recognisable speech or not — has been detected.
  418. };
  419. recognition.onsoundend = function (event) {
  420. // Fired when any sound — recognisable speech or not — has stopped being detected.
  421. };
  422. recognition.onspeechstart = function (event) {
  423. // Fired when sound that is recognised by the speech recognition service as speech has been detected.
  424. };
  425. recognition.onstart = function (event) {
  426. // Fired when the speech recognition service has begun listening to incoming audio with intent to recognize grammars associated with the current SpeechRecognition.
  427. };
  428. // }
  429. // #endregion
  430. // #region global functions
  431. function processSpeech (speechResult) {
  432. console.log('To dialogflow: ' + speechResult);
  433. ws.send(speechResult);
  434. let timeOut;
  435. switch (question) {
  436. case 1:
  437. timeOut = 6500;
  438. break;
  439. case 2:
  440. answerQuery += speechResult;
  441. return;
  442. case 3:
  443. if (speechResult.includes('uhr')) {
  444. speechResult = speechResult.replace('uhr', '');
  445. }
  446. timeOut = 6500;
  447. break;
  448. case 4:
  449. break;
  450. case 5:
  451. timeOut = 6500;
  452. break;
  453. }
  454. if (state === 'answer') {
  455. if (timerId != undefined) {
  456. clearTimeout(timerId);
  457. }
  458. answerQuery += speechResult;
  459. timerId = window.setTimeout(
  460. function () {
  461. // if (!rePrompt) {
  462. // ws.send('ich brauche noch etwas Zeit')
  463. // } else {
  464. console.log('recording end. Evaluate: ' + answerQuery);
  465. handleAnswer(answerQuery);
  466. answerQuery = '';
  467. diagnosticPara.textContent = '';
  468. // }
  469. recognition.stop();
  470. console.log('timer fallback');
  471. }, timeOut);
  472. } else {
  473. console.log('recording end.');
  474. recognition.stop();
  475. }
  476. }
  477. function startDemenzScreening () {
  478. // ws.send('starte demenz test');
  479. startQuestion(4);
  480. testBtn.disabled = true;
  481. testBtn.textContent = 'Test in progress';
  482. infoPara.textContent = 'wait...';
  483. diagnosticPara.textContent = 'detecting...';
  484. }
  485. function testSpeechOut () {
  486. readQuestionFour();
  487. // speechsynth.text = 'test 123';
  488. // speechsynth.volume = 1;
  489. // speechsynth.rate = 1;
  490. // console.log(speechsynth);
  491. // window.speechSynthesis.speak(speechsynth);
  492. // console.log(window.speechSynthesis);
  493. }
  494. function speak (sentence) {
  495. speechsynth.text = sentence;
  496. window.speechSynthesis.speak(speechsynth);
  497. }
  498. function calculatePoints (tokens, d) {
  499. let points = 0;
  500. let dict = {};
  501. Object.assign(dict, d);
  502. for (let word of tokens) {
  503. if (dict[word] !== undefined) {
  504. points += dict[word];
  505. delete dict[word];
  506. }
  507. }
  508. return points;
  509. }
  510. // #endregion
  511. testBtn.addEventListener('click', startDemenzScreening);
  512. testBtn2.addEventListener('click', testSpeechOut);