Day 20 - Native Speech Recognition

前言

JS 30 是由加拿大的全端工程师 Wes Bos 免费提供的 JavaScript 简单应用课程,课程主打 No FrameworksNo CompilersNo LibrariesNo Boilerplate 在30天的30部教学影片里,建立30个JavaScript的有趣小东西。

另外,Wes Bos 也很无私地在 Github 上公开了所有 JS 30 课程的程序码,有兴趣的话可以去 fork 或下载。


本日目标

利用 Web Speech API 取得使用者的麦克风存取权限进行语音辨识,然後把识别出的内容写到网页上。


设定本地服务器

跟昨天一样,要取得使用者的媒体装置必须是在安全连线之下(secure origin),所以我们必须自己架一个 little server。

今天不采用 Wes Bos 用 NPM 架 Server 的方法,因为我发现只要在Visual Studio 有安装 Live Server 这个延伸模组就可以不用安装一些 NPM 看不懂的套件,也能达到一样的效果。

  • 点击左方的方块(延伸模组 or Extension),然後搜寻Live Server安装後启用即可

  • 打开你的网页档案,右下角就会有一个Go Live的按钮,点下去就可以用localhost即时预览网页

这样有比用 NPM 来得轻松很多吧~~~


解析程序码

HTML 部分

div(.words) 用来放经过语音辨识後所产生的文字段落(<p>~</p>)。

<div class="words" contenteditable></div>

JS 部分

SpeechRecognition本身是一个在 browser 之下的 global variable,但是因为浏览器支援性的问题,我们在 Chrome 必须使用带有前缀字的webkitSpeechRecognition

window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

首先建立一个SpeechRecognition物件recognition来帮我们做语音辨识。

设定SpeechRecognition物件的属性interimResults为 true,它就会将现在辨识到的所有内容都回传,如果是 false 的话,则只有停止说话才回传内容。

设定SpeechRecognition物件的属性langen-US,表示要辨识的语言是英文。

const recognition = new SpeechRecognition();
recognition.interimResults = true;
recognition.lang = 'en-US';

建立<p>标签用来放入辨识出的文字内容。

宣告常数words并取得 div(.words)。

words.appendChild(p);,将<p>标签的文字段落放到div(.words)里面,换句话说就是把辨识出的内容先放到<p>再放到<div class="words">里面。

let p = document.creatElement('p');
const words = document.querySelector('.words');
words.appendChild(p);

SpeechRecognition物件recognition注册result event的监听器,如果有回传result就触发事件,然後把这个Speech Recognition Event印到 console,观察後你会发现我们需要用到的东西是放在‵e.results这个 lsit 里面。

recognition.start();开始语音识别,原则上只会识别一次,当停止说话一段时间即停止。

recognition.addEventListener('result',e=>{
    console.log(e); //log speech recognition event
    console.log(e.results);
    debugger; //breakpoint
});

recognition.start();    

SpeechRecognitionEvent

SpeechRecognitionEvent.results

其中 results[0] 隐含一些必要资讯,包括语音辨识出的内容(transcript)和是否已经停止辨识(isFinal)。

宣告常数transcript并透过两次map取得results[0].transcript也就是语音辨识出的内容。

因为results本身不是 Array 不能用 map(),所以先用Array.from()换成 Array。

第一次的 map() 取的 results[0] 所构成的阵列,第二次 map()取得 results[0] 内部 transcript 所构成的阵列,最後将阵列内的元素 transcriptjoin()串成一个大字串。

p.textContent = transcript;,将这次辨识出的所有内容放到<p>标签里面。

recognition.addEventListener('result',e=>{
    /*上略...*/
    const transcript = Array.from(e.results)
    .map(result => result[0])
    .map(result => result.transcript).join('');
    
    p.textContent = transcript; // will be overwrite
});

在第一次语音辨识结束之後,接下来它不会主动开始下一次的语音辨识。

我们可以在recognition注册end event,当语音辨识结束就触发下一次语音辨识开始(recognition.start)。

recognition.addEventListener('end',recognition.start);

目前为止我们还有一个问题没有解决,就是在每一次的语音辨识的过程,<p>标签会不断被覆写,因为我们没有为每一次的语音辨识建立自己的<p>标签,一直都是在改原来的那个。

下面我们用 if 判断当这次的语音辨识结束,就新增一个新的<p>标签到<div class="words">上,这样就不会有覆写的状况。

recognition.addEventListener('result',e=>{
    /*上略...*/
    if(e.results[0].isFinal){
      p = document.createElement('p');
      words.appendChild(p);
    }
});

最後,我们还可以加点有趣的东西,当语音辨识到特定内容,就在 console 上印出提示。

recognition.addEventListener('result',e=>{
    //上略...//
    if(transcript.includes('rainning day')){
      console.log('It is a bad weather.');
    }

    console.log(transcript);
});

补充资料:

使用 Web Speech API
SpeechRecognition

范例网页请点此


<<:  DAY20 搞样式--CSS Gird 怎麽用(下)?

>>:  【D6】试用厨具:合约资料(Contracts)

Day 5 - 回圈及 ++ --运算式

大家好,我是长风青云。 今天我跟朋友讨论到我参加比赛这件事,她是一个完全没有程序基础的人。 她告诉我...

Day 0x7 - Laravel 资料库连接设定、资料表规划

0x1 Laravel 资料库连接 请先确认 php.ini 的 pdo_pgsql extensi...

D 30 Python x Django 学习心得

从一开始接触Django到现在也一个月了 来简述跟总结一下自己认知到的技能 Django 网址传进来...

Angular ElementRef、TemplateRef、viewContainerRef 的区别

今天就来聊聊 ElementRef、TemplateRef、viewContainerRef,者实说...

Day 0xF - Web ATM 内容?,测试模拟交易回传资讯

0x1 前言 昨天订单回覆有个 Web ATM URL 好吸引我,想去瞧一下里面长什麽样, 今天也把...