【ドラム×プログラミング】テンポキープできないドラマーのためのTempoVisualizer

初めまして、さくたまです。

この記事は慶應理工アドベントカレンダーの22日目です。
みんなそれぞれ全然違うことやってて面白いなと思って見てます笑 そしてみんなレベル高くてドキドキです

最近ちまちま作ってたおもちゃを、年内にまとめたいなということでいい機会だと思って発表したいと思います!
...思って、、いたのですが、あと少し、皆さんにここから触っていただくには間に合いませんでした...
まとまったらまた追記ででも発信したいと思います

Tempo Visualizer

こんなものを作りました
動画はこちら
音源をアップロードすると、テンポの変化を解析して、色で表現して示してくれます。
いつどこでテンポが乱れやすいのかしっかり認識して練習することができます!!笑

ちなみにこの音源は自分がライブで叩いた音源です。テンポキープのできなさを晒していく...

背景

自己紹介: Adobeオタクドラマーたまごエンジニア???

まず少し自己紹介します、情報工学科のB3です。編集ツール好きでCG~MRの創作ツールを広げていきたいAdobeオタクです。映画撮ってたのがきっかけで、音響やってたので音楽も興味あります。趣味ドラマーです(5年目くらい?)。エンジニアとしては現状広く浅いです(これから頑張る)

動機

上で書いたような、色々自分がやってることごちゃ混ぜにしたらこうなりましたって感じのがTempoVisualizerです。

ドラマー的視点から言うと、私は趣味でずっと一人でやってて、バンドで合わせるライブ経験があまりない中、今年の8月に久しぶりにライブをやって、テンポの揺れは演奏中全然わからないし、入りから早いしテンポキープの出来なさを痛感しました。後から振り返ってもここ速くなってるね?というのがなかなか人と共有しにくいし、音ってその時聞いてないとよく分からないので振り返りにくいなと思って、目で見えるようにしたい!と思ったのが動機です

エンジニア的視点から言うと、とにかく何か趣味で作りたい!音楽処理趣味でやりたい!何かを一通りアプリケーション化できるようにしたい!色々(AWSとか、Laravel・Web以外のアプリケーションとか)触ってみたいよね!という気持ちです

概要

子供がセロテープのつぎはぎで作ったみたいなガラクタなので、「もっといい選択肢があるよ」というのがあれば是非是非教えてください!!

音楽処理

今回はlibrosaというPythonのライブラリを使って実装しました。
今回実装したコードはこちらにあります。後半は色々ごちゃごちゃ遊んだ残骸が残ってますが、ドライブにファイルをアップロードしてから、

ドライブに音楽ファイルをアップロードしてファイル名指定

あとはShift-Enterを連打していけばカラフルな画像が出てくると思います。

https://colab.research.google.com/drive/1hRX1PTEwkhXQSwrPm3APXPlHl7R-D4iG?usp=sharing

周波数の処理など、音声認識をかじった時の知識を頑張って引っ張り出そうと意気込んでいたのに、librosaが結構やってくれてしまって、曲のビートを検出するとかもやってくれちゃいます。
主な処理としては、

  1. librosaに音楽ファイルを渡す

  2. 平均テンポと、各拍のフレームを出してくれる

  3. 前後4拍の平均をとって各拍のテンポ変化を滑らかにする

  4. 平均テンポと各拍のテンポの差から、色を決定する

  5. 画像を塗りつぶす

ということをしています。

あとは、音楽のどの部分で色が変わってるのかパッとわかるように、音楽プレイヤーに画像を重ねればいいだけです。

UI(Web・バックエンドはLaravel)

画像幅と音楽プレイヤーの幅をちまちま調整してやれば、UIは割と簡単にできました。UIは

開発環境でlibrosa動くようにするのが辛かった...(また後で話します)

Laravelのプロジェクト内にPythonファイルを置き、

TempoVisualizerController.php

public function post(Request $request) {
$filepath = 'public/music';
$file = $request->file('file');
$file->storeAs($filepath, $filename)
$result = $this->analyze($filename);
// 省略
}
public function analyze(string $music_file) {
// Pythonのファイルがあるパスを設定
$python_path = "../app/Python/";
$command = "python3 " . $python_path . "tempo_visualizer.py " . $music_file;
exec($command , $outputs, $return_val);
return $outputs;
}

storageに画像を書き出しています。

tempo_visualizer.py

if __name__ == '__main__':
music_filename = sys.argv[1]
image_filename = os.path.splitext(music_filename)[0]+'.png'
tempo, beats = analyze_tempo(music_filename)

arr_tempo, beat_time = spline_by_mean_between_frames(tempo, beats, 4)

img = create_image_from_tempo(beats, beat_time, tempo, arr_tempo)
ret = cv2.imwrite('./storage/image/' + image_filename,img)
assert ret,'failed'
print(tempo)

デプロイまでやってみよう!と思い、LaravelにPythonを突っ込んだままデプロイしようとしました。紆余曲折ありDocker環境でやっていたので、ここまできたらECSを使えばすぐデプロイできたかもしれません。

  •  一回やったことあるしな

  • 前ec2の管理ミスって3ヶ月くらい無駄にサーバー代払ってたことがあって嫌だな...

  • 別のUIはiOSでもやってみたい...

という理由から、Python部分をAPI化する旅が始まりました...orz

API

今回は、AWSのAPI Gateway, Lambda, S3を主に使って実装しました。(情報工学実験だー)
色々すっ飛ばして、最終的に出来上がったものはこちら


