Alexa APL, 第8回　タッチ対応　その2（SpeakItem）

#はじめに
Alexaを搭載した画面付きデバイスの画面レイアウトを作成します。
画面の画像、もしくは商品名をタッチすると、その商品名を答えるレイアウトを作成します。
TouchWrapperコンポーネントを使って、スキルを経由せず、SpeakItemを使って、応答する方法を実施します。

##今回実施する内容
画面の画像、もしくは商品名をタッチすると、その商品名を答えるレイアウトを作成します。

中型デバイス（Echo Show用）
![中型レイアウト1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/243c2fce-b79d-9a5b-04b6-4d7282fbb660.jpeg)

#環境
OS:Windows 10 JP
Alexaスキル言語：Node.js
Editor：Visual Studio Code
APLバージョン：1.0, 1.1

#参考
・[Alexa ハローAPL、Alexaスキルの画面への対応](https://qiita.com/Mount/items/72d9928ff2c0ae5de737)
第1回のAlexa APLの記事です。タイトル通り、ハローAPLを表示させるだけのAPLです。

・[Alexa APL, 第7回　タッチ対応　その１（SendEvent）](https://qiita.com/Mount/items/6593efb1f4fc71f479a2)
第7回のタッチ対応の記事です。SendEventを使った商品名を答えるAPLです。

・[APLコマンド](https://developer.amazon.com/ja/docs/alexa-presentation-language/apl-commands.html)
AmazonのAPLドキュメントにあるAPLコマンドの説明です。

・[標準コマンド](https://developer.amazon.com/ja/docs/alexa-presentation-language/apl-standard-commands.html)
AmazonのAPLドキュメントにあるコマンドの説明です。SendEventやSpeakItemの説明があります。

・[APLデータソースとトランスフォーマー](https://developer.amazon.com/ja/docs/alexa-presentation-language/apl-data-source.html)
SSMLを音声に変換する仕組みの説明があります。

#用語
##APL
Alexa Presentation Language
amazonの画面つきのAlexaの画面表示用の言語。
JSONを使用した記載方法です。
インターネットのホームページはHTMLとCSSで作成しますが、AlexaはAPLで作成するということです。

##APLオーサリングツール
APL作成を視覚的に見ながらAPLのJSONファイルを作成するツール。
サンプルテンプレートも準備されており、その中から選択していくだけで、だいたいの画面は作成できる。

##SSML
音声合成マークアップ言語。
<speack>xxxx</speak>のように囲んで音声を発話させる言語。

#前提条件
前提条件はとくにないといえばないですが、本まとめを読むにあたり、以下がわかっていることが前提です。
・alexa developer consoleのアカウントがある
・Alexaスキルを開発したことがある
・JSONの記載方法を知っている
・「Alexa ハローAPL、Alexaスキルの画面への対応」の記事をみている

#タッチ対応の仕組み
画面タッチする場合も、基本的には音声による対話と同様の仕組みで、タッチが処理されます。イメージ図を示します。
![概要図2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/e5db84db-f6d7-51c7-85f1-b36ad2975f77.jpeg)

   - 図の通り、発話した場合、Alexaを通して「スキル」にリクエストを送信し処理することで、「音声」や「画面表示」を応答できます。
   - タッチした場合も同様に、「スキル」にリクエストを送信（SendEvent）し処理することで、「音声」や「画面表示」を応答できます。
   - タッチした場合、「スキル」にリクエストを送信しなくても、「音声」の応答をする方法（SpeakItem）を使うこともできます。どの装置が処理しているか定かではないですが、Alexaなのかな？と思っています。音声をバッファリングしているのか、毎回Alexaが答えるのかわかっていませんけど。

#タッチ対応
##レイアウトの作成
今回作成するレイアウトの構成は、以下のようなイメージです。
![レイアウト1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/72d5a3c8-c33a-8e89-91a0-8c7f7c698b9e.jpeg)

第7回と基本的に同じです。差分を**強調**で示します。
1. APLオーサリングツールを起動して「最初から作成」を選択する。
2. 「レイアウト画面」の「mainTemplate」を選択し、「Container」を追加する。
3. 「レイアウト画面」の「Container」を選択し、「TouchWrapper」を追加する。
ここのプロパティは後ほど追加する。
4. 「レイアウト画面」の「TouchWrapper」を選択し、「Container」を追加する。
5. 「レイアウト画面」の「Container」を選択し、「詳細設定画面」で、以下を設定する。

　　・ direction：row

6. 「レイアウト画面」の｛Container」を選択し、「Image」を追加する。
7. 「レイアウト画面」の「Image」を選択し、「詳細設定画面」で、以下をそれぞれ設定する。

      ・ source：https://coffee_480x480.jpg
      ・ width：20vw
      ・ height：20vh
8. 「レイアウト画面」の｛Container」を選択し、「Text」を追加する。
9. 「レイアウト画面」の「Text」を選択し、「詳細設定画面」で、以下を設定する。

      ・ text：コーヒー
      **・ speech：${payload.sample.properties.ssmlSpeech}**
      **・ id：speechText**
10. APL JSONの画面を開いて、「TouchWrapper」の記載を以下の通り、`onPress`に`type`と`componentId`を追加する（詳細設定画面でOnPressに値を設定するのと同じことですが、入力ボックスが小さいため、ソースに書き込んだほうがやりやすいです）。

```json
...
"type": "TouchWrapper",
"onPress": {
    "type": "SpeakItem",
    "componentId": "speechText"
},
...
```

##データJSONの作成
続いてデータJSONを以下の通り作成する。
上記のレイアウト作成では、`Text`のプロパティに`speech`を変数で設定しました。この部分の元データを作成します。
下記の記載は特殊なため、次の章でまとめて説明します。

```データJSON
{
  "sample": {
    "properties" : {
      "ssmlSpeech" : "<speak>コーヒー</speak>"
    },
    "transformers": [
      {
        "inputPath": "ssmlSpeech",
        "transformer": "ssmlToSpeech"
      }
    ]
  }
}
```

##TouchWrapperとSpeakItemの補足
- TouchWrapperは、画面レイアウトに設定すると、その部分がタッチされたことを検出します。
`onPress`の`type`で設定したコマンドが実施されます。
- `SpeakItem`は、タッチしたときに`componentId`で指定したコンポーネントに記載された`speech`の内容を発話するコマンドです。スキルは経由しません。
今回は`Text`に`id`を`speechText`に設定し、また`speech`で発話する内容を記載します。
- なお、TouchWrapperの範囲は、その上に置いたコンポーネントの範囲ではなく、TouchWrapperの範囲です。今回作成したAPLではタッチの範囲は下図の通り、幅は全体になります。
- タッチする範囲をImageやTextの範囲にしたい場合は、TouchWrapperのWidthとheightを調整してきっちり合わせる必要があります。
![概要図2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/ba30d797-341b-1ebb-f591-cf88027d5544.jpeg)
- `Text`の`speech`には、変数を設定していますが、ここに直接発話する文字列を記載しても音声は再生されません。`speech`に設定するのは音声データであり、文字列ではありません。したがって、**文字列を音声データに変換**する必要があります。そのため`Text`では変数にし、データJSONで処理します。
  - データJSON（datasources）では、`properties`と`transformer`を規定する。
  - `properties`には、音声にするSSMLを記載する。
  - `transformer`の`transformer`に`ssmlToSpeech`を設定すると`inputPath`に設定した`properties`配下のSSMLを音声に変換し、`outputName`として出力する。
  - `outputName`を省略すると`inputPath`と同じ名称で出力する。

![トランスフォーマー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/be93da67-403a-05e9-89b3-09351decc6c0.jpeg)

##トランスフォーマーの補足
上記のトランスフォーマーで、SSMLから音声データへ変換するということはわかりましたが、実際inputPathはどのように変換されたのかを確認してみました。
以下のように、上記で作成したAPLの`Text`に、inputPath（実際には出力名と同じ）を出力しました。
そうすると、amazonのサーバーのアドレスでmp3に変換されていることがわかりました。
そのアドレスを改めて、`Text`の`speech`に入れてみたところ、今回は話させようとした「コーヒー」と音声がでました。
もう少し詳しくソースも載せて再説明します。

- 以下のように、`text`を書き換えて、画面に値を表示する。
     
 ```json:APL
     ...
     {
         "type": "Text",
         "speech": "${payload.sample.properties.ssmlSpeech}",
         "id": "speechText",
         "text": "${payload.sample.properties.ssmlSpeech}"　　<=この部分
     }
     ...
 ```

- 表示された出力は、以下のような感じでした。

```
https://tinytts-us-west-2.amazon.com/....../resource.mp3
```

- 上記アドレスを、`speech`に代入してこれで音声が鳴るか確認する。

 ```json:APL
     ...
     {
         "type": "Text",
         "speech": "https://tinytts-us-west-2.amazon.com/....../resource.mp3",　　<=この部分
         "id": "speechText",
         "text": "${payload.sample.properties.ssmlSpeech}"
     }
     ...
 ```
- みごとに音声はなりました。

すなわち、トランスフォーマーで`ssmlToSpeech`を実施すると、SSMLが**mp3ファイル**に変換されて、amazonサーバーに保存されてそれの再生を行うということだと思います。

なお、こんなことを通常することはあり得ないと思います。通常は`transformer`を使えばいいとは思います。
amazonサーバーに保存されたデータもいつまで保存されるものなのかもわかりませんし、テンポラリのものと思います。
ただ、`Text`の`speech`にテキストデータ（今回だとコーヒー）を入力することでAlexaに会話をさせることができず、データJSON上で、`properties`やら`transformers`を設定しなければならず、やりたいこと（会話させること）はシンプルなのにコードは面倒とギャップが大きく、理解もしがたかったため、何が起きているのかを確認してみました。
通常、何気なく、`speak`や`reprompt`に文字列を記載してAlexaに会話させていることも同様にSSMLから**mp3ファイル**に変換が起こっているんだろうなと思いました。カプセル化されていてプログラマーは気づかないだけの事。
それがAPL上で実施すると、トランスフォーマーを使って文字列を音声に変換するソースも記載しないいけないということなんだと理解しました。

余談です。
試していないのですが、トランスフォーマーで`outputName`を使用する場合で、`inputPath`と違う名称（例えば、`ssmlSpeechOutput`）で出力すると、`Text`の`speech`に設定する値は`${payload.sample.properties.ssmlSpeech}`ではなく、`${payload.sample.properties.ssmlSpeechOutput}`になるようです。
inputPathを出力し別の変数に入れるため違う名称になるというのは理解できないこともないのですが、`properties`の後の名称が、データJSON上に存在しない`outputName`、すなわち`ssmlSpeechOutput`になってしまうのは、解せないですね。
今の私のspeckItemの使い方の理解では、`outputName`を必要としないため使わないので気にはならないのですけどね。

##APLソースコード
出来上がったAPLのフルソースコードとデータJSONを載せます。実際にスキルで表示を試す場合は、[Alexa ハローAPL、Alexaスキルの画面への対応](https://qiita.com/Mount/items/72d9928ff2c0ae5de737)を参考しに実施します。

```json:APL
{
    "document": {
        "type": "APL",
        "version": "1.1",
        "settings": {},
        "theme": "dark",
        "import": [],
        "resources": [],
        "styles": {},
        "onMount": [],
        "graphics": {},
        "commands": {},
        "layouts": {},
        "mainTemplate": {
            "parameters": [
                "payload"
            ],
            "items": [
                {
                    "type": "Container",
                    "items": [
                        {
                            "type": "TouchWrapper",
                            "onPress": {
                                "type": "SpeakItem",
                                "componentId": "speechText"
                            },
                            "items": [
                                {
                                    "type": "Container",
                                    "direction": "row",
                                    "items": [
                                        {
                                            "type": "Image",
                                            "width": "20vw",
                                            "height": "20vh",
                                            "source": "https://coffee_480x480.jpg"
                                        },
                                        {
                                            "type": "Text",
                                            "speech": "${payload.sample.properties.ssmlSpeech}",
                                            "id": "speechText",
                                            "text": "コーヒー"
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    },
    "datasources": {
        "sample": {
            "properties": {
                "ssmlSpeech": "<speak>コーヒー</speak>"
            },
            "transformers": [
                {
                    "inputPath": "ssmlSpeech",
                    "transformer": "ssmlToSpeech"
                }
            ]
        }
    }
}
```

#おわりに
今回は、以下について説明しました。
・TouchWrapperを使った画面レイアウト
・TouchWrapperでSpeakItemを使った音声の再生
・SSMLから音声データへのトランスフォーマー
・TouchWrapperのタッチ範囲