26
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

ml5でお前のソウルがウルトラソウルかニセモノソウルか判定する

それ本当にウルトラソウル?

そして輝く?」と言われたら皆さんはなんと答えますか?
そうですね、「ウルトラソウウゥッッッッッ ハアイィッッ」ですよね。

でもそのソウル、ちゃんと魂を込めたウルトラソウルですか?
魂のこもっていないニセモノソウルになっていませんか?
今回はTeachable Machineとml5を使って「ウルトラソウルかニセモノソウルか」を判定するウェブアプリを作ってみました。

デプロイしているので是非皆さんもウルトラソウウゥッッッッッハアイィッッしてみてくださいね。
コードも是非ご覧になってハンズオンしてみてください!

Appはこちらから
コードはこちらから

完成デモ

ソウル判定

「夢じゃないあれもこれも」をクリックすると、サビが始まります。
「そして~か~がや~く」に続いて全力で「ウルトラソウウゥッッッッッ ハアイィッッ」と叫んでください。
ウルトラソウウゥッッッッッ ハアイィッッ」より前の部分は判定に関わらないので、しっかり精神統一してください

ウルトラソウルだった場合

ちゃんとウルトラソウルだった場合は「ウルトラソウル」、心がこもってなかったら「ニセモノソウル」と判定されます。
「ニセモノソウル」判定の学習データが僕の歌声なので、僕がいくら全力で歌って検証してもニセモノ扱いされることに気づきました。
難易度がさっぱり分からないので感想やコメントお待ちしております!!

ニセモノソウルだった場合

ニセモノソウルの場合はこちら。
パソコンを前にして全力でウルトラソウルしてるのに非情な判定です。
つまりウルトラソウルはそんな生半可なものじゃないということですね(?)。

お手本を聞く

思い出しながらソウルするのは難しいので、是非お手本を聞いてウルトラソウルを感じてください
やってみると「ハアアィッッ」が案外難しいので、ここの攻略に焦点を置いて頑張ってください。

実装こまごま

学習操作

今回はTeachable MachineというGoogle製サービスを利用して機械学習を行いました。
その場で動画を取り学習データに利用できるなど、サクッとモデルが作れるサービスになっております。
GUIが非常に優秀で本当に使いやすいので、こちらから是非是非ハンズオンしてみてください!

Youtubeからサビの部分を再生したものを学習データとして利用しています。
こちらが「ウルトラソウル」判定用の学習データです。

こちらが「ニセモノソウル」判定用の学習データです。
改めて聞くとめちゃめちゃ恥ずかしいですね。

学習データがこのようになっているので、真面目なロジックとしては「B'zの歌声っぽいかvs僕の歌声っぽいか」で判定しています。
もしニセモノソウルとしか判定されない場合は、声質を可能な限りB'zに近づけるよう努力してみてください

モデルのロード

Teachable Machineで学習したモデルはクラウドへデプロイできます。
URLが発行され、モデルロード用のjsのコードも用意してくれるのでこれをコピペするだけです。
p5.jsを使って予測結果を描画するコードも出力してくれますベンリィィィ

image.png

画面に表示する中身をlabelとしています。
判定は常に実行され、その結果をlabellistにpushし続けています。
後ほどのロジックでlabellistに入った判定結果を利用してlabelに判定結果を代入し、画面表示を行います。

      //////////////////////////////////////
      // 画面に表示するために使うGlobal変数 //
      //////////////////////////////////////
      let label = 'Now Loading...';
      let labellist = [];

      ///////////////////////////////////////////////
      // Teachable Machineによる判定と表示ロジック ///
      //////////////////////////////////////////////

      // 判定器オブジェクト
      let classifier;
      let soundModel = 'https://teachablemachine.withgoogle.com/models/tJ-euCMSN/';

      //モデルのロード
      function preload() {
        // Load the model
        classifier = ml5.soundClassifier(soundModel + 'model.json');
      }

      //キャンバスの作成・判定
      function setup() {
        let canvas = createCanvas(320, 240);
        canvas.parent("canvas");
        classifier.classify(gotResult);
      }

      //labelを常に描画し続ける処理
      function draw() {
        background(0);
        // Draw the label in the canvas
        fill(255);
        textSize(32);
        textAlign(CENTER, CENTER);
        text(label, width / 2, height / 2);
      }

      // モデルが判定に成功するとここが実行
      function gotResult(error, results) {
        if (error) {
          console.error(error);
          return;
        }
        labellist.push(results[0].label);
      }

音楽の再生

