NoodlでWeb Speech API Speech Recognitionを使う！Noodl Javascriptノードの使い方も解説

※この記事はNoodl2.0を使用しています

NoodlでWeb Speech API Speech Recognitionを使う方法です。
音声認識を使うとなると、バックエンド側の開発が必要となり大変ですよね。
Chromeのブラウザのみ対応になりますが、Web Speech API Speech Recognitionを使うと
数行のJavascriptで簡単にマイクから拾った音声をテキストにすることができます！

# 参考にした記事
Web Speech API Speech Recognitionについて、詳細はこちら
[Webページでブラウザの音声認識機能を使おう - Web Speech API Speech Recognition](https://qiita.com/hmmrjn/items/4b77a86030ed0071f548)

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">あけおめmicrobit！！<br>無音だけど音声認識してる<a href="https://twitter.com/hashtag/Noodl?src=hash&amp;ref_src=twsrc%5Etfw">#Noodl</a> <a href="https://twitter.com/hashtag/microbit?src=hash&amp;ref_src=twsrc%5Etfw">#microbit</a> <a href="https://twitter.com/hashtag/%E5%B9%B4%E8%B6%8A%E3%81%97Noodl?src=hash&amp;ref_src=twsrc%5Etfw">#年越しNoodl</a> <a href="https://t.co/nhiWXMCcbp">pic.twitter.com/nhiWXMCcbp</a></p>&mdash; 年越しはりねずみ麺 (@hedgehog_noodl) <a href="https://twitter.com/hedgehog_noodl/status/1212026759065063424?ref_src=twsrc%5Etfw">December 31, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

[サンプルページはコチラ](https://mizuki-kisa.github.io/chrome_voice_sample/)
※micro:bitは抜いてます。

# サンプルの使い方
## 1. Noodlのプロジェクトをクローンする
[ここからクローン](https://github.com/mizuki-kisa/chromeVoiceSample)し、[Noodl2.0](https://qiita.com/noodl-tokyo/items/e8d9e945c60433019026)で読み込んでください。

## 2. chromeをブラウザから開く
<img width="1552" alt="スクリーンショット 2020-01-01 22.09.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480279/11b08625-ac59-d981-be74-c7d4bbe4d07d.png">
右上のブラウザアイコン→URLをクリックします。

<img width="1436" alt="スクリーンショット 2020-01-01 22.10.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480279/a309ec9d-b803-5714-1b5f-726a38ef2d96.png">
URLの「192.168.xxx.xxx」を「localhost」に変更する。
※ブラウザの制限で、マイク機能はlocalhostかhttpsでないと動かないためです。[サーバーにデプロイ](https://qiita.com/kmaepu/items/d59fe69d1c5e3f591504)することでも解消されます。

## 3. ラーメン画像をクリックし、マイクを許可
ラーメンタップで音声認識が開始します。

# NoodlのJavascriptノードの解説
Noodl1.3のJavascriptノードと少し使用が変わっているようです。
基本的な使い方の説明と、NoodlでJavascriptノードで非同期通信するときのコツを記述します。

## inputsとoutputs
inputs: 任意の入力ポートを作ることができる。
outputs: 任意の出力ポートを作ることができる。

**書き方**
`inputの名前:'データ型'`
利用可能な型：'number', 'string', 'boolean', 'color', 'signal', 'collection'

## mySignal:function
特定のシグナルが来た場合に実行したいときに使用します。
例えば、タップしたら実行するなど。

```javascript

【任意のinput名その1（boolean）】:function{
//実行したい処理
},
【任意のinput名その2（boolean）】:function{
//実行したい処理
}
```
このように、複数追加もできるようです。
Noodl1.3ではこのような処理はif文で書いていました。2.0のほうがスッキリかけそうですね。

## change:function
inputの値のどれかが変更されたときに実行される。

## このプロジェクトのJavascriptノードの中身
サンプルでは、ラーメンをタップしたときにmySignalにtrueのシグナルを送り、音声認識を実行させています。

```javascript 
define({
	// The input ports of the Javascript node, name of input and type
	inputs:{
	    // ExampleInput:'number',
	    // Available types are 'number', 'string', 'boolean', 'color' and 'signal',
	    mySignal:'signal'
	},
	
	// The output ports of the Javascript node, name of output and type
	outputs:{
	    // ExampleOutput:'string',
	    text: "string",
	    received: "boolean",
	    finalTextReceived:"boolean"
	},
	
	// All signal inputs need their own function with the corresponding name that
	// will be run when a signal is received on the input.
	mySignal:function(inputs,outputs) {
		// ...
		outputs.received = false;
		outputs.finalTextReceived = false;
		
		outputs.received = false;
		SpeechRecognition = webkitSpeechRecognition || SpeechRecognition;
          const recognition = new SpeechRecognition();
          
          recognition.interimResults = true;
        
          recognition.onresult = (event) => {
            outputs.text = event.results[0][0].transcript;
            this.flagOutputDirty("text");
            this.sendSignalOnOutput("received");
            console.log(event.results[0].isFinal);
            outputs.finalTextReceived = (event.results[0].isFinal) ? true:false;
            this.flagOutputDirty("finalTextReceived");
          }
        
          recognition.start();
	},
	
	// This function will be called when any of the inputs have changed
	change:function(inputs,outputs) {
		// ...
	}
})


})


```
## Javascriptノードで非同期通信をするときのコツ
Javascriptノード内で非同期通信をすると、
レスポンスが返ってきているのにoutputsが変更されない！という問題に遭遇します。

基本的にNoodlはinputsに変更があったときに中の関数を実行するため、
実行後にレスポンスが返ってきても、きちんとトリガーを作らない限りoutputsに反映されません。

こうした問題を解決するため、JavascriptノードにはいくつかのAPIが用意されています。

`this.flagOutputDirty("変更を反映させたいoutputの名前")`
inputsの変更以外をトリガーに、outputsを変更させるときに使用します。
このサンプルでは、音声認識のレスポンスが返ってきたときに、認識結果の文字列を"text"の出力に反映させています。

`this.sendSignalOnOutput("任意のoutputの名前（boolean）")`
素早くfalseからtrueへの往復を繰り返すブーリアンの信号を発信します。
このサンプルでは、音声認識のレスポンスが返ってきたときに信号を送信し、
音声認識中のアニメーション（ラーメンの拡縮）を止めるトリガーにしています。

# まとめ
Javascript数行で音声認識ができました！すごい！
対応ブラウザがchromeのみなのが不便ですが、今後、他のブラウザにも実装されることを祈ります...！