那些熟悉超级英雄电影和其他科幻类型的人,会熟悉数字助手入侵安全系统以及故事主角的更多内容。但对于我的构建,我将专注于我的数字助理语音应用程序的语音转文本和文本转语音方面。我的意思是,这到底有多难?我几乎不顾自己的理智或压力水平,一头扎进了语音合成技术的深渊,以及如何在 Javascript 中实现它们, Mendix.
“据我所知,JavaScript 是唯一一种人们觉得在开始使用之前不需要学习的语言。”
— 道格拉斯·克罗克福德
想法
设计很简单——我能做的最简单的。想法是创造一个 Mendix 应用程序可以听到并理解用户的口语,然后通过自己的“声音”做出回应。听起来很简单,对吧?一旦我决定了设计,就该寻找任何现有的技术了——当已经有许多语音平台(如 IBM 的 Watson 和 Google 的 Cloud AI Platform)时,从头开始重新开发它没有多大意义。
不过我之前也做过聊天机器人,去年的 Mendix World,我与 Jan de Vries 一起主持了一场 Low Code 现场构建。在会议中,我构建了一项 Alexa 技能,允许用户与 Mendix 应用程序,通过与 Alexa 对话。这一次,我决定少关注实际的对话和对话,而多关注实际的口语方面。如果你想了解更多关于构建对话树的信息,我建议你观看来自 Mendix 世界2020.
该设计
那么我到底要做什么呢?经过一番研究,我确定了一个设计,我的应用将专注于两个核心方面:
- 语音转文本可插入小部件,能够听到并理解用户的声音。
- 文本到语音的 JavaScript 操作允许应用程序大声响应用户。
对于小部件,我将使用我在 Github,这利用 Mozilla 的语音合成库。
方便的是,我遇到了一个 JavaScript 动作 JavaScript 教程由 Mendix 它在我们自己的文档中正是这样做的。
最后,在电影中,英雄总是会给他们的机器人助手起一个很酷的名字。为了纪念这一点,我决定将我的应用程序命名为 梅维斯 代表“Mendix非常棒的非常智能的系统=
构建 MAEVIS
通常在开发应用程序时,我会首先关注最具挑战性或最复杂的流程。由于我已经对如何让应用程序说话有了大致的想法,所以我决定专注于构建小部件,这将允许 MAEVIS 听我说。正如我上面提到的,我决定使用这个 自学资料库 by 尼克·瓦尔迪兹 在Github上。
我用了 Mendix 小部件生成器用于创建我的小部件框架。我选择使用 JavaScript ES6 来构建它,它是为 Web 和混合移动应用程序构建的。