先日投稿した記事でYoutube Player APIを勉強したので、音楽再生はこのAPIを利用して実装しました。
ボタンクリックすると音楽が再生されます。
本番かお手本再生かで再生開始時間と終了時間を区別しています。
loadVideoByIDといった便利なメソッドがあるので、是非公式ドキュメントもご覧になってみてくださいね。


     //YoutubePlayerを読み込んだ際に最初に走る処理
      function onYouTubeIframeAPIReady() {
        //player1の定義
        player = new YT.Player('player', {
          height: '0',
          width: '0',
          videoId:'Ujb-ZeX7Mo8',
          events: {
            'onReady': onPlayerReady,
            'onStateChange':onPlayerStateChange
          }
        });
      }

      //ボタンが押されたらplayerを再生
      function playMusic(isOtehon){
        if (isOtehon){
          label = "お手本"
          player.loadVideoById({
            videoId:'Ujb-ZeX7Mo8',
            startSeconds:65.5,
            endSeconds:67.5,  
          })
        }else{
          label = "ウルトラソウル ハイィッ\nで判定"
          player.loadVideoById({
            videoId:'Ujb-ZeX7Mo8',
            startSeconds:46,
            endSeconds:65.5,  
          })
        }
      }

ソウル判定

playerの再生が止まったことをトリガーに、ソウル判定を行います。
お手本再生の場合はソウル判定したくないので、再生終了時点での再生時間を取得して、ウルトラソウル前(だいたい65秒地点ぐらい)で停止した時のみ実行されるよう実装しています。

先述の「モデルのロード」の章で、判定結果が常にlabellistにpushされ続けると書きました。
「ウルトラソウル」以外の部分の判定結果は要らないので、一旦labellistの中身を空にし、空にした直後3秒間の結果を利用してcheckSoulしています。


      //YoutubePlayerが止まったときに走る処理
      function onPlayerStateChange(event) {
        //60~66秒地点で止まった場合のみ実行。66秒以降に止まった場合、お手本を再生しているのでソウル判定をしない
        if (event.data == YT.PlayerState.ENDED && player.getCurrentTime()>60 &&player.getCurrentTime()<66) {
          console.log(player.getCurrentTime())

          //lablelistを空に
          labellist = []

          //3秒間にlabellistにpushされた中身をみてchecksoul
          setTimeout(checkSoul,3000)
        }
      }

checkSoulの中身はこちらです。
3秒間にlabellistに入れられた判定結果を調べて、ホンモノとニセモノの個数を比較して判定しています。
安直な実装ですが、こんなものでも案外うまく判別できるんだなぁと実感しました。


      //ソウル判定
      function checkSoul(){
        //各ソウル判定回数
        let ultrasoul_count = 0;
        let nisemonosoul_count = 0;

        //3秒間にlabellistにpushされた中身を見てカウント
        for (idx=0;idx<labellist.length;idx++){
          if (labellist[idx]=="ホンモノソウル"){
            ultrasoul_count += 1;
          }
          if (labellist[idx]=="ニセモノソウル"){
            nisemonosoul_count +=1;
          } 
        }

        //ウルトラソウルとニセモノソウルの数を比較しlabelに入力
        if (ultrasoul_count> nisemonosoul_count){
          label = "ウルトラソウル"
        }else{
          label = "ニセモノソウル"
        }
      }

Youtube Player API非常に便利なんですが、動画の読み込みが遅かったり「再生」を何回も実行したときに予期しない動作をすることが多いですね。
特にsetTimeoutを実装した場合、動画再生のメソッドを実行してからの経過時間を測るようなので、動画の読み込みにかかる時間は考慮してくれません。
今回はその部分をloadVideoByIdでendSecondsを指定することで解決しましたが、もっといろいろなやり方がありそうですね。
勉強してみたいところです。

コード全体はGist(こちらです)に載せてあるので、是非是非ご覧になってくださいね。
アプリはこちらから!

余談:なぜウルトラソウルに至ったか

ml5やTeachable Machineを利用して何か作りたいなあと考えて最初に思い浮かんだのが、あのズンドコキヨシでした。
ズン・ズン・ズン・ズン・ドコといった独特のメロディを機械学習させて、入力した「ズン」がどの「ズン」かを判定するアプリを作ろうと思っていました。
で実際実装してみたんですがモデルが全然使い物にならず、面白い結果にもならなそうであえなくボツにしたんです。
Youtubeで「ズン」だけを録音し続けるのつらかったです。

音楽部分だけ何かに代えられないか?と考え、かつ多くの人が知っていそうな音楽を考えたところ、「ウルトラソウル」が出てきました。
①で何を判定しようか・・・
②えニセモノソウルのゴロ良くね?
③よし決まり!
で決まりました。安直ですよね。

今後やりたいこと

ズンドコキヨシまとめが作られるのと同じノリでウルトラソウル流行らないかなあとか思ったりしてます。
みんなノリノリになる名曲なので是非Youtube Player APIのハンズオンの時に使ってみてください・・・!

ここまで読んでいただきありがとうございました!
LGTMしていただけると励みになります、是非よろしくお願いします!
コメントもお待ちしております!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
26
Help us understand the problem. What are the problem?