動作確認(ローカル・プロクシ)
それでは、まずはローカルでプロクシを動かして音声認識をしてみましょう(図13)。
この場合は、mbedから同じネットワーク上にあるMac上で稼働しているプロクシに音声データを送り、そこからWSTに音声ファイルを送って認識結果を受け取ります(図の点線)。main.cppの以下の行を編集して、192.168.0.10の部分をプロクシを動かすMacのIPアドレスに変更します。
int responseCode = httpClient.post("http://192.168.0.10:9000/", request, &response);
変更できたら、コンパイルしてmbedに書き込み、リセットしてください。イーサネットの初期化が終わって、DHCPからIPアドレスが取得できると、黄色に点灯していたLEDが緑になります。ジョイスティックのボタンを押し込んで(すぐに離して構いません)、「明るくして」とマイクに話しかけてください。LED1からLED3までは、mbedの動作に応じて次のように変化します。
| LED1 | LED2 | LED3 | mbedの状態 |
|---|---|---|---|
| 消灯 | 消灯 | 消灯 | Joystickボタン押下待ち |
| 点灯 | 消灯 | 消灯 | 録音中 |
| 点灯 | 点灯 | 消灯 | 録音完了 |
| 点灯 | 点灯 | 点灯 | プロクシに音声データ転送中 |
認識がうまくいけば、LED4(USBポートを上に見た時に右下のLED)が点灯し、そうでなければ消灯します。プロクシは実行中に以下のような表示を行います。送信したwavファイルの場所や、認識結果も表示されるので、うまくいかない時は参考にしてください。最初はソースのコンパイルが行われるので少し時間がかかります。
[info] Compiling 5 Scala sources and 1 Java source to /Users/shanai/java/cz-mbed7/target/scala-2.11/classes...
[info] play.api.Play - Application started (Dev)
wav: /var/folders/hd/3vwf2zj95g19kvj45wq3_m640000gn/T/5500731341017076451.wav
response: '{"results":[{"alternatives":[{"confidence":0.653,"transcript":"分かる し "}],"final":true}],"result_index":0}'
transcript: '"分かる し "'
command: '分かるし'
この例では、「分かるし」と誤認識されています。もしも何回かやってもうまく認識できない場合は、wavファイルが表示されているので、このファイルを再生してみてください。
wav: /var/folders/hd/3vwf2zj95g19kvj45wq3_m640000gn/T/5500731341017076451.wav
このファイルは、本来は最後に消さないと残ってしまうのですが、現在は動作が確認しやすいように残したままにしてあります。このwavファイルの音量が小さいようならもっとマイクに近づいて、音が割れているようなら、もう少しマイクから離れてみてください。
プロクシの処理内容
プロクシの内容を簡単に解説しておきます。app/controllers/HomeController.scalaというファイルが処理が書かれたファイルになります。プロクシにリクエストがあると、indexという名前の関数が呼び出されます。
def index = Action.async { req => // (1)
println("Start index")
val file: File = req.body.asRaw.get.asFile
val temp: Path = Files.createTempFile(Paths.get("/tmp"), null, ".wav")
Files.copy(file.toPath, temp, StandardCopyOption.REPLACE_EXISTING) // (2)
val wav: Path = createWavFile(temp) // (3)
println("wav: " + wav.toAbsolutePath)
ws.url( // (4)
WatsonUrl
).withAuth(
WatsonUser, WatsonPassword, WSAuthScheme.BASIC
).withHeaders(
"Content-Type" -> "audio/wav"
).post(
wav.toFile
).map { response => // (5)
println("response: '" + response.json + "'")
val transcript = ((response.json \ "results")(0) \ "alternatives")(0) \ "transcript" match { // (6)
case JsDefined(v) => v.toString
case undefined: JsUndefined => "Undefined"
}
println("transcript: '" + transcript + "'")
val command = transcript.foldLeft(new StringBuilder) { (buf, c) => // (7)
if (c != ' ' && c != '"') buf.append(c)
else buf
}.toString
println("command: '" + command + "'")
Ok(if (command == "明るくして") "ON" else "OFF") // (8)
}
}
- (1)WSTにリクエストを送って結果を受け取る処理は、それなりに時間がかかる処理なので非同期に処理するようになっています(ここは、時間がかかる処理はこういう書きかたをするものと思ってもらえれば良いです)
- (2)アップロードされてきたデータを一時ファイルにコピーします
- (3)音声データをwavファイルに変換します。createWavFile()関数の定義は上にあるので、興味のある方は見てみてください
- (4)WSTに対して音声データ(wavファイル)を送信します
- (5)WSTからのレスポンス処理です
- (6)WSTからのレスポンスは、JSONなので内容をパースします。alternativesという要素の中に、認識された項目が1つ以上格納されるので最初の項目を取り出しています
- (7)WSTが認識した文書には、半角のスペースや引用符が含まれることがあるので削除しています
- (8)認識された文書が「明るくして」であれば、ONを、そうでなければOFFをmbedに返しています
