2013年12月16日月曜日

TwilioとGoogle Speech APIを使った音声変換について

Twilioを使うと、通話中に相手の会話を録音することができます。もちろん、録音されたデータはダウンロードが可能であり、留守番電話のような音声ファイルを使った様々なサービスを実装することができます。
Twilioには音声変換エンジンによる文字変換機能(transcribe)も用意されていますが、残念ながら日本語による変換はサポートされていません。

 そこで、日本語の音声ファイルをテキスト変換する方法として、GoogleのSpeech APIを用いる方法を調査してみましたので、今回はその方法をご紹介します。

 Twilioから音声変換するまでの流れ

  1. Twilio上での録音機能について
  2. 録音データの変換
  3. Google Speech APIの呼び出し



 Twilio上での録音機能について

※すでにTwilioについて詳しい方は、読み飛ばして構いません。
Twilio上で会話を録音するには、TwiMLの<Record>動詞を使います。次の例は、シンプルな留守番電話を実現します。

<?xml version="1.0" encoding="UTF-8"?>
<!-- page located at http://example.com/voicemail_record.xml -->
<Response>
  <Say>
    発信おんの後に、メッセージをどうぞ。
    終わりましたら何かキーを押してください。
  </Say>
  <Record
    action="http://foo.edu/handleRecording.php"
    method="GET"
    maxLength="20"
    finishOnKey="0123456789*#"
  />
  <Say>メッセージはお預かりできませんでした。</Say>
</Response>

この例では、ガイダンス(ちなみに「発信音」と指定すると「はっしんおと」と発声してしまうので、「発信おん」と指定するのがポイントですw)が流れた後に最大20秒の録音が開始されます(maxLengthパラメータで録音時間を指定でき、デフォルトは1時間となります)。録音を終了するキーに0〜9と#、*が設定されているため、何かキーを押すと録音を終了します。
発信音の後に無音が5秒続くと、録音はされずに最後の<Say>が実行されます。
正常に録音がされると、最後の<Say>は実行されずに、actionパラメータで指定されたURLに対してGETリクエストを送信します。

リクエストには以下のパラメータが付与されています。

RecordingUrl
 録音された音声ファイルのURL。
RecordingDuration
 録音された音声ファイルの長さ(秒)。
Digits
 ユーザによって押されたキー。

<Record>動詞のリファレンスは以下にあります。
https://jp.twilio.com/docs/api/twiml/record


 録音データの変換

録音されたデータは、明示的な削除を行わない限りTwilioサーバ側で保存されます。
なお、サーバー側の録音データについては、10,000分までは無料ですが、それ以降は1分につき0.1円が月額費用として請求されるので、不要になった録音データはREST APIを使って削除するようにしましょう。
録音データの削除に関するREST APIは以下にあります。
https://jp.twilio.com/docs/api/rest/recording

 Twilioサーバーに保存された録音データを抜き出すには、上記REST APIを利用することもできますが、もっとも一般的でかつ簡単な方法は<Record>動詞内で設定したactionパラメータを使うことです。
actionパラメータで指定したURLに対して、Twilio側から戻ってくるリクエストに含まれるRecordingUrlが録音データへのURLとなります。
なお、RecordingUrlの接続先オーディオファイルはWAV形式となります。RecordingUrlの最後に「.mp3」を指定すると、mp3形式でダウンロードすることもできます。

 Twilioで録音されたそれぞれのデータ形式は以下のとおりです。

WAV形式

チャンネル数:1
サンプリングレート:8000
量子化ビット:16
ビットレート:128k

mp3形式

チャンネル数:1
サンプリングレート:22050
量子化ビット:16
ビットレート:32.0k

 なお、音声ファイルのサイズは、WAV形式の方がmp3形式より3倍以上大きくなります。

 さて、ここからが音声変換のための準備となります。

 Speech APIを利用するためには、音声ファイルをFLAC形式に変換する必要があります。変換にはオープンソースのFLACを利用しますので、あらかじめ準備をしておく必要があります。CentOSではyumでインストールが可能ですし、MacやWindows版のFLACもここに公開されています。今回はCentOS上にインストールしたflacコマンドを利用します。
FLACはmp3からの変換に対応していないため、変換にはWAV形式の音声ファイルを利用します。WAVファイルのFLAC変換は以下のコマンドで行います。

 flac -n ソースファイル.wav
(-n:nは0〜8までの数値となり、圧縮率を指定します。8がもっとも圧縮率が高く、0は無圧縮です)

 このコマンドによって、拡張子がflacになった音声ファイルが作成されます。
他にもオプションが用意されていますが、詳しくはFLACのリファレンスを参照してください。
なお、FLACによる変換では、元データのサンプリングレートなどは変更されませんので、例えばTwilioの録音データ(WAV形式)であれば、サンプリングレートは8000のままとなります。


 Google Speech APIの呼出し

