Amazon Alexa用のクライアントをiOSで作ってみた

Alexa Skills Kit

1 はじめに

Amazon Alexaは、AmazonによるクラウドベースのAI(人工知能)音声アシスタントです。Alexaに対応したデバイスを使用すると、デバイスに話しかけると、同サービスからその処理結果を音声で受け取り再生されます。

現在の提供されているデバイスには、下記のようなものがあります。

※ 上記のハードウエアは、全て無線が必要ですが、現時点(2017.03.14現在)で、技適マークを取得したものは無いようですので、これらのデバイスについては、残念ながら、まだ、日本国内で使用できません。

また、アプリも幾つか公開されています。

上のようなAlexa対応デバイス(アプリ)は、Amazonから提供されている、Alexa Voice Service(AVS) を使用することで作成が可能です。

今回は、この Alexa Voice Service(AVS) を使用して、iOSでAlexaクライアントを実装してみました。なお、今回作成したものは、Alexaのごく限られた機能だけの実装であることを予めご了承ください。

最初に、作成したアプリを使用しているようすです。

2 Alexa Voice Service

Alexa Voice Service(AVS) を使用すると、マイクとスピーカーが利用可能なデバイスでAlexaの機能(音楽再生、タイマー、アラーム、カレンダー管理やサードパーティー製のスキル)にアクセスすることができます。

作成する機能は、概ね以下のとおりです。

  1. 認証
  2. 録音開始
  3. マイクから音声を録音
  4. 音声データをAPIへ送信
  5. 処理後の音声データを受信
  6. スピーカーで再生

販売されているデバイスなどでは、「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 )をダウンロードします。

002

続いて、zipを解凍して得られる、LoginWithAmazon.frameworkSafariServices.framework及び、Security.framewokrと共にプロジェクトに追加します。

001

Swiftから利用するためには、Bridging-Headerも必要になります。

#import <LoginWithAmazon/LoginWithAmazon.h>

003

最後に、Webブラウザからのリダイレクトをスキームで受けれるように、URL Schemesで、Bundle Identifireの前にamzn- を付けたスキームを指定します。

004

ここまでの、準備が整えば、下記のように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種類のメッセージが定義されています。


System Interface

下図は、公式ドキュメント内のものですが、Alexaへ音声を送る Recognizeイベント を表したものです。

Structuring an HTTP/2 Request to AVS より 005

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)

AWS Cloud Roadshow 2017 福岡

  • iOSDeveloper

    いつも拝見しています。解説記事を書いていただきありがとうございます。

    LoginWithAmazonによる認証のところでつまずいていて、AIAuthenticationDelegateのrequestDidFailで
    “One or more input parameters are wrong.”とエラーメッセージが吐かれます。
    scopeDataに間違いがある気がしているのですが、プロダクトIDは開発者コンソールのID or AmazonID or 他のものか、デバイスシリアル番号とはiPhoneの設定から見られるシリアル番号でよろしいでしょうか?
    お手数おかけしますが教えていただけるでしょうか。よろしくお願いいたします。

  • SIN

    すいません、そのエラーは見たことが無いのですが・・・
    プロダクトIDは開発者コンソールの「Application Type ID」です。
    シリアル番号は、なんでもOKです。勝手に決めていいです。

    • iOSDeveloper

      ご返信ありがとうございます。Application Type IDは試してみましたがダメでした。
      AppDelegateでopen urlの部分のurlを見ると
      {URLSchemeで設定したもの}:?methodName=signin&error_description=An+unknown+scope+was+requested&state={36桁のランダムのような文字列}&error=invalid_scopeとあるので渡しているscopeがおかしそうです。

      login時の処理では
      let scopeData = “{“alexa:all”:{“productID”:”{Application Type ID}”,”productInstanceAttributes”:{“deviceSerialNumber”:”123456″}}}”
      AIMobileLib.authorizeUser(
      forScopes: [“alexa: all”],
      delegate: self,
      options: [kAIOptionScopeData: scopeData]
      )
      と書いています。

      • SIN

        [“alexa: all”] -> [“alexa:all”]
        でどうでしょう?

        • iOSDeveloper

          typoでしたね……無事にアクセストークンが取れたのでまた進めてみます!
          ご回答本当にありがとうございました!