kun432's blog

Alexaなどスマートスピーカーの話題中心に、Voiceflowの日本語情報を発信してます。たまにAWSやkubernetesなど。

APL for Audio を試す

Alexa Live 2020で発表された機能の一つに"APL for Audio"というものがあり、個人的にずっと切望してた機能なのでとても嬉しいです!早速やってみました。

これまでの課題

以前、AAJUG京都のLTで話してますが、Alexaで一つだけ不満があるのが「オーディオとスピーチを並列で再生できない」ということです。

これGoogleではできるんですよね。SSMLにparタグというのがあり、これを使うと並列再生ができるのです。

こういうSSMLを書きます。

</speak>
日本昔ばなしスキルです。今日のお話は、桃太郎。<break time="1s"/>
<par>
  <media xml:id="bgm" end="obaasan_story.end+2s" fadeOutDur="2s">
    <audio src="https://dl.dropboxusercontent.com/s/xxxxxxxxxx/momotaro.mp3" 
     soundLevel="-20dB"/>
  </media>
  <media xml:id="intro" begin="0.7s">
    <speak>むかーしむかし、あるところに、おじいさんとおばあさんが住んでいました。毎日、</speak>
  </media>
  <media xml:id="ojiisan_story" begin="intro.end+0.2s">
    <speak>おじいさんは、山に芝刈りに、</speak>
  </media>
  <media xml:id="ojiisan_sound" begin="ojiisan_story.end-0.5s">
    <audio src="https://dl.dropboxusercontent.com/s/xxxxxxxxxx/shibakari.mp3"   
     repeatCount="2"/>
  </media>
  <media xml:id="obaasan_story" begin="ojiisan_story.end+0.5s">
    <speak>おばあさんは、川に洗濯に、いっていました。</speak>
  </media>
  <media xml:id="obaasan_sound" begin="obaasan_story.end-2.0s" fadeOutDur="2s">
    <audio src="https://dl.dropboxusercontent.com/s/xxxxxxxxxx/sentaku.mp3" clipEnd="3s"/>
  </media>
</par>
</speak>

parタグで囲むとその中にある各mediaタグが並列で再生されます。mediaタグの属性で再生タイミングやボリュームなどのコントロールができるという、いかにもSSMLらしいものになっています。

デモはこんな感じです。

でこれがついにAlexaでもできるようになりました。それがAlexa Live 2020で紹介された「APL for Audio」です!

APL for Audioでできること

とはいうものの、SSMLでできるわけではなく、APLを使ってやる必要があります。具体的にAPL for Audioでできることを見てみましょう。

Alexa Live 2020で紹介された以下のスライドがまとまっています。

f:id:kun432:20200723064755p:plain

  • Alexaが使えるデバイスで利用可能。APLと言っているが画面なしデバイスでもOK
  • Alexaの発話とオーディオをミックス可能
  • 複数の発話・オーディオをミックス可能
  • 高品質なオーディオをサポート(44.1KHz/1411.20Kbps)
  • aac/mp3/ogg/opus/wav形式をサポート
  • 1回の発話で15ファイルまで使用可能
  • APLインタフェースを有効化しなくても利用可能

APLというからには画面付きデバイスのみかと思いましたが、Alexa Live 2020の説明では「マルチモーダル=ビジュアル」ではなく、オーディオもマルチモーダルの一つということでしたので、画面なしデバイスでも利用可能ということです。

Alexa Blogでも紹介されています(英語)

公式のドキュメントもあります。こちらもまだ英語だけですね。

やってみた

すでに以下の2つのサンプルコードが公開されています。

今回は前者のAPL&APL for Audioを使った「お天気スキル」のサンプルをやってみます。

先日、Alexa-hostedスキル新規作成時にgithubのレポジトリをインポートする機能が発表されましたので、これを使えばかんたんかなと思いきや、インポートできる構成になっていないようなので、Alexa-hostedでインポートできるようにしました。ついでに、日本語化+少し修正も入れてます。

上記にも記載していますが、

  1. Alexa開発者コンソールで新規スキル作成
  2. スキル名は適当でよい(はず)
  3. カスタム・Alexa-Hosted(Node.js)を選択 -「スキルを作成」をクリック
  4. テンプレートの選択画面で「スキルをインポート」をクリックして、URLに https://github.com/kun432/apl-for-audio-sample-no-look-weather-jp.git を入力してインポート
  5. テストシミュレータで「開発中」ステージに変更

で使えるようになります。呼び出し名「お天気のサンプル」で起動します。実際のデモはこちら。

お天気を喋っている後ろで音がなっているのがわかるでしょうか?きちんと音声とオーディオが並列で再生されていますね!これを待っていました!

コード

では少しコードを見てみましょう。APL for Audioのテンプレートはこんな感じになっています。"type":"APLA"になっているのが特徴的ですね。

{
    "type": "APLA",
    "version": "0.8",
    "mainTemplate": {
        "parameters": [
            "payload"
        ],
        "item": {
            "type": "Mixer",
            "items": [
                {
                    "type": "Speech",
                    "contentType": "SSML",
                    "content": "<speak>${payload.myData.ssml}</speak>"
                },
                {
                    "type": "Audio",
                    "when": "${payload.myData.audio != ''}",
                    "source":"${payload.myData.audio}",
                    "filters": [
                        {
                            "type": "Volume",
                            "amount": "40%"
                        },
                        {
                            "type": "FadeIn",
                            "duration": 300
                        },
                        {
                            "type": "FadeOut",
                            "duration": 300
                        }
                    ]
                }
            ]
        }
    }
}

