#はじめに
ちょっとした環境音スキルを作る時に躓いたポイントがあったので、簡単に記事として残したいと思います。
#Alexaで音を扱うには
大きく分けて2つの方法があります。
- SSMLのAudioタグを利用する
- AudioPlayerインターフェイス
SSMLのAudioタグを利用する
通常のspeak
タグの中にaudio
タグを含めることで、スキルからのレスポンスで音声を再生することができます。
<speak>
タクシー予約へようこそ。
<audio src="soundbank://soundlibrary/transportation/amzn_sfx_car_accelerate_01" />
配車または料金の見積もりをリクエストできます。
どちらにしますか?
</speak>
Audioタグのメリット
セッションが切れないので、1つの返答として扱える
セッションが切れないので、音声再生後に何らかのアクションを継続的に受け取ることができます。
もくもく会などでゲームスキルがよく取り扱われますが、ゲームのプレイヤーが考えている間に音楽を流してセッションを意図的に継続させるときによく使われています。
Audioタグのデメリット
音声の再生時間の上限がある
1つの返答内容にAudioタグは、最大5つまで含めることができます。
また、5つのファイルの合計再生時間が**240秒(4分)**以内であることが必須です。
AudioPlayerインターフェイス
AudioPlayerインターフェースは、オーディオをストリーミングしたり再生状況を監視したりするディレクティブとリクエストを提供します。スキルは、ディレクティブを送信して再生を開始したり停止したりできます。AlexaサービスはAudioPlayerリクエストをスキルに送信し、トラックの終了間際や再生の開始時と停止時など、再生状況に関する情報を提供します。また、Alexaは、リモコンなどのハードウェアボタンや、画面付きのAlexa搭載デバイスの次へ/前へタッチコントロールに応答して、PlaybackControllerリクエストも送信します。
要は、Alexaスキルでジュークボックススキルを作ったりする時に利用する音楽再生用のインターフェイスのことです。
AudioPlayerインターフェイスのメリット
任意のhpptsホスティングしている音源を再生することができる。
Audioタグと異なり再生するファイル、再生時間の制約がありません。
なので、音源さえ用意できればスキル経由で、好きな音源を再生することができます。
AudioPlayerインターフェイスのデメリット
セッションが継続できない
音源の再生開始後、セッションが完了されてしまうので、スキル側からの返答を行うことができません。
なので、音源再生開始後にAlexa、ヘルプ!
と発話するとスキル側のヘルプインテントではなく、Alexaそのもののヘルプ時の返答が帰ってきます。
AudioPlayerを使うと、音を使ってスキルから返答するようなことは出来ないということです。
Audioの使い分け
スキル内での体験を向上させたい
→ SSMLのaudio
タグを利用する
スキルを通して音楽を利用したい
→ AudioPlayerインターフェイスを活用しましょう
AudioPlayerインターフェイスで同一音源をループ再生するには
今回の本題です。
最近流行り(流行ってないかも...)の環境音スキルなど、特定の環境を再現するために特定の音源をループ再生したいと思うこともあるかと思います。
そのような時、次の音源の読み込みにはPlaybackNearlyFinishedリクエスト
を用いて次の音源を処理することになります。
//再生したい音源のURL
const soundUrl = "https://hogehoge/fugafuga.mp3";
//再生終了直前のインテント
const playbackNearlyFinishedHandler = {
canHandle(handlerInput) {
return Util.checkIntentTypeName(handlerInput, 'AudioPlayer.PlaybackNearlyFinished');
},
handle(handlerInput) {
return playbackNearlyFinished(handlerInput);
}
}
//再生終了直前のロジック部分
function playbackNearlyFinished(handlerInput) {
let audioPlayer = handlerInput.requestEnvelope.context.AudioPlayer;
let url = soundUrl; //再生音源URLの指定
let token = audioPlayer.token; //オーディオストリームを識別するためのトークン
let expectedPreviousToken = audioPlayer.token; //前のストリームの識別トークン
return handlerInput.responseBuilder
.addAudioPlayerPlayDirective('ENQUEUE', url, token, 0, expectedPreviousToken)
.getResponse();
}
※Util.checkIntentTypeName
では、RequestTypeとIntentNameの判別を行っています。
当初、同一音源なので、再生音源のURLとtokenは前のストリームと同じものを付与していました。
しかし、上記のコードでは新しいストリームとして生成することが出来ず、指定したファイルの再生時間後自動で再生が停止してしまいます。
ループ再生するには
//再生したい音源のURL
const soundUrl = "https://hogehoge/fugafuga.mp3";
//再生終了直前のインテント
const playbackNearlyFinishedHandler = {
canHandle(handlerInput) {
return Util.checkIntentTypeName(handlerInput, 'AudioPlayer.PlaybackNearlyFinished');
},
handle(handlerInput) {
return playbackNearlyFinished(handlerInput);
}
}
//再生終了直前のロジック部分
function playbackNearlyFinished(handlerInput) {
let audioPlayer = handlerInput.requestEnvelope.context.AudioPlayer;
let url = soundUrl; //再生音源URLの指定
>> let token = String(Math.random()); //オーディオストリームを識別するためのトークン
let expectedPreviousToken = audioPlayer.token; //前のストリームの識別トークン
return handlerInput.responseBuilder
.addAudioPlayerPlayDirective('ENQUEUE', url, token, 0, expectedPreviousToken)
.getResponse();
}
原因は単純なことです。
token値の更新を行わず、前回のストリームと同一トークンを設定していたため、前回のストリームと新しいストリームの識別が出来ず、新しいストリームの再生が行えなかったということでした。
なので、非常に簡易的な方法として新しいTokenはMath.random()
などで生成した乱数をStirngにキャストし、addAudioPlayerPlayDirective
の新しいストリームのtoken値の引数に先程の乱数を渡すことで、前回のストリームと新しいストリームの識別が可能になり、同一音源のループ再生が可能になります。
非常に初歩的な見落としですが、audioplayerのスキルの開発事例がまだ少なく見落としがちなポイントでしたので記録として参考になれば幸いです。
最後に
毎月、Amazon Japan目黒本社にて「もくもく会」が開催されています。
もくもく会と聞くと、スキルがある人がもくもくと開発するようなイメージになりがちですが、全くAlexaスキルの開発経験がない方も多く参加していただいており、初学者向けのスキル開発講習なんかも冒頭に行われています。
最近参加人数が少し減り気味なので、興味がある方は是非参加してみてください!
詳細は下記からチェック!
Amazon Alexa 関連のイベント ハンズオントレーニング、コミュニティーイベント