Googleは、Chromeブラウザ(バージョン25以降)において、JavaScriptから利用可能なWeb Speech APIを提供しています。なお、Web Speech APIについては、W3Cにて標準化が検討されている状況です。
今回は、GoogleがWeb Speech API向けに独自に用意している「Speech API」という機能を使って、REST形式で音声変換を行う方法を紹介します。
従来、このAPIは非公開APIとして存在していたものなのですが、最近になって正式に公開されました。また、今までの単純なREST方式ではなく、双方向の送受信にも対応しているため、より効率よく音声変換が可能になっています。

ただし、現在のところ以下の利用制限が設けられています(制限を解除することもできなくはなさそうなのですが、これ以上の調査はしていません)。

  • 開発用で、かつ個人利用であること
  • 1日のリクエスト数は50まで


 Speech APIを利用するためには、事前に以下のサイトにてAPIキーを取得する必要があります(別途、Google Cloud Consoleへのアカウント取得とサインインが必要です)。
https://cloud.google.com
ただし、Speech APIはUSもしくはCanadaユーザしか一覧には出てこないので、日本のユーザは、別途以下のChromium-devグループに加入することで表示されるようになります。
https://groups.google.com/a/chromium.org/

 私の場合は、Speech APIをONにしても、2日間はSpeech APIが認証エラーになり、3日目あたりから成功するようになりました(謎)。
なお、今回音声変換を行うためのAPIキーについては、WebApplicationのServer Keyを利用します(たぶん、Browser Keyでもいけると思います)。

 Google Speech APIについては、以下のブログが大変役に立ちます。
http://mikepultz.com/2013/07/google-speech-api-full-duplex-php-version/

上記サイト内には、PHPのプラグインも公開されているので、もしPHPを利用しているのであれば、APIキーさえ用意すればそのまま利用することもできるでしょう。

 PHPではなく、curlを使って変換をするコマンド例も以下に紹介します。この例では、ローカルフォルダにある、test.flacという音声ファイルを変換しています。

 curl -v "https://www.google.com/speech-api/full-duplex/v1/down?pair=3456123487654321" & curl -v -X POST "https://www.google.com/speech-api/full-duplex/v1/up?lang=ja-JP&lm=dictation&client=chromium&pair=3456123487654321&key=[SpeechAPIKey]" --header "Transfer-Encoding: chunked" --header "Content-Type: audio/x-flac; rate=8000"  --data-binary @test.flac

 コマンドを見てもらうとわかるように、送信用のURLと受信用のURLを同時に実行しています。それぞれのURLに含まれるpairパラメータについては、送信と受信で同じ16バイトのランダムな文字列を指定すれば良いようです。SpeechAPIKeyは、皆さんが取得したSpeech APIのServer Keyを指定します。
Twilioの録音データのサンプリングレートは8000ですので、rateパラメータには8000を指定しないといけません。また、日本語変換をするためには、langパラメータにja-JPを指定します。

 変換結果はJSON形式で戻ってきます。以下にサンプル音声データを変換した結果を示します(見やすいように整形しています)。

{
  "result":[
    {"alternative":[
      {"transcript":"長寿庵さんですか ざるそば 二つ島 おかめそばひとつ 大至急お願いします","confidence":0.51536459},
      {"transcript":"長寿庵さんですか ざるそば二つ島 おかめそばひとつ 大至急お願いします"},
      {"transcript":"長寿庵さんですか ざるそば 二傳 おかめそばひとつ 大至急お願いします"},
      {"transcript":"長寿庵さんですか ざるそばスタートおかめそばひとつ 大至急お願いします"},
      {"transcript":"長寿庵さんですか ざるそば 二ツ島おかめそばひとつ 大至急お願いします"}
    ],"final":true}
  ],"result_index":0
}

データを見るとわかるように、いくつか変換候補が戻ります。先頭の結果にconfidenceパラメータが戻っていますが、これが変換結果の信頼性を表しており、値が1に近ければ近いほど正確な変換ができたことを表すようです。

 Google Speech APIについては、ネット上でもあまり情報がなく、現時点では手探りの状態で参考になるかどうか判りませんが、変換精度はかなり良いようなので、今後に期待したいところです。

2013年9月9日月曜日

Evernote Businessでサーバー同期エラー

Evernoteのビジネスバージョンで、管理者がWeb上の管理コンソールのノートブックの管理からビジネスノートブックを強制的に削除すると、そのノートブックに参加していたユーザのクライアントアプリ(今回はWindows版を利用中)で同期に失敗します。
エラーメッセージは「サーバー側で予期しない問題が発生しました」。
このメッセージの解決方法は、ゴミ箱を削除するとか、プロキシーの設定を確認するなどなのですが、今回はいずれの方法でも解決しませんでした。

で、見つけた解決策は以下のとおりです。

  1. 同期に失敗する各ユーザアカウントでWebのEvernoteにサインインします。
  2. 参加中のノートブックに、管理者によって削除されたノートブックが出てくるので、それをクリックします。
  3. 以下のダイアログが表示されるので、OKを押します。


アプリを再インストールしたり、色々試したけどすべて駄目だったので途方に暮れていましたが、なんとか解決して良かったです。

2013年3月22日金曜日

AWSのVPCにおけるMTUとMSS設定について

Amazon Web ServicesのVPCを、YAMAHA RTX1100で構築していますが、以下の現象が発生して困っておりました。

