Web Speech APIで本当にしゃべるかどうかを判定するには？

Web Speech APIを使うとJavaScriptを使ってしゃべらせることができるんですが、しゃべってくれない環境もあります。それをチェックできないか思い試してみました。

## その1. SpeechSynthesisUtteranceが定義されているか？でチェック
以下のようにSpeechSynthesisUtteranceが定義されているかをチェックしてみました。

```js
function canSpeech() {
	if (typeof SpeechSynthesisUtterance != "undefined") {return true;}
	return false;
}
```

残念ながら、私の手元の環境では全ての環境でtrueとなってしまいました。よほどクラシックなブラウザでなければこの方法は使えないようです。

## その2. イベントが発生するしないでチェック
onstartやonerrorといったイベントが発生しますので、それをキャッチすることで判定できないかと思い、以下のような簡単なプログラムを作ってみました。以下のコードは https://hachisukansw.github.io/speech-test/ で試すことができます。

```html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Speech Test</title>
</head>
<body>
<table border="1" cellspacing="0" cellpadding="4">
<tr><td></td><td>defined</td><td>error</td><td>start</td><td>end</td></tr>
<tr>
<td><input type="button" id="enUS" value="Hello"></td>
<td><span id="enUSDef"></span></td>
<td><span id="enUSError"></span></td>
<td><span id="enUSStart"></span></td>
<td><span id="enUSEnd"></span></td>
</tr>
<tr>
<td><input type="button" id="jaJP" value="こんにちは"></td>
<td><span id="jaJPDef"></span></td>
<td><span id="jaJPError"></span></td>
<td><span id="jaJPStart"></span></td>
<td><span id="jaJPEnd"></span></td>
</tr>
</table>
</body>
<script type="text/javascript">
document.getElementById("enUS").addEventListener("click", function(e) {
	document.getElementById("enUSDef").textContent = typeof SpeechSynthesisUtterance;
	if (typeof SpeechSynthesisUtterance == "undefined") {return}
	var u = new SpeechSynthesisUtterance();
	u.clickTime = new Date();
	u.addEventListener("error", function(e) {
		document.getElementById("enUSError").textContent = (new Date() - this.clickTime) + "ms";
	});
	u.addEventListener("start", function(e) {
		this.startTime = new Date();
		document.getElementById("enUSStart").textContent = (new Date() - this.clickTime) + "ms";
	});
	u.addEventListener("end", function(e) {
		document.getElementById("enUSEnd").textContent = (new Date() - this.startTime) + "ms";
	});
	u.text = "hello";
	u.lang = 'en-US';
	u.rate = 1.0;
    //u.volume = 0;
	window.speechSynthesis.speak(u);
});
document.getElementById("jaJP").addEventListener("click", function(e) {
	document.getElementById("jaJPDef").textContent = typeof SpeechSynthesisUtterance;
	if (typeof SpeechSynthesisUtterance == "undefined") {return}
	var u = new SpeechSynthesisUtterance();
	u.clickTime = new Date();
	u.addEventListener("error", function(e) {
		document.getElementById("jaJPError").textContent = (new Date() - this.clickTime) + "ms";
	});
	u.addEventListener("start", function(e) {
		this.startTime = new Date();
		document.getElementById("jaJPStart").textContent = (new Date() - this.clickTime) + "ms";
	});
	u.addEventListener("end", function(e) {
		document.getElementById("jaJPEnd").textContent = (new Date() - this.startTime) + "ms";
	});
	u.text = "こんにちは";
	u.lang = 'ja-JP';
	u.rate = 1.0;
    //u.volume = 0;
	window.speechSynthesis.speak(u);
	})
</script>
</html>
```

結果は以下のとおりです。

### ja-JPの結果
|OS等|ブラウザ|音声|error|start|end|
|:--|:--|:-:|:--|:--|:--|
|Ubuntu12-16|Chrome|○||272ms|1320ms|
||Firefox|×||||
|Win10(15063)|Chrome|○||86ms|1185ms|
||Firefox|○||91ms|1067ms|
||Edge|○||37ms|2282ms|
|Win7|Chrome|○||281ms|1210ms|
||Firefox|×||287ms|0ms|
|iOS10.2(iPad)|Chrome|○||150ms|1096ms|
||Firefox|○||150ms|1100ms|
||Safari|○||172ms|1085ms|


### en-USの結果
|OS等|ブラウザ|音声|error|start|end|
|:--|:--|:-:|:--|:--|:--|
|Ubuntu12-16|Chrome|○||272ms|1320ms|
||Firefox|×||||
|Win10(15063)|Chrome|○||251ms|710ms|
||Firefox|○||477ms|2064ms|
||Edge|×|8ms|||
|Win7|Chrome|○||273ms|685ms|
||Firefox|○||360ms|1066ms|
|iOS10.2(iPad)|Chrome|○||650ms|1108ms|
||Firefox|○||222ms|1100ms|
||Safari|○||573ms|1107ms|

## まとめ
* ほとんどの場合、対応していなくてもerrorイベントは発生しない。（例外）Edgeは発生する。
* ほとんどの場合、しゃべらない場合はonstartイベントが発生しない。（例外）Win7のFirefoxは発生する。
* 音量をゼロにしても結果は同じ。

ということで音量をゼロにしてonstartをチェックすることでほぼほぼ確認できそうです。それで十分な気もするのですがWin7のFirefoxの挙動まで考えるとonendイベントでタイムを測定して…みたいな処理が必要なのかもしれないです。