1. Webサーバーがポストされた音楽ファイルをlocal storageに保存しつつS3にも上げる(図中POST)
2. Step FunctionsをトリガーするAPI Gatewayにリクエスト。
3. Lambdaは音楽ファイルを処理し、生成された画像ファイルをS3に書き出すタスクを開始する。
4. WebサーバーにinvokeしたタスクのIDが返る
5. ブラウザは待ち画面に遷移。(status)
6. IDをもとにAPI Gatewayを叩いてDescribe Executionを実行し、Step Functionsのステータスを定期的に確認
7. タスクが完了したらS3から画像を取得(result)

順序がわかりにくいのでシーケンス図にするとこんな感じです

API使う側のLaravel部分のコードはこんな感じです。

https://github.com/sakutama-11/TempoVisualizer/blob/master/app/Http/Controllers/TempoVisualizerController.php

AWSの色々は、隠したりとかスクショしたりめんどくさいので、コード載せずに上の図で説明します。こういう時にserverless framework使えば共有しやすいのかという学び。あとは初歩的なことなのですが、IAMの使い方を学び、慣れました
詳細は②に書きます

苦労したポイント

①音楽処理ライブラリ、"librosa"が曲者だった

ローカルのDocker環境とlambdaに上げるようで2回環境構築したのですが

  • 依存関係を持つ

    • scipy

    • PyGObject

    • soundfile

    • audioread

    • ffmpeg

がそれぞれOSが変わると同じように動かないパッケージになっていたり、Pythonのバージョンと互換性を気にしなければいけなかったりしてとてもとても時間がかかりました。一つ一つはエラーググればクリティカルな情報があるんですが、うまくいかずに諦めた方法もいくつかありました。どこで詰まったか忘れましたがM1でビルドできない依存関係があったのでDockerでやらざるを得ませんでした。コンテナに入って確認しながら色々いじってみて上手くいったらDockerfileに書き込むのの無限ループです。

↓公式
https://github.com/librosa/librosa

↓LambdaのコンテナのDockerfile
https://github.com/sakutama-11/TempoVisualizer-API/blob/main/Dockerfile

ffmpegについては、mp3, mp4, m4aなどの音楽ファイルを扱うのに必要なのですが、結局使えていません

https://blog.toru-takagi.dev/article/24

上の記事を参考にし、ローカルでコンテナを動かす分には実行できたのですが、lambda上だと実行ユーザーが違うのでpermissionの問題でうまくいかずに詰まって諦めました。(手元でデバッグできないバグ辛い)
laravel側でwavにしてから送ってあげるのもありかなと思っています。

Google Colaboratoryなんで```import librosa```だけでで使えるようになるのかまじ意味分からん。

②LambdaでAPI化したい!

1. Step Functions??
AWS Step Functionsとは

AWS Step Functions は、デベロッパーが分散アプリケーションの構築、IT およびビジネスプロセスの自動化、AWS のサービスを利用したデータと機械学習のパイプラインの構築に使用するローコードのビジュアルワークフローサービスです。

らしいです。私は今は、AWSサービスを組み合わせた状態遷移を管理して実行してくれるのね。と理解しています。そういう意味では今回は状態一個なので宝の持ち腐れかもしれません。Lambdaの処理が終わった後に何かを実行して終了を伝える方法があったりしそうですがわかりません。

今回なぜ使ったかというとAPI Gatewayのタイムアウト問題です。今は①の理由でwavしか処理できないのですが、mp3だとライブラリの関係で読み込みに時間がかかったり、librosaの音楽解析の時間がかかるので意外と時間がかかります。
API Gatewayのタイムアウトは30秒なので、Lambdaを動かしつつとりあえずレスポンスを返す必要がありました。

こちらの記事を参考にしました

https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/tutorial-api-gateway.html

↑にLambdaを組み合わせる形

https://dev.classmethod.jp/articles/apigateway-stepfunctions-asynchronous/


2. Lambdaはなぜコンテナでやったのか
①でDocker環境で頑張って調整した環境を使い回したかったからです。

3. S3の使い方
最初はbase64にして文字列で送ったりしていたのですが、長くてペイロードをはみ出てしまったのもあり、デコードエンコードもめんどくさいし、LaravelのストレージとPythonのboto3使ってS3でやりとりするのが結局一番シンプルで楽でした。https://readouble.com/laravel/8.x/ja/filesystem.html

③Herokuがよくわからない

ここまでできて、LocalではスムーズにAPIで動くようになって、あとはLaravel部分をデプロイするだけなので、複雑なこともないしGitとHerokuでやるのがタダで手間も少ないだろうと思ったのですが、大きなファイルをやり取りしていることもあってLaravelからS3のファイルアップロードが長すぎる、、HerokuのWebサーバー周りの仕組みをもう少し勉強しなければいけなさそうです。

まとめ

まとまらないまま書きすぎましたね。。
まとめると、音楽ファイルをS3に入れたらテンポを可視化した画像ファイルを作ってくれるAPIを作りました。AWS難しいけどわかるとパズルみたいで楽しいなと思います。

テンポキープ、難しいけど、これだと一人で練習できないから(音源聴きながらだと合っちゃう)自分の演奏テンポに合わせてくれちゃう音源が必要だな…🤔

一人でズルズルやりすぎて時間かけすぎてしまうので小さいものを量産していきたい...!ドラムや、身近な面白いものをこれからも作っていきたいです

来年の抱負

  • ガラクタ大×3, 小×5

  • Twitterを動かして発信を増やす!

発表する機会ないとちゃんとまとまらないから、慶應理工アドベントカレンダー声かけてもらえてよかったです
ありがとうございました!

この記事が気に入ったらサポートをしてみませんか?