【推しを愛でるプロジェクト】ゲーム内ミュージック(+α)を耳コピしてobniz＆スピーカーで奏でてみた

ゲームセンターでゲットしたフィギュアたち。大切に！大切に！クリアケースに入れて飾っています。
そんな毎日視界の中に入る推しフィギュアたち。
常々**「何かやって愛でてあげたいな」**と、考えていました。
そんなところに**obniz**・**LED**・**スピーカー**等をゲット！
Lチカさせて音楽奏でたい！<font color="#ff0000">推しキャラのために！</font><font color="#ff0000">**推しを愛でるプロジェクト！**</font>を開催いたします！

:sparkles::two_hearts:<font color="#ff0000">**推しフィギュアをゲーム内BGMを奏でるオルゴールのようにしたい！**</font>:two_hearts::sparkles:
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122199/3e64c51f-ffa9-1f68-5ec0-90b1da039cff.jpeg" width=50%>
知らない方が大勢いらっしゃると思いますが、**「[TWISTED WONDERLAND](https://twisted-wonderland.aniplex.co.jp/)」**という**Disney**のほぼフルボイスのスマホアプリに登場するキャラクターです。


#「推しを愛でるためのオルゴール(仮)」できました
:::note warn
リズムや音程に若干のずれが生じております。ご了承ください。
:::
##1曲目　映画リトルマーメイドより「Part of your world」

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">アズール達をオルゴール化計画。1曲目は「Part of your world」<a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/obniz?src=hash&amp;ref_src=twsrc%5Etfw">#obniz</a> <a href="https://twitter.com/hashtag/Disney?src=hash&amp;ref_src=twsrc%5Etfw">#Disney</a> <a href="https://twitter.com/hashtag/%E3%83%84%E3%82%A4%E3%82%B9%E3%83%86?src=hash&amp;ref_src=twsrc%5Etfw">#ツイステ</a> <a href="https://twitter.com/hashtag/%E3%83%AA%E3%83%88%E3%83%AB%E3%83%9E%E3%83%BC%E3%83%A1%E3%82%A4%E3%83%89?src=hash&amp;ref_src=twsrc%5Etfw">#リトルマーメイド</a> <a href="https://t.co/AHyPdVl0oj">pic.twitter.com/AHyPdVl0oj</a></p>&mdash; yui-kouy (@yui8686kouy) <a href="https://twitter.com/yui8686kouy/status/1453161958744604675?ref_src=twsrc%5Etfw">October 27, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

##2曲目　ツイステRHYTHMICより「お店を切り盛りしよう！」

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">推しのオルゴール化計画は楽しい。2曲目はツイステRHYTHMICより「お店を切り盛りしよう！」<a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/obniz?src=hash&amp;ref_src=twsrc%5Etfw">#obniz</a> <a href="https://twitter.com/hashtag/Disney?src=hash&amp;ref_src=twsrc%5Etfw">#Disney</a> <a href="https://twitter.com/hashtag/%E3%83%84%E3%82%A4%E3%82%B9%E3%83%86?src=hash&amp;ref_src=twsrc%5Etfw">#ツイステ</a> <a href="https://twitter.com/hashtag/Rhythmic?src=hash&amp;ref_src=twsrc%5Etfw">#Rhythmic</a> <a href="https://t.co/o5DUsk0TOS">pic.twitter.com/o5DUsk0TOS</a></p>&mdash; yui-kouy (@yui8686kouy) <a href="https://twitter.com/yui8686kouy/status/1453163530492866565?ref_src=twsrc%5Etfw">October 27, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

##3曲目　ツイステEVENT RHYTHMIC-スケアリーモンスターズ-より「ハロウィーンを楽しもう！」

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">シーズン的にはピッタリ！？早くも最後の曲となりました。3曲目はツイステEVENT RHYTHMIC-スケアリーモンスターズ-より「ハロウィーンを楽しもう！」<a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/obniz?src=hash&amp;ref_src=twsrc%5Etfw">#obniz</a> <a href="https://twitter.com/hashtag/Disney?src=hash&amp;ref_src=twsrc%5Etfw">#Disney</a> <a href="https://twitter.com/hashtag/%E3%83%84%E3%82%A4%E3%82%B9%E3%83%86?src=hash&amp;ref_src=twsrc%5Etfw">#ツイステ</a> <a href="https://twitter.com/hashtag/%E3%82%B9%E3%82%B1%E3%82%A2%E3%83%AA%E3%83%BC%E3%83%A2%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%BC%E3%82%BA?src=hash&amp;ref_src=twsrc%5Etfw">#スケアリーモンスターズ</a> <a href="https://twitter.com/hashtag/%E3%83%8F%E3%83%AD%E3%82%A6%E3%82%A3%E3%83%B3?src=hash&amp;ref_src=twsrc%5Etfw">#ハロウィン</a> <a href="https://t.co/5IHukWzAtT">pic.twitter.com/5IHukWzAtT</a></p>&mdash; yui-kouy (@yui8686kouy) <a href="https://twitter.com/yui8686kouy/status/1453164575482793999?ref_src=twsrc%5Etfw">October 27, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#ゲーム内BGMの音源化
ゲーム内のBGMやつながりのあるディズニー作品の曲を耳コピしています。
音を聴きながら、鍵盤を叩き、音合わせしながら紙に書き出しました。
音符の長さを確認し、その後、周波数と長さあわせてメロディーを作りました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122199/ecfb9cf3-8a43-4a01-5c95-3615cf7064db.jpeg" width=50%>

#試作品の内側
LEDを光らせるところは半透明の折り紙を巻いただけという簡単なものでした。
試作品なので超簡単な装飾で失礼いたします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122199/7eb2973d-a14b-a558-47de-138d2f6ac585.jpeg" width=50%>

#環境、言語等
・Visual Studio Code
・JavaScript
・Node.js

#使用機器、部品等
・obniz 1Y
・圧電スピーカー
・LED(青・黄緑)　等

#処理コード
処理概要、全体の処理コード、各処理コードについて記載します。

##処理概要
・音符・休符の長さを定数化
・3曲のランダム再生(5回繰り返し)
・3曲分の音の周波数、音を出す長さ、止める長さの指定(メロディー部分)
・obnizへ曲名の表示

##全体の処理コード
処理コードがとても長いので、全体コードを折り畳みます。
<details><summary>クリックで開きます</summary><div>

```JavaScript
const Obniz = require('obniz');
const { exit } = require('process');
const obniz = new Obniz('XXXX-XXXX'); // Obniz_IDに自分のIDを入れます
const sp1 = 1600                    //全音符・全休符
const sp2 = sp1 / 2                 //2分音符・2分休符
const sp4 = sp1 / 4                 //4分音符・4分休符
const sp8 = sp1 / 8                 //8分音符・8分休符
const sp16 = sp1 / 16               //16分音符・16分休符
const sp2_34 = sp2 + sp4            //付点2分音符・付点2分休符  
const sp4_34 = sp4 + sp8            //付点4分音符・付点4分休符
const sp8_34 = sp8 + sp16           //付点8分音符・付点8分休符
const sp16_34 = sp16 + (sp16 / 2)   //付点16分音符・付点16分休符

// obnizが接続済み
obniz.onconnect = async function () {
    // ディスプレイの表示をクリア
    obniz.display.clear();
    // 任意の秒数待つことができる関数
    // 参考: https://qiita.com/suin/items/99aa8641d06b5f819656
    const sleep = (msec) => new Promise(res => setTimeout(res, msec));
    // 電圧スピーカーを利用
    const speaker = obniz.wired('Speaker', { signal: 1, gnd: 2 });
    // LEDを利用
    const led_b = obniz.wired('LED', { anode: 7, cathode: 8 });
    const led_g = obniz.wired('LED', { anode: 10, cathode: 11 });
    // 曲番号を配列に格納
    const songNo = [1,2,3];
    // 5回くり返す
    for (a = 1 ; a <= 5 ; a+=1 ) { 
        // songNo配列からランダムに配列番号を返し、その番号の曲を再生する
        let songFlg = (songNo[Math.floor(Math.random() * songNo.length)]); 

        switch (songFlg) {
            case 1:             // Part of your world
                for (b = 1 ; b <= 1 ; b+=1 ) {
                    obniz.display.print('Part of your world');
                    led_b.blink(sp4);
                    led_g.blink(sp4_34);
                    speaker.play(587.330); await sleep(sp8); speaker.stop(); await sleep(0);         // レ
                    speaker.play(659.255); await sleep(sp8); speaker.stop(); await sleep(0);         // ミ
                    speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(698.456); await sleep(sp4_34); speaker.stop(); await sleep(sp4);    // ファ
                    speaker.play(659.255); await sleep(sp8); speaker.stop(); await sleep(0);         // ミ
                    speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(783.991); await sleep(sp8); speaker.stop(); await sleep(0);         // ソ
                    speaker.play(783.991); await sleep(sp4_34); speaker.stop(); await sleep(sp4);    // ソ
                    speaker.play(587.330); await sleep(sp8); speaker.stop(); await sleep(0);         // レ
                    speaker.play(659.255); await sleep(sp8); speaker.stop(); await sleep(0);         // ミ
                    speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(698.456); await sleep(sp4); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(659.255); await sleep(sp4); speaker.stop(); await sleep(0);         // ミ
                    speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(659.255); await sleep(sp8); speaker.stop(); await sleep(0);         // ミ
                    speaker.play(698.456); await sleep(sp4); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(783.991); await sleep(sp8); speaker.stop(); await sleep(0);         // ソ
                    speaker.play(783.991); await sleep(sp4); speaker.stop(); await sleep(sp4);       // ソ
                    speaker.play(659.255); await sleep(sp8); speaker.stop(); await sleep(0);         // ミ
                    speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(783.991); await sleep(sp8); speaker.stop(); await sleep(0);         // ソ
                    speaker.play(783.991); await sleep(sp4); speaker.stop(); await sleep(0);         // ソ
                    speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(783.991); await sleep(sp8); speaker.stop(); await sleep(0);         // ソ
                    speaker.play(698.456); await sleep(sp4_34); speaker.stop(); await sleep(sp8);    // ファ
                    speaker.play(587.330); await sleep(sp8); speaker.stop(); await sleep(0);         // レ
                    speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(783.991); await sleep(sp8); speaker.stop(); await sleep(0);         // ソ
                    speaker.play(880.000); await sleep(sp4); speaker.stop(); await sleep(0);         // ラ
                    speaker.play(880.000); await sleep(sp4); speaker.stop(); await sleep(0);         // ラ
                    speaker.play(783.991); await sleep(sp8); speaker.stop(); await sleep(0);         // ソ
                    speaker.play(783.991); await sleep(sp8+sp2); speaker.stop(); await sleep(sp1);   // ソ 
                    obniz.display.clear();
                }
                break;
            case 2:
                // TWISTED-WONDERLAND SCREAMING HALLOWEEN by Disney
                for (c = 1 ; c <= 2 ; c+=1 ) {
                    obniz.display.print('TWISTED-WONDERLAND SCREAMING HALLOWEEN by Disney');
                    led_b.blink(sp8);
                    led_g.blink(sp8_34);
                    speaker.play(880.000); await sleep(sp8); speaker.stop(); await sleep(sp8);       // ラ
                    speaker.play(587.330); await sleep(sp8); speaker.stop(); await sleep(0);         // レ
                    speaker.play(659.255); await sleep(sp8); speaker.stop(); await sleep(0);         // ミ
                    speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(587.330); await sleep(sp8); speaker.stop(); await sleep(sp8);       // レ
                    speaker.play(880.000); await sleep(sp8); speaker.stop(); await sleep(sp8);       // ラ
                    speaker.play(932.328); await sleep(sp8); speaker.stop(); await sleep(sp8);       // シ♭
                    speaker.play(880.000); await sleep(sp8); speaker.stop(); await sleep(sp8);       // ラ
                    speaker.play(783.991); await sleep(sp4); speaker.stop(); await sleep(0);         // ソ
                    speaker.play(830.609); await sleep(sp8); speaker.stop(); await sleep(0);         // ソ#
                    speaker.play(880.000); await sleep(sp8); speaker.stop(); await sleep(sp8);       // ラ
                    speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(587.330); await sleep(sp8); speaker.stop(); await sleep(0);         // レ
                    speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(587.330); await sleep(sp8); speaker.stop(); await sleep(sp8);       // レ
                    speaker.play(698.456); await sleep(sp4); speaker.stop(); await sleep(0);         // ファ
                    speaker.play(587.330); await sleep(sp8); speaker.stop(); await sleep(0);         // レ
                    speaker.play(830.609); await sleep(sp8); speaker.stop(); await sleep(0);         // ソ#
                    speaker.play(880.000); await sleep(sp8); speaker.stop(); await sleep(0);         // ラ
                    speaker.play(1046.502); await sleep(sp16); speaker.stop(); await sleep(sp16);    // ラ
                    speaker.play(987.767); await sleep(sp4); speaker.stop(); await sleep(sp8);       // シ
                    obniz.display.clear();
                }
                break;
            case 3:
                // TWISTED-WONDERLAND Mostro Lounge by Disney
                for (d = 1 ; d <= 2 ; d+=1 ) {
                    obniz.display.print('TWISTED-WONDERLAND OCTAVINELLE by Disney');
                    led_b.blink(sp4);
                    led_g.blink(sp8);
                    speaker.play(987.767); await sleep(sp8_34); speaker.stop(); await sleep(0);      // シ
                    speaker.play(932.328); await sleep(sp16); speaker.stop(); await sleep(0);        // シ♭
                    speaker.play(880.000); await sleep(sp8_34); speaker.stop(); await sleep(0);      // ラ
                    speaker.play(739.989); await sleep(sp16); speaker.stop(); await sleep(0);        // ファ
                    speaker.play(783.991); await sleep(sp8_34); speaker.stop(); await sleep(0);      // ソ
                    speaker.play(659.255); await sleep(sp16+sp4); speaker.stop(); await sleep(0);    // ミ
                    speaker.play(880.000); await sleep(sp4); speaker.stop(); await sleep(sp8_34);    // ラ
                    speaker.play(932.328); await sleep(sp16+sp4); speaker.stop(); await sleep(sp4);  // シ　
                    speaker.play(698.456); await sleep(sp4); speaker.stop(); await sleep(sp8_34);    // ファ
                    speaker.play(739.989); await sleep(sp16+sp4); speaker.stop(); await sleep(0);    // ファ#
                    speaker.play(587.330); await sleep(sp4); speaker.stop(); await sleep(0);         // レ
                    speaker.play(659.255); await sleep(sp4); speaker.stop(); await sleep(0);         // ミ
                    speaker.play(739.989); await sleep(sp4); speaker.stop(); await sleep(0);         // ファ#
                    speaker.play(783.991); await sleep(sp16); speaker.stop(); await sleep(sp16_34);  // ソ
                    speaker.play(880.000); await sleep(sp16); speaker.stop(); await sleep(sp16);     // ラ
                    speaker.play(1046.502); await sleep(sp8_34); speaker.stop(); await sleep(sp8);      // ド
                    obniz.display.clear();
                }
                break;

        }
    }
    exit();
}
```

</div></details>


##定数宣言　音符と休符の長さ

全音符・全休符を基準に計算して格納することにしました。
実際に動かして、早さを確認しながら微調整が簡単にできました。

★参考：[バンド・スコアの小楽典 - 2. 音譜・休符・拍子](https://www.doremi.co.jp/gakufu_no_mikata/gakufu/basic2.html)

```JavaScript
const sp1 = 1600                    //全音符・全休符
const sp2 = sp1 / 2                 //2分音符・2分休符
const sp4 = sp1 / 4                 //4分音符・4分休符
const sp8 = sp1 / 8                 //8分音符・8分休符
const sp16 = sp1 / 16               //16分音符・16分休符
const sp2_34 = sp2 + sp4            //付点2分音符・付点2分休符  
const sp4_34 = sp4 + sp8            //付点4分音符・付点4分休符
const sp8_34 = sp8 + sp16           //付点8分音符・付点8分休符
const sp16_34 = sp16 + (sp16 / 2)   //付点16分音符・付点16分休符
```

##繰り返し処理　５回繰り返す
5回繰り返し再生します。
配列へ設定した曲番号をランダムに取得して再生します。
<font color="#ff0000">同じ曲が連続して流れる可能性がありますが、**それも運です。**</font>

```JavaScript
// 曲番号を配列に格納
const songNo = [1,2,3];

// 5回くり返す
for (a = 1 ; a <= 5 ; a+=1 ) { 
    // songNo配列からランダムに配列番号を返し、その番号の曲を再生する
    let songFlg = (songNo[Math.floor(Math.random() * songNo.length)]); 
```

##スピーカー出力、Lチカ　メロディの設定
音を調べて周波数を当てはめます。
以下は全体コードから一分抜粋

★参考：[音階の周波数](https://tomari.org/main/java/oto.html)

```JavaScript
switch (songFlg) {
    case 1:             // Part of your world
        for (b = 1 ; b <= 1 ; b+=1 ) {
            obniz.display.print('Part of your world');
            led_b.blink(sp4);
            led_g.blink(sp4_34);
            speaker.play(587.330); await sleep(sp8); speaker.stop(); await sleep(0);         // レ
            speaker.play(659.255); await sleep(sp8); speaker.stop(); await sleep(0);         // ミ
            speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
            speaker.play(698.456); await sleep(sp4_34); speaker.stop(); await sleep(sp4);    // ファ
            speaker.play(659.255); await sleep(sp8); speaker.stop(); await sleep(0);         // ミ
            speaker.play(698.456); await sleep(sp8); speaker.stop(); await sleep(0);         // ファ
            speaker.play(783.991); await sleep(sp8); speaker.stop(); await sleep(0);         // ソ
            speaker.play(783.991); await sleep(sp4_34); speaker.stop(); await sleep(sp4);    // ソ
            //xxx途中省略xxx
            obniz.display.clear();
        }
        break;

```

##obnizの画面表示
現在の曲名を表示します。
各caseごとに曲名を指定しています。

処理の最初でディズプレイをクリアしておかないと、QRコードとObniz_IDの上に曲名が書き込まれていきます。
バグかと思い、焦って再起動して何度やっても、QRコードとObniz_IDの上に書き込まれていく・・・
「あ、これは自分の記述がおかしいんだな。」と気付けて修正しましたが、焦りました:sweat_smile:

```JavaScript
obniz.display.print('Part of your world');
```
ディズプレイを都度クリアしないと表示に文字が追加されますので、クリアをセットにしています。
再生された順番を表示させたければ、クリアしなくても大丈夫です:ok_hand:

```JavaScript
obniz.display.clear();
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122199/8d6746e3-4133-efbe-da5f-4d0da9523920.jpeg" width=50%>

#追記　マイクロサーボも実装
フィギュアがめっちゃ揺れる・・・落ちないか恐怖だったので一発収録しました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">サーボちゃん動いたのでフィギュアの台座ごと動ごくように設置。<br>動かす記述として書いたはずのところがモニタークリア記述を書いていた。動かないの当たり前よ……｡ﾟ(ﾟ´Д｀ﾟ)ﾟ｡<a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/obniz?src=hash&amp;ref_src=twsrc%5Etfw">#obniz</a> <a href="https://twitter.com/hashtag/%E3%83%84%E3%82%A4%E3%82%B9%E3%83%86?src=hash&amp;ref_src=twsrc%5Etfw">#ツイステ</a> <a href="https://t.co/VywnqvE7WU">pic.twitter.com/VywnqvE7WU</a></p>&mdash; yui-ko (@yui8686kouy) <a href="https://twitter.com/yui8686kouy/status/1453702502356946956?ref_src=twsrc%5Etfw">October 28, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

##例のごとく作品の内側
マイクロサーボはお菓子の紙箱の中に入れて、LEDと電圧スピーカーは折り紙で隠しています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122199/6a64fc0a-2e48-1e59-fd82-cd4b3649c373.jpeg" width=50%>

##追加処理コード
上の処理コードの `obniz.onconnect = async function () {` の中に以下を追加しました。

```JavaScript
// サーボモータを利用
const servo = obniz.wired('ServoMotor', { gnd: 0, vcc: 1, signal: 2 });
// 角度を保持する変数
let degrees = 90.0;
```
Switch文のcase1～3の中に以下を一小節ごとに60度と90度を交互に入れました。

```JavaScript
degrees = **.*;    //ここを60.0と90.0を交互に入れています
servo.angle(degrees);
```



#今後チャレンジしたいこと
obnizのスイッチもしくはLINE Botと組み合わせ、どの曲を再生させるか選べるようにしたい。
フルカラーLEDを使用して、音階によって色が変わっていくようにしたい。

#感想
推しへの愛が詰まっていたと思います:heart::heart::heart:(気持ち悪いですね。)
オルゴール化したいので、箱等の見た目もきれいにして常に使えるようにしたいです。
ピアノをやっていたことがこんなところで役に立つとは思いませんでした。

#記事の書き方で参考にしたURL
| 記事名(サイト表題) | URL |
|:-|:-|
| Qiitaの投稿で装飾したいときによく使うHTMLタグ |https://qiita.com/7note/items/9c0763d5491e43edb552 |
| 絵文字チートシート | https://www.webfx.com/tools/emoji-cheat-sheet/ |
| WEB色見本 原色大辞典 - HTMLカラーコード | https://www.colordic.org/ |