viコマンドやls -lコマンドなどを利用した場合に、sshがフリーズしてしまう(再現性あり)


  • VPC内の端末間では現象がおきないことから、VPC(AWSと自社側)の間で何か問題が発生しているっぽい。
  • 特定のコマンドや、特定のディレクトリをリストした場合に現象が出ることから、送受信データに問題があるっぽい。


ということで、疑わしいところはパケットサイズではないかと睨み、MTUのサイズをpingコマンドで探し出してみた。
ping -f -l 1362 -n 1 サーバアドレス ←これはOK
ping -f -l 1363 -n 1 サーバアドレス ←これはNG
ということで、MTUサイズは1362+20(IPヘッダ)+8(ICMPヘッダ)=1390
MSSサイズは、MTU-20(IPヘッダ)-20(TCPヘッダ)となるので、1390-40=1350が正しいサイズになる。

そこで、tunnel select 1とtunnel select 2に
ip tunnel mtu 1390
ip tunnel tcp mss limit 1350
を記載したところ、現象が改善された!

VPCを設定する際に、AWSがルータのコンフィグを自動生成してくれるのだが、このコンフィグではMSSが1387になっているので修正する必要がある(MTUについてはコンフィグには記載なし)。

2013年2月13日水曜日

AWSのDNSフェイルオーバーについて

Amazon Web Services(AWS)のDNSサービスである「Route53」にて、フェイルオーバー機能がサポートされたとのことでしたので、早速調査を行いました。

【AWS発表】Route53にDNSフェイルオーバー機能が追加。S3のウェブホスティング機能と連携したバックアップサイトを作成可能に。


詳しい設定手順などは上記ブログに任せるとして、ここではこの機能の概要と、設定する際の注意点についてまとめます。
図1 Route53によるDNSフェイルオーバー
まずはこの機能の概念図を見てください(クリックして拡大)。
この図では、架空のドメイン(hoge.com)をRoute53(DNS)で管理しています。通常のDNS運用では、たとえば「www.hoge.com」というウェブサイトに対して、Aレコードを定義しています。

DNSフェイルオーバーでは、この通常のAレコードをプライマリとし、プライマリのAレコードに新設された「Health Check」という機能を紐付けます。「Health Check」とは、その名のとおり死活監視をする機能で、世界中のAmazonロケーションから定期的にターゲットサイトを監視します。
監視方法は、HTTPかTCPによるもので、HTTPを使った場合は400番より小さいレスポンスを2秒以内に返さないとエラーと判断します(TCPの場合は4秒以内にバーチャルサーキットが開設できないとエラーとなるようです)。
実際に構築してみたところ、約16箇所から60秒/箇所間隔でパケットが届きました。なので、すくなくとも16アクセス/分となります。上記の公式サイトでは30秒間隔とあるので、実際はもっと増える可能性もあります。

エラーになった場合に利用されるのが、フェイルオーバー機能により新設されたセカンダリのAレコードです。これは、プライマリのAレコードのエイリアスとして定義するもので、通常は存在していないので、フェイルオーバーを設定する際に追加する必要があります。
セカンダリのAレコードでは、飛び先としてS3の静的ウェブサイトの他、EC2なども選択が可能です(エイリアスターゲットと呼びます)。S3の静的ウェブサイトでは、運用コストを非常に安くすることができるので、ここにお詫びのコンテンツをいれておくことで、Health Checkにてエラーと判断された場合に、自動的にそのコンテンツが表示されるという仕組みです。
S3で静的なウェブサイトを作成する方法については、下記のリンクが役に立ちます。

なお、Health Checkは前述のとおり、かなり細かい間隔で行われるため、DNSの返答先の切り替え自体は早いのですが、プライマリのAレコードに指定したTTL(情報の生存時間)が有効なため、フェイルオーバーを設定する際は、プライマリAレコードのTTLを60秒にする(通常は300秒など)ことを推奨しているようです。

最後に注意点です。
  1. エイリアスターゲットにS3を利用する場合、SSLが使えません。
    これはS3側の静的ウェブサイトに起因しますが、S3の静的ウェブサイトでは現在SSLに非対応なため、DNSがS3に切り替えたとしても接続ができず、接続エラー(サーバーが見つからない)になります。
  2. 同じくエイリアスターゲットにS3を利用する場合、バケット名はターゲットサイトと同じ名前にしておくこと。
    上の図でいうと、www.hoge.comという名前のバケットを作成しておく必要があります。これは、セカンダリAレコードのエイリアスターゲットの指定画面では、バケット名までは指定することができず、暗黙的にターゲットサイトの名前のバケットが利用されるからです。
    この制限によって、たとえば、複数のサイトを管理している場合に、同じエラー画面を1つのS3バケットで使い回しすることができないことを意味します。
サーバが何らかの理由によって(たとえば正規のメンテナンスも含む)停止する場合、DNSでフェイルオーバーができるのはかなり強力な機能といえます。今回紹介したようなお詫びコンテンツをS3で運用するだけでなく、たとえばバックアップサイトをEC2で構築しておき、そちらに切り替える(LBでやればいいじゃんって話もありますが)などの利用方法が考えられます。