Experts.jp日本に、もっとエキスパートを。2014で最優秀賞を頂きました。コトバツナギは、SpeechRecognitionとWebRTCを軸にしたマルチユーザーコンテンツです。このコンテンツで使っている主な技術の解説をしたいと思います。概要コトバツナギは、声に出して声で遊ぶマルチユーザーコトバアソビゲームです。PCサイトを開き、スマートフォンを皆で持って集まって遊ぶゲームになっています。スマートフォンの代わりにマイク機能付きのノートPCでも可です。WebRTCとwebkitSpeechRecognitionを使っているため、2014年8月の時点ではPC・スマホともにChromeブラウザのみ対応です。iOSのChromeはWebRTCとwebkitSpeechRecognitionに非対応のため不可ですが、MacのChromeは動作します。動作環境が限られるので遊べないユーザーがいる場合もあると思い、デモ動画も用意しました。画像検索で、できてます。次章より詳しく説明していきます。getUserMediaとSSLWebRTCでカメラ・マイク入力を利用可能にするgetUserMediaは、使用する度にブラウザに許可を得るダイアログが出てしまいます。このままだとゲームとして遊ぶには問題がありますが、httpsでアクセスできるようにすると回避できるため、SSLを導入しました。httpsの場合、アクセスしているドメインに対して最初に1回だけ許可すると、その後の許可は不要になります。(ブラウザを閉じても、設定をクリアするまで有効)コトバツナギでは、カメラ・マイクでそれぞれ許可ダイアログが出るので、最初に計2回の許可が必要になります。getUserMediaのvideo・audioを一度に扱えば一回で済みますが、マイク入力は使えるけどカメラが使えない(またはカメラの利用が嫌だ)という場合にも対応するため、このコンテンツではそれぞれの許可を分けました。PeerJSPCとスマートフォン間でWebRTCによる通信を実現するためにPeerJSを利用しています。serviceを用意してくれていますがSSLに対応していません。今回は、レンタルしているVPSにPeerServerをインストールして、下記のサーバーサイドjavascriptをnode.jsのforeverコマンドで起動し、PeerServerを立てました。fs.readFileSync('[sslのcrtファイルのパスが入ります]')}});アイコトバでハジメルPCとスマートフォンを接続する際に、数字入力や文字入力をするコンテンツがよくありますが、その方法はあまり味気ないと以前より思っていたので、このコンテンツでは別の方法を検討しました。タップと声だけで遊べるコンテンツにしたかったので、アイコトバを声に出して言ってから始める仕組みにしています。コトバツナギではアイコトバやゲーム本編での音声認識に、webkitSpeechRecognitionを使ってます。アイコトバは予めいくつか用意しておいて、認識した音声を対応する数字に変換するという仕組みをとっています。//webkitSpeechRecognitionは複数の解析候補を出力してくれる場合があるが、このコンテンツでは一つだけに絞って使用。//webkitSpeechRecognitionは複数の解析候補を出力してくれる場合があるが、このコンテンツでは一つだけに絞って使用。resultText=event.results[0][0].transcript;//resultTextを予め用意してあるリストに照合して、マッチしたら該当の数字に変換}recognition.start();なお、webkitSpeechRecognitionは、onresult以外に、onstart、onend、onaudiostart、onsoundstart、onspeechstart、onspeechend、onsoundend、onaudioend、onnomatch、onerrorという多くのイベントがあり、必要に応じて使い分けることが重要になります。解析結果(上記resultText)を数字に変換したら、WebRTCのDataChannel(PeerJSのDataConnection)を利用してPC側のゲーム本体と通信します。peer.connect('[接続先のゲーム本体のID番号。peer.connect('[接続先のゲーム本体のID番号。function(data){//接続先から送られてきたdataを処理}数字入力や文字入力を使った方が動作は安定するのですが、あえてアイコトバを使うようにしたのは、声に出して遊ぶゲームであることをゲーム本編の前にユーザーに体験してもらい、確認・練習してもらうという意図があります。ゲーム本編の前にマイク入力の許可をさせておきたいという狙いもあります。ゲームモードの選択ゲームは「シリトリでアソブ」「レンソウでアソブ」の2つのモードで遊べるようにしています。ゲームモードを選択する際、スマートフォンの場合にはタップ操作以外に端末を右か左に傾けることでも選べるようにしました。参加者みんなが体を傾けて選択している絵が作れたら面白いと思い、こういう機能をつけてます。端末側の傾き検知は、以下のようになっています。必要に応じて、傾き検知を開始・停止できるようにしておくと便利です。//端末が傾くと呼ばれる。実際にはaddEventListnerしていると常に呼ばれ続ける。stopDeviceMotion=function(){window.removeEventListener('devicemotion',onDevicemotion);};//端末が傾くと呼ばれる。実際にはaddEventListnerしていると常に呼ばれ続ける。data={mode:"gravity",gx:gravity.x,gy:gravity.y};connect.send(data);};ユーザーの端末傾き情報をゲーム側で受け取って、ゲームモード決定するところは以下のようになっています。このコンテンツのように一対多で接続する場合は、WebRTCのDataChannel(PeerJSのDataConnection)を接続毎に保持して、各端末からの通信を処理します。//一対多の接続の場合は端末から接続がある毎に呼ばれるので、connectを保持しておく。//data.gx、data.gyを元にアイコンを移動させる。{//一対多の接続の場合は端末から接続がある毎に呼ばれるので、connectを保持しておく。onGravity=function(data){//data.gx、data.gyを元にアイコンを移動させる。selectMode;}}ゲームモード決定時には「シリトリでアソブ」か「レンソウでアソブ」と、ゲーム本体から声が出ますが、これは音声合成で発話しています。1;speechSynthesis.speak(speech);//speechSynthesisはwindowが持っているオブジェクトゲーム本番シリトリゲーム、または連想ゲームでコトバをつないで遊びます。「シリトリでアソブ」のゲームの処理の流れを、利用している技術要素とともに図にしました。おおまかには、1~8の流れでコトバをつないでいきます。カメラの利用が許可されている場合、回答する度に毎回操作端末側のカメラでユーザーを撮影して、ゲーム本体に画像を送信しています(フロー2)。ユーザーの操作端末がノートPCの場合は、回答の音声を端末側で録音し、blobデータとしてゲーム本体に送信しています。(フロー2、3)録音にはrecorder.jsを利用しました。Workerとして使う仕組みになっているようです。//エラーの場合は何もしない。//スマホの場合はwebkitSpeechRecognitionと同時併用できないのでエラーになる。Recorder(source,{workerPath:[recorderWorker.jsまでのパス]"});rec.record();},function(err){//エラーの場合は何もしない。//スマホの場合はwebkitSpeechRecognitionと同時併用できないのでエラーになる。onRecExported=function(blob){//blobをWebRTCでゲーム本体に送信};スマートフォンの場合、2014年8月現在ではSpeechRecognitionとgetUserMediaのaudioを同時に使えない仕様になっていたので、残念ながらスマートフォンの場合は録音を諦めました。(SpeechRecognitionを同時に使わなければ、スマートフォンでも録音することができます)なお、ここで録音撮影した音声・画像は、ゲーム終了時に使います。ゲーム終了ゲーム終了すると、これまでつないだコトバが連続して再生されます。getUserMediaのvideoが使える場合、終了時に流れる各コトバに表示されているユーザー画像は、ゲーム中に回答した時に、毎回撮影したものを表示しています。(ゲーム本番のフロー2で撮影)それぞれのコトバを発声した時に、どんな顔をしていたかがわかるようになっています。(ページ上部のデモ動画参照)また、ノートPCを使って回答したユーザーのコトバの場合、ゲーム中に回答した時に、録音した音声を再生します。(ゲーム本番のフロー12で録音)上述の通りスマートフォンでは音声録音がSpeechRecognitionと併用できないので、スマートフォンを使って回答したユーザーのコトバの場合は、音声合成で代用します。//audioContextはコンテンツ内でnewできる数に上限があるため、何度も同じ処理をする場合は、毎回newするのはなるべく避ける。//このコンテンツのように同時発音する可能性がなければ、AudioContextは再利用した方がnew上限エラーにならないので良いと思われる。//audioblobがない場合は合成音声で発話。recognitionTextはゲーム時に保存していた音声解析のテキスト。AudioContext();//audioBlobはゲーム時に保存していたblobデータif(audioBlob){//audioContextはコンテンツ内でnewできる数に上限があるため、何度も同じ処理をする場合は、毎回newするのはなるべく避ける。//このコンテンツのように同時発音する可能性がなければ、AudioContextは再利用した方がnew上限エラーにならないので良いと思われる。buffer;source.connect(audioContext.destination);source.start(0);});//audioblobがない場合は合成音声で発話。recognitionTextはゲーム時に保存していた音声解析のテキスト。1;speechSynthesis.speak(speech);}最後にコトバツナギの主な仕組みは上記のようになっています。ゲーム本編だけでなく、ゲーム終了後にも参加者みんなで結果を振り返って楽しんでもらえるように作っています。音声解析がなかなかうまくいかないことも多いですが、思わぬ解析結果になることも含めて楽しんでもらえれば幸いです。よかったら遊んでみてください。