クリスマスの夜に向けて密かに歌を練習するアレクサ　～そしてデュエットへ～

# こんにちはこんばんは

きゅっきゅ（しのやん）と申します。
スマートスピーカー界隈、雲行きが怪しいですね！寂しい限りです。GoogleNestHub用アクションもCLOVA用エクステンションも精魂込めて作ったサービスがこんな形でクローズしていってしまうのは何とも残念でなりませんが、そんななか（不採算と目を付けられつつも）サービス継続しているamazonのAlexaは唯一の希望なのであります。たとえ最後の一人になってもAlexaでいい感じの楽しいゲームを公開していくことを目標に、日々精進を続けているのであります。

ところで、コタツが恋しい季節になりませんか？なりませんか！そうですか！（昔の何かをリマインド）

# ということで

そこはかとなくケイオスな感じにスタートしましたが、本記事はスマートスピーカー [Voice User Interface Advent Calendar 2022](https://qiita.com/advent-calendar/2022/vui)の10日目の記事です。

前述のとおり、私のトガり分野はAlexaの画面制御なのですが、今回は画面ではなくオーディオ制御でトガってみたいと思います。alexaで音声を出す方法はいろいろありますが、今回はAPLA(Alexa Presentation Language for Audio)を使います。これは「何をどのタイミングで鳴らすか」を割と簡潔に記述できるものですが、どんな音声を出すか自体はSSML(Speech Synthesis Markup Language)言語を使います。マークアップ言語なので、すごーく雑に言ってしまえばHTMLタグの音声版みたいなものです。

今回そのAPLAとSSMLを使って、クリスマスソングをアレクサに歌って頂こうと思います。

実はこの試みは私が勤める某イケメン会社（語弊）の[アドベントカレンダー](https://qiita.com/advent-calendar/2022/cybird)に参加した記事の続編的位置づけでして（本音を言うと別のネタを用意できなかった）、以下のようなポリシーで進めようと思います。

+ あたかもリズムが取れているような演出を目指す
+ 音程を表現できないことをあきらめない
+ mp3等音源や録音加工などはせず純粋にアレクサ姉さんの声(SSML)で挑戦
+ SSMLタグの限界に挑戦
+ AlexaのAPLA言語のほうも工夫してみる
+ ``デュエットに挑戦``(new!)

そして、今回実際に歌って頂くのはこちらです！

「We Wish You a Merry Christmas」

歌って頂くと言っても、前編の知見から「歌詞歌わせるとうまくいかない」ことがわかっているので、「あー」のみで行きます。
あと、「んー」「るー」なども入れようかと思いましたが言葉が変わると音階が変わるため避けます。
さらに「ずんちゃ」「じゃーん」「いぇーい」など入れたい衝動に駆られましたが、時間が溶けそうなので割愛しておきたいと思います。
（冬のコミケに出すAPLマニアックス5の原稿もまだ終わっていないので…あぁ年末は毎年物書きモードだなぁ…）

# できあがりはこちら

0分クッキングで突然の出来上がりとなりますが、まず完成品はこちらとなります。

https://drive.google.com/file/d/1G74i4bEyImoNGLWGYVAeswa_FUP2owRo/view

声が枯れているのは風邪をひいているのでしょう…あなたの家のEchoShowもいたわってあげてください。
続いてソースはこちら。長いので折りたたんでいます。

<details><summary>■■■クリックしてJSON山脈コードを表示■■■</summary>

```json
{
    "type": "APLA",
    "version": "0.91",
    "compositions": {},
    "mainTemplate": {
        "parameters": [
            "payload"
        ],
        "item": {
            "type": "Sequencer",
            "_description": "レ# ミ   fa   fa#  ソ   ソ# ラ ラ# シ    ド  ド#   レ  レ#",
            "description": "-30, -25, -20, -15, -10, -5, 0, 5,  12.5, 20, 27.5, 35, 42.5",
            "data": [
                [
                    [ 0, 100, ""  ,-0, 100, ""],
                    [ 0, 100, "" ],
                    [ 0, 100, "" ],
                    [ 0, 100, "" ],
                    [ -30, 100, "あー" ],
                    [ 0, 100, "" ]
                ],
                [
                    [ -5, 100, "あー" ,-30, 40, "あー"],
                    [ 0, 100, "" ],
                    [ -5, 100, "あー" ],
                    [ 5, 100, "あー" ], 
                    [ -5, 100, "あー" ],
                    [ -10, 100, "あー" ]
                ],
                [ 
                    [ -20, 100, "あー" ,-5, 40, "あー"],
                    [ 0, 100, "" ],
                    [ -20, 100, "あー" ],
                    [ 0, 100, "" ],
                    [ -20, 100, "あー" ],
                    [ 0, 100, "" ]
                ],
                [
                    [ 5, 100, "あー" ,27.5, 40, "あー"],
                    [ 0, 100, "" ],
                    [ 5, 100, "あー" ],
                    [ 20, 100, "あー" ],
                    [ 5, 100, "あー" ],
                    [ -5, 100, "あー" ]
                ],
                [
                    [ -10, 100, "あー" ,5, 40, "あー"],
                    [ 0, 100, "" ],
                    [ -30, 100, "あー" ],
                    [ 0, 100, "" ],
                    [ -30, 100, "あー" ],
                    [ 0, 100, "" ]
                ],
                [
                    [ 20, 100, "あー"  ,42.5, 40, "あー"],
                    [ 0, 100, "" ],
                    [ 20, 100, "あー" ],
                    [ 27.5, 100, "あー" ],
                    [ 20, 100, "あー" ],
                    [ 5, 100, "あー" ]
                ],
                [
                    [ -5, 100, "あー"  ,20, 40, "あー"],
                    [ 0, 100, "" ],
                    [ -20, 100, "あー" ],
                    [ 0, 100, "" ],
                    [ -30, 100, "あー" ,-5, 40, "あー"],
                    [ 0, 100, "" ]
                ],
                [
                    [ -20, 100, "あー"   ,-20, 40, "あー"],
                    [ 0, 100, "" ],
                    [ 5, 100, "あー" ],
                    [ 0, 100, "" ],
                    [ -10, 100, "あー" ,-10, 40, "あー"],
                    [ 0, 100, "" ]
                ],
                [
                    [ -5, 100, "あー"   ,-5, 40, "あー"],
                    [ 0, 100, "" ],
                    [ 0, 100, "" ],
                    [ 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=\"\">a</sub></speak>"
                        },
                        {
                            "type": "Sequencer",
                            "duration": "trimToParent",
                            "bind": [
                                {
                                    "name": "step",
                                    "value": "${index}"
                                }
                            ],
                            "items": [
                                {
                                    "type": "Speech",
                                    "contentType": "SSML",
                                    "content": "<speak><break time=\"${step * 0.25}s\"/><sub alias=\"\">a</sub></speak>"
                                },
                                {
                                    "type": "Speech",
                                    "contentType": "SSML",
                                    "content": "<speak><prosody pitch=\"${data[0]}%\" rate=\"${data[1]}%\">${data[2]}</prosody></speak>"
                                }
                            ]
                        },
                        {
                            "type": "Sequencer",
                            "when":"${data.length >= 4}",
                            "duration": "trimToParent",
                            "bind": [
                                {
                                    "name": "step",
                                    "value": "${index}"
                                }
                            ],
                            "items": [
                                {
                                    "type": "Speech",
                                    "contentType": "SSML",
                                    "content": "<speak><break time=\"${step * 0.25}s\"/><sub alias=\"\">a</sub></speak>"
                                },
                                {
                                    "type": "Speech",
                                    "contentType": "SSML",
                                    "content": "<speak><prosody volume=\"-8dB\" pitch=\"${data[3]}%\" rate=\"${data[4]}%\">${data[5]}</prosody></speak>"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    }
}
```

</details>

ポイントは以下となります。
+ 音階の表現
prosodyタグのpitchプロパティで音声の高低がつけられるので、これを活用して半音づつ上がる位置を耳で決めています(descriptionタグにコメントを入れています）。割とキリの良い数字になっているあたり、周波数の関係などで何らかの法則があるのでしょうか。
あと0を境に上がり幅が変わっているように感じました（あくまで私の感覚値です）。
スケールがE♭なのは、pitchの指定可能範囲が-33.3～+50という仕様で１オクターブ確保できるのがぎりぎりこの部分しかなかったためです。
ちなみにしゃべる言葉を変えると容易に音階が変化してしまうため…、すべて同じ発話に揃えてあります。

+ 配列展開
dataプロパティを設定すると、その配列の数だけループし${data}にその要素の値が格納されます。なんというかforeachみたいな挙動です。これをうまく使って１小節と１拍を表現しています。

+ breakタグ+subタグ
無音時間が指定できるこのタグで各拍の再生タイミングを表現できるのですが、何故かその後に何か発話させないと無音時間が生成されないため、ダミーの文字をsubタグで挟んで「それは何も発音しない」と指定することで実現しています。

+ テンポ遅延防止
"duration": "trimToParent"で「親の尺に合わせる」ことでリズムの遅延を防止しています。

# あとがき

いかがでしたでしょうか。
今どきの音声合成やボカロを聞きなれた方は恐らく「なーんだこんなもんか」って思うかもしれませんが、もともと歌う仕様になっていないシステムにここまでさせるというある種の「Genkai-Toppa遊び」は実際やってみると結構ハマってしまいます。

ではでは、そんな感じで。メリークリスマス！

# おまけ：Alexa対応機種持っているあなたに

いくつかAlexa用ゲームスキル作って公開してます。が、Alexaで出来る事の可能性を引き出すことだげ頑張って作ったため、面白さは正直微妙…？面白さの創出って難しいですね…誰か教えてください…

https://www.amazon.co.jp/dp/B09DFBWFNV

https://www.amazon.co.jp/dp/B086V9NDH1

https://www.amazon.co.jp/dp/B07LBP689L

https://www.amazon.co.jp/dp/B07GT2CWDG

Alexaのスキル開発してるよ！という方は是非Twitterあたりでお友達になりましょう！
「AlexaAPLマニアックス」という本をBOOTHで頒布してますが、自分で言うのもなんですがあちらは正直初心者向けではないのでご注意ください。（冬コミで５冊目出します）

そして実はいま懲りずに次のゲームを開発中です。来年の２月くらいには公開できたらいいな…
チクタクバ○バ○ ＋ RPGという謎のゲームです。
こちらもEchoShow/FireHDタブレットでないと遊べないです。ご了承ください。

![echorogue.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309295/0fff15bf-3e27-a742-215f-832803636f91.png)