item.itemsをまずみてください。"type":"Speech""type":"Audio"の2つが見えますね。これがAPL for Audioで再生される各要素になります。SpeechのほうがAlexaの発話でAudioのほうがサウンドです。

まずSpeech。まあこれは普通ですよね。

                {
                    "type": "Speech",
                    "contentType": "SSML",
                    "content": "<speak>${payload.myData.ssml}</speak>"
                },

そしてAudio。filltersでボリューム・フェードイン・フェードアウト・再生開始/終了タイミングなどを指定してサウンドのコントロールが可能です。

                {
                    "type": "Audio",
                    "when": "${payload.myData.audio != ''}",
                    "source":"${payload.myData.audio}",
                    "filters": [
                        {
                            "type": "Volume",
                            "amount": "40%"
                        },
                        {
                            "type": "FadeIn",
                            "duration": 300
                        },
                        {
                            "type": "FadeOut",
                            "duration": 300
                        }
                    ]
                }

そしてこれらをどういうふうに再生させるかを定義しているのがitem.typeになります。

        "item": {
            "type": "Mixer",
            "items": [
...snip...
            ],

mixer で並列再生、sequencerでシーケンシャルな再生、他にも条件付きの再生を行うSelectorがあります。Selectorの場合、上記audioで指定してあるようなWhenを使った条件だったり、ここには記載してないですが、storategyで子要素をランダムで再生する、データをランダムで再生する、などのコントロールもできるようですね。

これらのテンプレートにdatasoucesでデータを渡して、resonseBuilderにaddDirectiveする、というのは、これまでのAPLのやり方と変わっていませんね。

        return handlerInput.responseBuilder
            .addDirective({
                "type": "Alexa.Presentation.APLA.RenderDocument",
                "token": "token",
                "document": apl_audio,
                "datasources": {
                    "myData": {
                        "ssml": ssml,
                        "audio": audio
                    }
                }
            })
            .getResponse();

また、以下のようにビジュアルのAPLとオーディオのAPLを両方addDirectiveしてあげれば併用ができるようです。ちなみに、APL for Audioの方はAPLインタフェースを有効化しなくても使えるみたいです(なのでオーディオの方ではAlexa.getSupportedInterfaces(handlerInput.requestEnvelope)['Alexa.Presentation.APL'])をチェックしていない。ビジュアルの方では有効化が必要です)

        // Add APL directive to response
        if (Alexa.getSupportedInterfaces(handlerInput.requestEnvelope)['Alexa.Presentation.APL']) {
            // Create Render Directive
            handlerInput.responseBuilder
                .addDirective({
                    "type": "Alexa.Presentation.APL.RenderDocument",
                    "token": "token",
                    "document": apl_visuals,
                    "datasources": {
                        "myData": {
                            "bgImage": bgImage,
                            "currentTemp": weather[id].temp,
                            "weatherDescription": weather[id].description
                        }
                    }
                })
        }

        return handlerInput.responseBuilder
            .addDirective({
                "type": "Alexa.Presentation.APLA.RenderDocument",
                "token": "token",
                "document": apl_audio,
                "datasources": {
                    "myData": {
                        "ssml": ssml,
                        "audio": audio
                    }
                }
            })
            .getResponse();

APL、個人的にあまりやってないので、ちゃんと理解できていませんが、APL for Audioだけであればまずはざっくりこんな感じかなと思います。

所感

まずは以前から待ち望んでいた機能であり、要望にも投票していたので、これが使えるようになったのはとても嬉しいです。冒頭でご紹介したLT資料にも書いてあるとおり、表現力が非常に高まる上、オン・ザ・フライで動的にミックスできると開発工数の削減も見込めます。APLちょっと食わず嫌いなところがあったのですが、Audioのためだけに真面目にやるぞと思うぐらい、メリットが大きいと思っています。

ただ、やはり思うのは、「なぜこれをSSMLでできなかったか?」ということですよね・・・parタグはSMIL3で定義されているいわば共通規格のようなものであり、Googleがすでにやっているとはいえプロプライエタリな独自規格ではありません(拡張はされてるみたいだけど)。ここがSSMLとして共通化されていればAlexa・Googleマルチプラットフォームに対応する上でも都合が良いし、SSMLがVUIの標準規格として全体のエコシステム的にもよかったはず。

なので、ここにAPLを持ってきた、というのはやや微妙な感じもしてます。

が、まあ前向きにできることが増えたと考えてやっていきたいなと思います。

おまけ

そして、APL for Audio、実はVoiceflowも対応してます!まだ先行で一部のユーザのみのようですが、リリースが待ち遠しいですね!

そして、Alexa ConversationsにもVoiceflowはどうやら対応するようです!これも期待して待ちましょう!

まとめ

AudioのためにAPLやっていくぞ!!!

追記

7/30

もう一つのサンプルも日本語化しました。こちらもAlexa-hosetedでインポートできます。ぜひお試しください。

github.com