こんにちは。サイバードエンジニアによる CYBIRD Advent Calendar 2022 、10日目担当の @kyukkyu81 です。
9日目は @ayany0_zzZ さんによる「AR.jsを使って恐竜を家に呼んでみた」でした。
AR,js使うことでここまで短いソースで恐竜召喚できる事も驚きですが、それ以前にコーディングと一緒に3DCGアプリケーションをサクっといじっちゃうのがなんていうか「いまどき」だなぁ、と思いました。イイネ!
はじめに
(ここ、ほぼほぼ去年のと同じです)
趣味も仕事も似たようなことをしているのですが、amazon EchoShow等のAlexa対応端末で、カスタムスキル(スマホで言うアプリのようなもの)を開発・公開しており、特に趣味で開発しているものについては画面付き端末で画面表示言語APL(Alexa Presentation Language)を駆使してアクションゲームの実現に情熱を注いでいます。スマートスピーカーなのに画面制御メイン。こんなニッチなジャンルのせいか、同志がめちゃめちゃ少ないです。求む同志。
ちなみに[apl.ninja]というサイトがありまして、先ほどのAPL言語の虜となった人たちが自作した「Alexaの画面表示部分」を発表したり、みんなが利用・学習できるテンプレートを作って公開するソーシャルプラットフォームです。
公式のAlexaカスタムスキルはスキルとして完成されている必要があり、審査を通過しなければ公開することができませんが、こちらは審査無くどんなに簡単なサンプルでも即公開することができます。
興味ある方は覗いてみてください。私も参戦しています。
歌うアレクサ
去年はアレクサでラップやボイパに挑戦しましたが、今年はさらに無謀にも「アレクサで歌を歌う」に挑戦してみようと思います。何が無謀かと言うと…去年すっぱりと
- 音程を表現できないことをあきらめる
と断言した部分、これを諦めなかったらどうなるか!ってことをやってみようかと。
今回もamazon Alexa対応端末のAPLA(APL for Audio)という言語で鳴らしますが、その中で扱っているSSML(音声合成マークアップ言語)は発話・会話を目的として作られた言語であるため、歌を歌うような音階の指示はできませんが、ある程度ピッチ(音の高低)をいじるタグがあるので。そこになんとか可能性を見出してみようかと思います。
※念のため先に書いておきますが、amazon社自身が提供している「アレクササンバ」や「アレクサ音頭」のようにはいかないことをご了承ください。
ざっくりルールを決める
繰り返しますがボカロではないので、SSML言語でできる範囲でやります。
- あたかもリズムが取れているような演出を目指す
- 音程を表現できないことをあきらめ
ない
- mp3等の音源は使わず純粋にアレクサ姉さんの声だけで挑戦
- SSMLタグの限界に挑戦
- AlexaのAPLA言語のほうも工夫してみる
実践方法
誰でも無料でAlexaのカスタムスキルを作ることが出来る「Alexa開発コンソール」というものがあります。
そこから適当にダミーのスキルを作り、「マルチモーダル」⇒「オーディオ」⇒「オーディオ応答を作成」⇒「Blank Document」のボタンをペペッと押していきます(ひな形を選ぶこともできますが今回は1から作っていきます)。
さぁやっぞ
まず「あー」と言わせます。
{
"type": "APLA",
"version": "0.91",
"compositions": {},
"mainTemplate": {
"parameters": [
"payload"
],
"item": {
"type": "Sequencer",
"items": [
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak>あー</speak>"
},
]
}
}
}
SSMLに音声のトーン(高さ)を変えるタグがあるのでこれを使っていきます。
"content": "<speak><prosody pitch=\"-30%\">あー</prosody></speak>"
そして耳を頼りに「ドレミファソラシド」に聞こえるpitchの数値を作っていきます。
■■■クリックしてJSONコードを表示■■■
{
"type": "APLA",
"version": "0.91",
"compositions": {},
"mainTemplate": {
"parameters": [
"payload"
],
"item": {
"type": "Sequencer",
"items": [
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak><prosody pitch=\"-30%\">あー</prosody></speak>"
},
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak><prosody pitch=\"-20%\">あー</prosody></speak>"
},
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak><prosody pitch=\"-10%\">あー</prosody></speak>"
},
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak><prosody pitch=\"-5%\">あー</prosody></speak>"
},
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak><prosody pitch=\"+5%\">あー</prosody></speak>"
},
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak><prosody pitch=\"+20%\">あー</prosody></speak>"
},
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak><prosody pitch=\"+35%\">あー</prosody></speak>"
},
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak><prosody pitch=\"+42.5%\">あー</prosody></speak>"
}
]
}
}
}
基本となる要素はこちらです。
- mainTemplate ⇒ この中が本体
- Mixer ⇒ 記載した要素を同時に実行する
- Sequencer ⇒ 記載した要素を順次実行する
- Speech ⇒ しゃべる
Speech内のcontentに記載しているのが、実際のSSMLです。HTMLのようなタグで表記されています。
実際に鳴らすと、こんな感じになります。
本人の予想を超えてちゃんと音階になっているので一人で感動。
pitchの数値も想像してたより切りのいい数字で。(もし私の耳が緩くて音階全然違ぇよ!って感じだったらすいません)
ちなみにスケールがE♭メジャー(ミのフラットから音階が始まっている)のは、pitchの数値が-33.3~+50で指定する必要があり、1オクターブぎりぎり収めるためです。
ここにAPLAの制御を加え、ハミングさせてみます。
■■■クリックしてJSONコードを表示■■■
{
"type": "APLA",
"version": "0.91",
"compositions": {},
"mainTemplate": {
"parameters": [
"payload"
],
"item": {
"type": "Sequencer",
"description": "発話高さ(pitch)、発話速度(rate)、発話内容",
"data": [
[
[ -10, 100, "ん" ],
[ -10, 100, "ん" ],
[ -10, 25, "ん" ],
[ 0, 100, "" ]
],
[
[ -10, 100, "ん" ],
[ -10, 100, "ん" ],
[ -10, 25, "ん" ],
[ 0, 100, "" ]
],
[
[ -10, 100, "ん" ],
[ 5, 100, "ん" ],
[ -30, 100, "ん" ],
[ -20, 100, "ん" ]
],
[
[ -10, 20, "ん" ],
[ 0, 100, "" ],
[ 0, 100, "" ],
[ 0, 100, "" ]
]
],
"items": [
{
"type": "Mixer",
"data": "${data}",
"items": [
{
"type": "Speech",
"when": "${index == 0}",
"contentType": "SSML",
"content": "<speak><break time=\"1.5s\"/><sub alias=\"\">${index}</sub></speak>"
},
{
"type": "Sequencer",
"duration": "trimToParent",
"bind": [
{
"name": "step",
"value": "${index}"
}
],
"items": [
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak><break time=\"${step * 0.375}s\"/><sub alias=\"\">${data[0]}</sub></speak>"
},
{
"type": "Speech",
"contentType": "SSML",
"content": "<speak><prosody pitch=\"${data[0]}%\" rate=\"${data[1]}%\">${data[2]}</prosody></speak>"
}
]
}
]
}
]
}
}
}
基本となる要素はこちらです。
- ${} ⇒ 文字列中に変数や数式を展開できます。
- breaktimeタグ ⇒ 指定の秒数で無音が入ります。上記では(配列の要素数によって)4つ同時に発話されるなかで0.375秒づつずらしています。
- sub aliasタグ ⇒ 読み方を指定するタグですが何も指定していないのでここでは何も発話しないです。
バグ回避おまじない。 - dataプロパティ ⇒ この中に配列を格納すると、配列の数だけ子要素を繰り返しします。子要素の中ではその配列の1要素を文字列中に${data}と記述して取得できます。「foreach」っぽい。
- bindプロパティ ⇒ 変数の定義を格納する部分。nameという名前の変数にvalueという値を入れて初期化しています。
- indexという名の変数 ⇒ 子要素がdataによって繰り返されるとき、何番目の要素かが自動で格納されている定義済み変数。
- "duration": "trimToParent" ⇒ 親に尺を合わせます(超える分は削除されます)。ここだと親の尺(一小節)で1.5秒の無音を流しているのでこれに合わせます。これが無いと一小節が間延びしてしまいます。
…ということで、一小節を1.5秒・一拍を0.375秒の4拍子で発話するループを作りました。
そして完成
なんだか3分クッキングのようになってしまいましたが、実際に鳴らすとこんな感じです。
うん、曲に聞こえるね!もうちょっと音階変化のある曲にすればよかったか…。
「あー」や「ルー」だとどうしてもダミ声になってしまうので(風邪ひいたのかな?)落ち着いた感じの「ん」にしました。
ちなみに歌詞を入れるとこんな感じになります。
うん、歌詞入れない方が良いね…!アレクサ姉さん、無理させてしまってごめん。
SSMLは会話記述言語なので、どうしても単語になってしまってイントネーションが発生してしまうのは致し方なしです。最後の「るぅ~」がちょっと可愛い気がしなくもない。
あとがき
いかがだったでしょうか。GoogleNestHubのサードパーティアクション終了やCLOVA対話機能終了など寂しいニュースが続きますが、アレクサは今後も健在でいて欲しいです。
趣味では画面付きアレクサ端末(EchoShowシリーズ)等で動作するアクションゲームなどを作ってますので興味ある方は"alexa kyukkyu"で検索!
明日のCYBIRD Advent Calendar 2022 11日目は、@chikako_ikeda さんの「Android Jetpack Composeを試してみた」です。私ネイティブはからっきしなので「Android Jetpack Composeとは」でとりあえずザッとググってみたところ、ふむふむ、AndroidでUIを実装する際にXMLで書くところをプログラムっぽく宣言で書けるよって感じでしょうか。これ、AlexaのUIを表現するAPL(Alexa Presentation Language)に近いものを感じます!(超大まかな分類)。明日の記事気になってきました!