3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-10-27

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

:sparkles::two_hearts:推しフィギュアをゲーム内BGMを奏でるオルゴールのようにしたい!:two_hearts::sparkles:

知らない方が大勢いらっしゃると思いますが、TWISTED WONDERLAND」というDisneyのほぼフルボイスのスマホアプリに登場するキャラクターです。

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

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

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

#ゲーム内BGMの音源化
ゲーム内のBGMやつながりのあるディズニー作品の曲を耳コピしています。
音を聴きながら、鍵盤を叩き、音合わせしながら紙に書き出しました。
音符の長さを確認し、その後、周波数と長さあわせてメロディーを作りました。

#試作品の内側
LEDを光らせるところは半透明の折り紙を巻いただけという簡単なものでした。
試作品なので超簡単な装飾で失礼いたします。

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

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

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

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

##全体の処理コード
処理コードがとても長いので、全体コードを折り畳みます。

クリックで開きます
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();
}

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

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

★参考:バンド・スコアの小楽典 - 2. 音譜・休符・拍子

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回繰り返す
5回繰り返し再生します。
配列へ設定した曲番号をランダムに取得して再生します。
同じ曲が連続して流れる可能性がありますが、それも運です。

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

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

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

★参考:音階の周波数

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:

obniz.display.print('Part of your world');

ディズプレイを都度クリアしないと表示に文字が追加されますので、クリアをセットにしています。
再生された順番を表示させたければ、クリアしなくても大丈夫です:ok_hand:

obniz.display.clear();

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

##例のごとく作品の内側
マイクロサーボはお菓子の紙箱の中に入れて、LEDと電圧スピーカーは折り紙で隠しています。

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

// サーボモータを利用
const servo = obniz.wired('ServoMotor', { gnd: 0, vcc: 1, signal: 2 });
// 角度を保持する変数
let degrees = 90.0;

Switch文のcase1~3の中に以下を一小節ごとに60度と90度を交互に入れました。

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/
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?