#それ本当にウルトラソウル?
「そして輝く?」と言われたら皆さんはなんと答えますか?
そうですね、「ウルトラソウウゥッッッッッ ハアイィッッ」ですよね。
でもそのソウル、ちゃんと魂を込めた__ウルトラソウル__ですか?
魂のこもっていない__ニセモノソウル__になっていませんか?
今回はTeachable Machineとml5を使って「ウルトラソウルかニセモノソウルか」を判定するウェブアプリを作ってみました。
デプロイしているので是非皆さんも__ウルトラソウウゥッッッッッハアイィッッ__してみてくださいね。
コードも是非ご覧になってハンズオンしてみてください!
#完成デモ
##ソウル判定
「夢じゃないあれもこれも」をクリックすると、サビが始まります。
「そして~か~がや~く」に続いて全力で「ウルトラソウウゥッッッッッ ハアイィッッ」と叫んでください。
「ウルトラソウウゥッッッッッ ハアイィッッ」より前の部分は判定に関わらないので、しっかり精神統一してください。
お前のソウルが本当にウルトラソウルかを判定するウェブアプリ作りました。「夢じゃないあれもこれも」をクリックするとサビが始まるので、サビ最後で元気に「ウルトラソウウゥッハアアイィッ」と叫んでください。
— canonno (@canonno_blog) September 2, 2020
え?前奏が長すぎる??
そんなこと言う時点でニセモノソウルです(煽り)#protoout pic.twitter.com/ZarxNGh9XU
##ウルトラソウルだった場合
ちゃんとウルトラソウルだった場合は「ウルトラソウル」、心がこもってなかったら「ニセモノソウル」と判定されます。
__「ニセモノソウル」判定の学習データが僕の歌声なので、僕がいくら全力で歌って検証してもニセモノ扱いされる__ことに気づきました。
難易度がさっぱり分からないので感想やコメントお待ちしております!!
外部マイクがなぜか使えないので音声が聞こえませんが、ホンモノのウルトラソウルを聞かせるとちゃんとウルトラソウルと判定します。#protoout #ml5 #MachineLearning pic.twitter.com/csk9WWwZVt
— canonno (@canonno_blog) September 2, 2020
##ニセモノソウルだった場合
ニセモノソウルの場合はこちら。
__パソコンを前にして全力でウルトラソウルしてるのに非情な判定__です。
つまりウルトラソウルはそんな生半可なものじゃないということですね(?)。
外部マイクがなぜか使えないので音声が聞こえませんが、パソコンを前にして全力でウルトラソウルしてます。
— canonno (@canonno_blog) September 2, 2020
ですがニセモノ扱いされて悲しいです。#protoout #ml5 #MachineLearning pic.twitter.com/C6uXZNGpOM
##お手本を聞く
思い出しながらソウルするのは難しいので、是非お手本を聞いてウルトラソウルを感じてください。
やってみると「ハアアィッッ」が案外難しいので、ここの攻略に焦点を置いて頑張ってください。
ホンモノどんなんだっけ・・・?というときは手本を聞いてください。
— canonno (@canonno_blog) September 2, 2020
しっかりホンモノをソウルに叩き込んでください。#protoout #ml5 #MachineLearning pic.twitter.com/qs6tYSoMLP
#実装こまごま
##学習操作
今回はTeachable MachineというGoogle製サービスを利用して機械学習を行いました。
その場で動画を取り学習データに利用できるなど、サクッとモデルが作れるサービスになっております。
GUIが非常に優秀で本当に使いやすいので、こちらから是非是非ハンズオンしてみてください!
Youtubeからサビの部分を再生したものを学習データとして利用しています。
こちらが「ウルトラソウル」判定用の学習データです。
学習データです pic.twitter.com/lrjd5FgSVm
— canonno (@canonno_blog) September 2, 2020
こちらが「ニセモノソウル」判定用の学習データです。
改めて聞くとめちゃめちゃ恥ずかしいですね。
学習データ2です pic.twitter.com/pcGNW5joTi
— canonno (@canonno_blog) September 2, 2020
学習データがこのようになっているので、真面目なロジックとしては「B'zの歌声っぽいかvs僕の歌声っぽいか」で判定しています。
もしニセモノソウルとしか判定されない場合は、声質を可能な限りB'zに近づけるよう努力してみてください。
##モデルのロード
Teachable Machineで学習したモデルはクラウドへデプロイできます。
URLが発行され、モデルロード用のjsのコードも用意してくれるのでこれをコピペするだけです。
p5.jsを使って予測結果を描画するコードも出力してくれますベンリィィィ
画面に表示する中身を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で「ズン」だけを録音し続けるのつらかったです。
ちなみに今回のボツ案「ズンドコキヨシ判定器」です pic.twitter.com/LdQar9FXbO
— canonno (@canonno_blog) September 2, 2020
音楽部分だけ何かに代えられないか?と考え、かつ多くの人が知っていそうな音楽を考えたところ、「ウルトラソウル」が出てきました。
①で何を判定しようか・・・
②えニセモノソウルのゴロ良くね?
③よし決まり!
で決まりました。安直ですよね。
#今後やりたいこと
ズンドコキヨシまとめが作られるのと同じノリで__ウルトラソウル流行らないかなあ__とか思ったりしてます。
みんなノリノリになる名曲なので是非Youtube Player APIのハンズオンの時に使ってみてください・・・!
ここまで読んでいただきありがとうございました!
LGTMしていただけると励みになります、是非よろしくお願いします!
コメントもお待ちしております!