我在将此代码改编为 MAEVIS 时遇到的主要问题是,示例使用了 Functional 组件,而小部件支架将代码生成为 Class 组件。一旦我理解了这个问题,解决它就很简单了。
我最终将其作为最终的小部件代码:
import React,{ Component, createElement, useState, useEffect } from "react";
import "./ui/SpeechToText.css";
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const mic = new SpeechRecognition();
mic.continuous = true;
mic.interimResults = true;
mic.lang = 'en-US';
export default function SpeechToText(){
const [isListening, setIsListening] = useState(false);
const [note, setNote] = useState('');
const [savedNotes, setSavedNotes] = useState([]);
useEffect(() => {
handleListen()
}, [isListening]);
const handleListen = () => {
if (isListening) {
mic.start()
mic.onend = () => {
console.log('continue..')
mic.start()
}
} else {<mic.stop()
mic.onend = () => {
console.log('Stopped Mic on Click')
handleSaveNote()
}
}
mic.onstart = () => {
console.log('Mics on')
}
mic.onresult = event => {
const transcript = Array.from(event.results)
.map(result => result[0])
.map(result => result.transcript)
.join('')
console.log(transcript)
//textAttribute(transcript)
setNote(transcript)
mic.onerror = event => {
console.log(event.error)
}
}
}
const handleSaveNote = () => {
setSavedNotes([...savedNotes, note])
setNote('')
}
return <span className="flexColumn">
<>
<p>{note}</p>
<button
className={isListening ? 'pulse-button btn-danger' : 'pulse-button'}
onClick={() => setIsListening(prevState => !prevState)}>
{isListening ? <span>🎙️Stop</span> : <span>🛑Start</span>}
</button>
</>
<>
<h2>Notes</h2>
{savedNotes.map(n => (
<p key={n}>{n}</p>
))}
</>
</span>
}
我还添加了一些样式来改变小部件前端,因此它看起来比屏幕上的常规按钮更好:
.flexColumn{
display: inline-flex;
flex-direction: column;
}
.container {
width: 200px;
height: 100%;
margin: 0 auto 0;
perspective: 1000;
-webkit-perspective: 1000;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
background: #fff;
}
.pulse-button {
position: relative;
margin: auto;
display: block;
width: 10em;
height: 10em;
font-size: 1.3em;
font-weight: light;
font-family: 'Trebuchet MS', sans-serif;
text-transform: uppercase;
text-align: center;
line-height: 100px;
letter-spacing: -1px;
color: white;
border: none;
border-radius: 50%;
background: #5a99d4;
cursor: pointer;
box-shadow: 0 0 0 0 rgba(90, 153, 212, 0.5);
-webkit-animation: pulse 1.5s infinite;
animation: pulse 1.5s infinite;
}
.pulse-button:hover {
-webkit-animation: none;
animation: none;
}
@-webkit-keyframes pulse {
0% {
-moz-transform: scale(0.9);
-ms-transform: scale(0.9);
-webkit-transform: scale(0.9);
transform: scale(0.9);
}
70% {
-moz-transform: scale(1);
-ms-transform: scale(1);
-webkit-transform: scale(1);
transform: scale(1);
box-shadow: 0 0 0 50px rgba(90, 153, 212, 0);
}
100% {
-moz-transform: scale(0.9);
-ms-transform: scale(0.9);
-webkit-transform: scale(0.9);
transform: scale(0.9);
box-shadow: 0 0 0 0 rgba(90, 153, 212, 0);
}
}
@keyframes pulse {
0% {
-moz-transform: scale(0.9);
-ms-transform: scale(0.9);
-webkit-transform: scale(0.9);
transform: scale(0.9);
}
70% {
-moz-transform: scale(1);
-ms-transform: scale(1);
-webkit-transform: scale(1);
transform: scale(1);
box-shadow: 0 0 0 50px rgba(90, 153, 212, 0);
}
100% {
-moz-transform: scale(0.9);
-ms-transform: scale(0.9);
-webkit-transform: scale(0.9);
transform: scale(0.9);
box-shadow: 0 0 0 0 rgba(90, 153, 212, 0);
}
}
我必须说,我认为让这个小部件在屏幕上呈现是我迄今为止最伟大的开发壮举之一,这是我一生中第一次在用 JavaScript 编码时真正感受到“啊哈!”的时刻。对于那些想知道这花了多长时间的人来说,我花了大约 3 天的时间,抓狂不已,对着我的笔记本电脑大喊大叫,但最终我得到的回报是巨大的。
完成困难的部分后,只需按照以下操作即可 构建 JavaScript 动作的教程.
只用了一两个小时,我就完成了一个动作,它可以大声读出我作为参数给出的任何文本。对于那些只想找代码的人来说,这里是代码,但如果你是 JavaScript 动作的新手,我建议你遵循这个教程 Mendix.
// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import { Big } from "big.js";
// BEGIN EXTRA CODE
// END EXTRA CODE
/**
* @param {string} text
* @returns {Promise.<boolean>}
*/
export async function JS_TextToSpeech(text) {
// BEGIN USER CODE
if (!text) {
return false;
}
if ("speechSynthesis" in window === false) {
throw new Error("Browser does not support text to speech");
}
// const utterance = new SpeechSynthesisUtterance(text);
// window.speechSynthesis.speak(utterance);
// return true;
return new Promise(function(resolve, reject) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.onend = function() {
resolve(true);
};
utterance.onerror = function(event) {
reject("An error occured during playback: " + event.error);
};
window.speechSynthesis.speak(utterance);
});
// 最终用户代码
}
是时候测试一下了
事不宜迟,我想向大家介绍我的作品 梅维斯。
结束了
我想强调我在这个项目上学到了很多东西,虽然很有挑战性,但我强烈建议您亲自尝试一下。我本来想在这个版本中添加更多内容,但由于正在进行一些其他非常令人兴奋的项目,我不得不将其留在这里。
理想情况下,我希望小部件本身能够触发 Nanoflow,从而触发文本转语音 JavaScript 操作。我认为利用 props 将小部件中的对话提取回 Mendix,而不是简单地将其存储在小部件的状态中。我将继续研究这些功能,并可能在未来发布后续内容,但在此之前,我认为这对你们所有人来说都是一次很好的练习,可以尝试这样做!如果您使用它,请联系我,我很想看看这会给你们带来什么疯狂的想法。在此之前,请记住 - 去实现它!