Amazon Alexa用のクライアントをiOSで作ってみた
1 はじめに
Amazon Alexaは、AmazonによるクラウドベースのAI(人工知能)音声アシスタントです。Alexaに対応したデバイスを使用すると、デバイスに話しかけると、同サービスからその処理結果を音声で受け取り再生されます。
現在の提供されているデバイスには、下記のようなものがあります。
※ 上記のハードウエアは、全て無線が必要ですが、現時点(2017.03.14現在)で、技適マークを取得したものは無いようですので、これらのデバイスについては、残念ながら、まだ、日本国内で使用できません。
また、アプリも幾つか公開されています。
- Amazon Alexa Amazon 公式アプリ(日本のストアでは表示されません)
- Lexi $180
- echosim.io (Alexa Skill Testion Tool) Webアプリ
上のようなAlexa対応デバイス(アプリ)は、Amazonから提供されている、Alexa Voice Service(AVS) を使用することで作成が可能です。
今回は、この Alexa Voice Service(AVS) を使用して、iOSでAlexaクライアントを実装してみました。なお、今回作成したものは、Alexaのごく限られた機能だけの実装であることを予めご了承ください。
最初に、作成したアプリを使用しているようすです。
2 Alexa Voice Service
Alexa Voice Service(AVS) を使用すると、マイクとスピーカーが利用可能なデバイスでAlexaの機能(音楽再生、タイマー、アラーム、カレンダー管理やサードパーティー製のスキル)にアクセスすることができます。
作成する機能は、概ね以下のとおりです。
- 認証
- 録音開始
- マイクから音声を録音
- 音声データをAPIへ送信
- 処理後の音声データを受信
- スピーカーで再生
販売されているデバイスなどでは、「Alexa!」と呼びかけることで、2.の録音開始の状態に移行しているようですが、今回作成したアプリでは、ボタンを押すことで、録音開始としましたので、「Alexa!」との呼びかける必要ありません。
3 認証
アプリが、AVS のAPIにアクセスするためには、Amazonアクセストークン(Login with Amazon) が必要です。
Login with Amazonとは、Amazonアカウントで利用可能な、OAuth 2.0ベースの認証連携(シングルサインオン機能)です。
Login with Amazonを実装するには、 https://developer.amazon.com/sdk-downloadからSDK ( Amazon-iOS-SDKs.zip )をダウンロードします。
続いて、zipを解凍して得られる、LoginWithAmazon.frameworkをSafariServices.framework及び、Security.framewokrと共にプロジェクトに追加します。
Swiftから利用するためには、Bridging-Headerも必要になります。
#import <LoginWithAmazon/LoginWithAmazon.h>
最後に、Webブラウザからのリダイレクトをスキームで受けれるように、URL Schemesで、Bundle Identifireの前にamzn- を付けたスキームを指定します。
ここまでの、準備が整えば、下記のようにLogin with Amazonでアクセストークンが取得できます。
class LoginViewController: UIViewController, AIAuthenticationDelegate { // ・・・省略・・・ @IBAction func tapLoginButton(_ sender: Any) { let SCOPE_DATA = "{\"alexa:all\":{\"productID\":\"\(プロダクトID)\",\"productInstanceAttributes\":{\"deviceSerialNumber\":\"\(デバイスシリアル番号)\"}}}" AIMobileLib.authorizeUser(forScopes: ["alexa:all"], delegate: self, options: [kAIOptionScopeData:SCOPE_DATA]) } // MARK: AIAuthenticationDelegate func requestDidSucceed(_ apiResult: APIResult!) { if apiResult.api == .authorizeUser { AIMobileLib.getAccessToken(forScopes: ["alexa:all"], withOverrideParams: nil, delegate: self) } else { // アクセストークン let accessToken = apiResult.result! as! String } } // ・・・省略・・・ }
3 録音及び再生
録音については、AVAudioRecorder、再生については、AVAudioPlayerをそれぞれ使用しました。
下記は、受信したデータを再生している実装の例です。
func startPlay(data: Data){ do{ try audioSession.setCategory(AVAudioSessionCategoryPlayback) try audioSession.setActive(true) self.player = try AVAudioPlayer(data: data) self.player?.volume = 1.0 self.player?.delegate = self self.player?.play() } catch let error as NSError { print(error) } }
録音及び再生については、各所で詳しく紹介されておりますので、そちらをご参照ください。
https://developer.apple.com/reference/avfoundation/avaudiorecorder
https://developer.apple.com/reference/avfoundation/avaudioplayer
Qiita Swift2で録音と再生ができる簡易レコーダーを作ってみた
AVAudioRecorder の基本的な使い方
4 RestAPI
AlexaのAPIは、HTTP/2で提供されており、クライアントからクラウド側に対して、何か事象が発生した事を通知する、「イベント」 と、逆に、クラウドからの指示となるディレクティブの2種類のメッセージが定義されています。
下図は、公式ドキュメント内のものですが、Alexaへ音声を送る Recognizeイベント を表したものです。
Structuring an HTTP/2 Request to AVS より
HTTP本体は、2つのマルチパートメッセージで構成されており、1つ目に、Json形式で必要なデータを詰め込み、2つ目で音声データを格納しています。
Alexaから返されるディレクティブについても、同様にマルチパートメッセージで構成されています。
各イベント・ディレクトリの詳細につては、下記のドキュメントをご参照ください。
System Interface https://developer.amazon.com/public/solutions/alexa/alexa-voice-service/reference/system
5 最後に
今回は、簡単なAlexaのデバイスをiOSで実装してみました。
話しかけた私の英語が、聞き取れないときは、「Sorry I didn’t understand the question I heard.」 のような返答が返ってきます。 まずは、伝わる英語を話せるようにならないといけないようです。
6 参考リンク
Alexa Voice Service API Overview
Use the Login with Amazon SDK for iOS APIs
Create a Login with Amazon Project (iOS)