チュールのうたを圧電スピーカーで音階設定して、動画BGMを作成した話。

こんにちは～

私には飼っている猫（こっちゃん♀）をいなばチュールのCMに出演させたい！という
夢があるのですがなかなか叶えることができず・・・
こっちゃんのために自分でＢＧＭを作り、動画までとってみよう！と
実践したことをまとめました！

**今回はYoutubeのちゅーるのうたを流すだけでは面白くないので
圧電スピーカーで音階設定して、LINEリッチメニューで使用しやすいように作成しました。**


ちなみにチュールはいなば食品グループのいなばペットフードから発売されており、
猫達が食べやすいよう水分を90%含んだペースト状のフードらしいのですが、
我が家ではほぼ猫のおやつです。
そして今年はチュール販売開始から10年という記念すべき年でもあります！


## 完成した動画
https://twitter.com/nghrhtm1/status/1551163597216448514

私の多少の音痴がチュールのうたにも出ているし、
obnizが映ってないから2回撮影しましたが、
こっちゃんは喜んで対応してくれました笑


## 使用したもの
*  [obniz](https://akizukidenshi.com/catalog/g/gM-14930/)
*  [圧電スピーカー](https://akizukidenshi.com/catalog/g/gP-04118/)
* Node.js
* LINE Bot

## 工夫した点

**1つ目 チュールのうたを繰り返し聞けるように設定！**
音階の周波数を設定するのはコピペで出来るのですが、リズムをとるのが難しく、
サビの部分の設定で心が折れました・・・

でもサビだけではチュールを食べきれないので
繰り返し音楽が流れるようにコードを変更しました！


**2つ目 リッチメニューで音の再生・停止も簡単にする**
元々はLINEBotにコメントを送ったら、再生・停止ができる設定していましたが
チュール食べさす時って手が汚れる＆チュール大好きこっちゃんは待ってくれない！
リッチメニューでサクッと対応できるように設定しました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/6950d3b5-31dd-f8a7-9698-8658c967679c.png" width="300" height="400">


## こっちゃんファン（友人）からのFB
「**こっちゃんへの愛が溢れている！！**」と言われました笑

「**でも最近の猫はチュール食べるときに立つんやで！**」って言われて
こっちゃん、本家のCM出演無理やん！ってなり、飼い主撃沈。


https://youtu.be/dzmuiOgMcgw

「**この機械のことはあまり分らんし、めちゃ凄いと思うねんけど、
あとは写真とか動画も一緒に撮るとかできたらえんちゃう？
奇跡的に立つ瞬間とか撮影できるかもやで！！**」

確かに！立ってるこっちゃんも見たい！

こっちゃんはというとチュール後すぐ寝てました(笑)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/4c966b88-0a64-d3fe-3c6f-a495e067f777.png" width="400" height="400">



こっちゃんマイペースに頑張ろうね～

## 作ってみた感想

やっぱりこっちゃんは可愛い！！
チュールを必死でなめている音がたまらなく可愛い！！！！
音階設定に不安があったのですが、メロディを奏でることができると嬉しい！！

こっちゃんもチュール食べれるからノリノリで、
**ものづくりしながら楽しめました！**


あと、今まで誰かのマネして自分の作品作るだけでも3日はかかってたのに
今回の実装時間は6時間！
え。びっくり。こんなに早くできた！
**少しずつ成長していることも実感できて嬉しかったです。**



最後に動画のフルバージョン載せておくので猫に癒されたい方、ぜひみてください。

https://youtu.be/Ro8qLuZpGes





---------------------------------------
##### 実際に作ってみたい方は以下のリンクを参考にしてください！

 [▶obnizでわが子と一緒に「オリジナル目覚ましBot」を作って、実際に起きられるかどうかを試した結果](https://qiita.com/kokano23/items/a9cadbb7547203c55fc3
)
 [▶音階の周波数の一覧](https://www.tomari.org/main/java/oto.html
)

<details><summary>実際に作成したコードはこちら</summary>

```js

'use strict';

// obniz呼び出し
const Obniz = require('obniz');
const obniz = new Obniz("********");  // Obniz_ID に自分のIDを入れます

const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 4000;
let is_play = true;// 音楽を流し続けるかの判定に使用する。

// Messaging APIで利用するクレデンシャル（秘匿情報）です。
const config = {
  channelSecret: '*******************',
  channelAccessToken: '*******************'
};

const app = express();

// HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をします
app.post('/webhook', line.middleware(config), (req, res) => {

    // 検証ボタンをクリックしたときに飛んできたWebhookを受信したときのみ以下のif文内を実行
    if (req.body.events.length === 0) {
      res.send('Hello LINE BOT! (HTTP POST)'); // LINEサーバーに返答します（なくてもよい）
      console.log('検証イベントを受信しました！'); // ターミナルに表示します
      return; // これより下は実行されません
    } else {
      // 通常のメッセージなど … Webhookの中身を確認用にターミナルに表示します
      console.log('受信しました:', req.body.events);
    }
  
    // あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、
    // 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返します
    Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result));
  });
  
  const client = new line.Client(config);
  
  // obniz接続
  obniz.onconnect = async function () {
    obniz.display.clear();
    obniz.display.print("obniz meets LINE Bot!");
  }
  
  function handleEvent(event) {
    if (event.type !== 'message' || event.message.type !== 'text') {
      return Promise.resolve(null);
    }
  
    let mes = ''
    if(event.message.text === '音楽かけて'){
      mes = 'すこしまってね';  //メッセージだけ先に処理
      getAskObnizSpeaker(event.source.userId);  //スクレイピング処理が終わったらプッシュメッセージ
    }else if(event.message.text === '音楽とめて') {
      mes = 'オッケー';  //メッセージだけ先に処理
      is_play = false; // 音楽が止まる
    }else{
      mes = event.message.text;
    }
  
    return client.replyMessage(event.replyToken, {
      type: 'text',
      text: mes
    });
  }
  
  const getAskObnizSpeaker = async (userId) => {
   // スピーカーを呼び出す
   const speaker = obniz.wired("Speaker", {signal:0, gnd:1});
   console.log("Speaker")
   await client.pushMessage(userId, {
    type: 'text',
    text: "ちゅーるちゅーるチャオちゅーる♪",
  });
  is_play = true;// 音楽停止後に再度再生する

   // 音階とhzの参考サイト https://tomari.org/main/java/oto.html
   // 「ちゅーるのうた」
   while (is_play) { // 停止が押されるまで繰り返す
    await speaker.play(783.991); // G5 ソ
    await obniz.wait(800);
    await speaker.stop();
    await obniz.wait(60);
    await speaker.play(659.255); // E5 ミ
    await obniz.wait(150);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(783.991); // G5 ソ
    await obniz.wait(800);
    await speaker.stop();
    await obniz.wait(60);
    await speaker.play(659.255); // E5 ミ
    await obniz.wait(150);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(622.254); // D#5 レ♭
    await obniz.wait(150);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(622.254); // D#5 レ♭
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(880.000); // A5 ラ
    await obniz.wait(400);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(783.991); // G5 ソ
    await obniz.wait(1000);
    await speaker.stop();
    await obniz.wait(600);
    
    
    await speaker.play(783.991); // G5 ソ
    await obniz.wait(800);
    await speaker.stop();
    await obniz.wait(60);
    await speaker.play(659.255); // E5 ミ
    await obniz.wait(150);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(783.991); // G5 ソ
    await obniz.wait(800);
    await speaker.stop();
    await obniz.wait(60);
    await speaker.play(659.255); // E5 ミ
    await obniz.wait(150);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(880.000); // A5 ラ
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(880.000); // A5 ラ
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(880.000); // A5 ラ
    await obniz.wait(400);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(659.255); // E5 ミ
    await obniz.wait(1000);
    await speaker.stop();
    await obniz.wait(600);
    
    await speaker.play(1046.502); // C6 ド
    await obniz.wait(800);
    await speaker.stop();
    await obniz.wait(60);
    await speaker.play(880.000); // A5 ラ
    await obniz.wait(150);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(987.767); // B5 シ
    await obniz.wait(800);
    await speaker.stop();
    await obniz.wait(60);
    await speaker.play(880.000); // A5 ラ
    await obniz.wait(150);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(1046.502); // C6 ド
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(1046.502); // C6 ド
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(1046.502); // C6 ド
    await obniz.wait(400);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(987.767); // B5 シ
    await obniz.wait(1000);
    await speaker.stop();
    await obniz.wait(600);
    
    
    await speaker.play(698.456); // F5 ファ
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(659.255); // E5 ミ
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(698.456); // F5 ファ
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(783.991); // G5 ソ
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(783.991); // G5 ソ
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(987.767); // B5 シ
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(783.991); // G5 ソ
    await obniz.wait(200);
    await speaker.stop();
    await obniz.wait(80);
    await speaker.play(1046.502); // C6 ド
    await obniz.wait(1200);
    await speaker.stop();
    await obniz.wait(1200);
    }
   speaker.stop();
  }
  
  app.listen(PORT);
  console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);

```
</details>


<details><summary>リッチメニューの作成の方法はこちら</summary>



**1．まずは反映させたいリッチメニューの画像を作成してください。**
私は [Canva](https://www.canva.com/)を使って2分割のリッチメニューの画像を作成しました。

 [実際に作ったリッチメニュー画像はこちら](https://www.canva.com/design/DAFHOhUFj94/KD3RYRxZsoFcadsxiGKnNg/edit?utm_content=DAFHOhUFj94&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton)
リッチメニューの画像を飼っているペットに変更し、
共有→ダウンロード→PNGを選択し、ダウンロードして下さい。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/dbffc1cf-6209-cefd-0079-b29e48c9d1bc.png" width="400" height="400">

**2．次にLINE Developersのページを開き、チャネル基本設定の
[LINE Official Account Manager](https://www.linebiz.com/jp/login/)をクリックします。**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/6a03796e-51c7-6403-7d57-daca677104fc.png" width="400" height="400">

ページを開いたらホームを選択し、リッチメニューを選択してください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/483cf4f0-835a-5a5a-e094-000d238e74f2.png" width="400" height="400">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/db696b80-38ab-c61b-2942-1c5df9bcd66e.png" width="400" height="400">

**3．リッチメニューの表示設定を行います。**
タイトル・表示期間を入力してください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/6b7e240c-daec-acf3-3aa1-d6efd36ba3ad.png" width="400" height="400">

コンテンツの設定でテンプレートをクリックし、2分割設定にしてください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/b0734e1d-1214-1278-9bb2-bc0a29ea8b27.png" width="400" height="400">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/1a03e6e3-c612-9d81-76cd-acd47c1b2b3b.png" width="400" height="400">



次に画像を作成を選択し、画像をアップロードをクリックしてください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/1e2ece7d-7bc3-31f2-3ed6-5a0b206455dc.png" width="400" height="400">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/b7d722ad-c2b7-f2e7-47e3-e03eebbf75ce.png" width="400" height="400">


アップロードした画像を選択し、再生の画像と停止画像を設定して下さい。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/801fffdf-d166-b2c6-6143-4cdaa324d30c.png" width="400" height="400">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/fa855d51-e5a8-7da7-c876-ac0de7e8cb20.png" width="400" height="400">

画像設定ができたら適用を選択してください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/ac2f1e99-7a98-8253-4a69-6f1eea2a0d4d.png" width="400" height="400">

**4．アクションをテキストに設定し、
Aには「音楽かけて」、Bには「音楽とめて」のコメントを設定してください。**

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811930/5cade3dc-a615-af99-509d-e644df57b72e.png" width="400" height="400">

最後に**保存**を押せば設定完了です。

</details>