今回はWebで快適にMIDIを再生する話。
WebでのMIDI再生は動作が重かったので、MP3に変換して再生しました。
はじめに
musicLineでは、コミュニティでユーザーの投稿した曲を再生できます。
またコミュニティ曲のURLを共有して、他のユーザーに曲を勧めることができます。
しかし、musicLineのアプリを持ってないユーザーは曲を聴くことができず、Playストアに飛ぶようになっています。
もちろんPlayストアでアプリをインストールすると良いですが、そこまでしないと曲を聴けないです😅
そこまでして欲しいと願っていますが。。そもそもiOSはアプリすらまだないです😭
他の方法として共有動画を作成することもできますが、それもちょっと気軽に勧めたいって場合にはレンダリング時間が気になるところです。
それでもXで共有動画で曲を紹介してくれている mL民 に感謝😆✨
なのでもっと気軽に、アプリを超えて、曲を共有することを目指して、
WebでmusicLineコミュニティ内の曲を再生できるようにしました。
実装
WebでのMIDI再生の課題
はじめにWeb(Javascript)で動作するMIDIプレイヤーのライブラリを試してみました。
サーバーからMIDIファイルをDLして、Javascriptで再生するシンプルな実装だったのですが、
- 再生処理が重くて音が途切れる
特にモバイルでの動作時は止まることが多い - コミュニティの音と大きく異なる
そもそもサウンドフォント(sf2)が使えない
の課題がありました。
そのため、MIDIファイルを一度音楽ファイル(WAVやMP3)に変換してから、再生する方法に変更しました。
MIDIファイルを音楽ファイルに変換
サーバーから直接MIDIファイルをDLせずに、サーバー(Ruby)で音楽ファイルに変換してDLします。
MIDIからWAVへの変換はFluidSynth
を使いました。さらにWAVからMP3への変換にFFmpeg
を使っています。
この変換する時間はどうしても数十秒は掛かってしまうので、変換したMP3はしばらくキャッシュします。キャッシュしたMP3を使うことで、次訪れるユーザーはすぐに再生できます。
そのため、WAVファイルのままDLしても再生できますが、キャッシュサイズを小さくしたかったので、MP3に圧縮しています。
以上でWebでのMIDI再生の課題が解決できました。
こちらの方法でもWebにアクセスする最初の人はファイルを変換する時間を待たなければいけない問題はあります。
でもアプリをインストールしないと曲を聴けないよりは良いですねえ。
Herokuに外部ライブラリをインポート
より専門的な話になりますが、musicLineのサーバーにFluidSynth
ライブラリを取り入れるところにも課題があったので紹介します。
musicLineのサーバーはHerokuを使っています。そのため、Herokuの環境下に外部ライブラリを構築する必要があります。FFmpeg
はHerokuの拡張機能で簡単に取り込みができましたが、FluidSynth
は拡張機能のBuildPackが用意されていないため、自分でカスタムBuildPackを作る必要がありました。
ちなみに、違う方法(DockerContainerを使った方法)でも環境構築ができるので、こちらも試してみましたがこの方法は辞めました。
ローカルで仮想サーバーを試すときにはDockerContainerを使っているので、そのままHerokuの環境に適用できれば便利だと考えたのですが、
- 他のBuildPackも使えなくなる
Herokuが用意しているBuildPackも自分で環境を整えなければならない - デプロイ(環境構築)が遅い
デプロイの際にキャッシュ等を使って最適化されているそう
ということがあり、Herokuを使う利点が失われると思い辞めました。
カスタムBuildPack
devcenter.heroku.com
このページを参考に作ります。
以下の3ファイル作る必要があるのですが、compile
ファイル以外は他のBuildPackと同じファイルを使って良さそうです。
- detect: この buildpack がアプリに適用するか判定
- compile: アプリで変換手順を実行するために使用
- release: メタデータをランタイムに戻す
compile
ファイルでライブラリのソースをDL・インストールする処理を記載します。
C++のソースをコンパイル・リンクして、環境変数を設定しています。
なかなか難しい部分でしたが、CMAKE等のC++ビルドツールに使い慣れていれば簡単だったかもしれません。
普段なにげに使っているapt-get等のパッケージ管理ツールはとても便利なんだと思い知らされました。
#!/bin/sh BUILD_DIR=$1 CACHE_DIR=$2 # midi->wavへ変換する依存ライブラリlibsndfileをインストール LIBSND_CACHE_DIR=$CACHE_DIR/libsnd LIBSND_DIR=$BUILD_DIR/vendor/libsnd # $LIBSND_CACHE_DIR が存在する時キャッシュを使用 if [ ! -d "$LIBSND_CACHE_DIR" ]; then echo "-----> Installing libsndfile" LIBSND_SRC_DIR=/tmp/libsnd SND_URL="http://www.mega-nerd.com/libsndfile/files/libsndfile-1.0.28.tar.gz" mkdir -p $LIBSND_SRC_DIR curl -L $SND_URL | tar xz -C $LIBSND_SRC_DIR --strip-components=1 mkdir -p $LIBSND_CACHE_DIR cd $LIBSND_SRC_DIR || return ./configure --prefix=$LIBSND_CACHE_DIR make make install echo "-----> libsndfile installation complete" else echo "-----> libsndfile use cache" fi mkdir -p $LIBSND_DIR cp -r $LIBSND_CACHE_DIR/* $LIBSND_DIR # fluidsynthをインストール FLUIDSYNTH_CACHE_DIR=$CACHE_DIR/fluidsynth FLUIDSYNTH_DIR=$BUILD_DIR/vendor/fluidsynth # $FLUIDSYNTH_CACHE_DIR が存在する時キャッシュを使用 if [ ! -d "$FLUIDSYNTH_CACHE_DIR" ]; then echo "-----> Installing Fluidsynth" # FLUIDSYNTH_VERSION="2.3.5" FLUIDSYNTH_SRC_DIR=/tmp/fluidsynth FLUIDSYNTH_VERSION="2.1.7" FLUIDSYNTH_URL="https://github.com/FluidSynth/fluidsynth/archive/refs/tags/v$FLUIDSYNTH_VERSION.tar.gz" mkdir -p $FLUIDSYNTH_SRC_DIR curl -L $FLUIDSYNTH_URL | tar xz -C $FLUIDSYNTH_SRC_DIR --strip-components=1 FLUIDSYNTH_BUILD_DIR=$FLUIDSYNTH_SRC_DIR/build mkdir $FLUIDSYNTH_BUILD_DIR mkdir -p $FLUIDSYNTH_CACHE_DIR cd $FLUIDSYNTH_BUILD_DIR || return cmake .. -Denable-libsndfile=ON -DCMAKE_PREFIX_PATH=$LIBSND_DIR make make install DESTDIR=$FLUIDSYNTH_CACHE_DIR echo "-----> Fluidsynth installation complete" else echo "-----> Fluidsynth use cache" fi mkdir -p $FLUIDSYNTH_DIR cp -r $FLUIDSYNTH_CACHE_DIR/* $FLUIDSYNTH_DIR # 環境変数の追加 mkdir -p $BUILD_DIR/.profile.d echo "export PATH=\$PATH:$HOME/vendor/fluidsynth/usr/local/bin:$HOME/vendor/libsnd/bin" > $BUILD_DIR/.profile.d/fluidsynth.sh echo "export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:$HOME/vendor/fluidsynth/usr/local/lib:$HOME/vendor/fluidsynth/usr/local/lib64:$HOME/vendor/libsnd/lib" >> $BUILD_DIR/.profile.d/fluidsynth.sh
おわりに
今回はWebでMIDIを再生する際に、再生を最適化するためにMP3に変換して再生しました。
WebでMIDIをそのまま再生すると処理が重くモバイルだとまともに再生されないことが誤算でしたが、MP3に変換することでなんとかWebでも再生することができました。
ちなみに同じサウンドフォントを使ってますが、音響ライブラリがアプリと異なるため、音も少し変わります😅
このURLでmusicLineを持っていなければ、Webプレイヤーに移ります。
https://3musicline.com/community/176751
(musicLineを持っていれば、アプリを開きコミュニティに移ります。)
https://3musicline.com/player/176751
(musicLineを持ってるユーザーはこちらのURLでWebプレイヤーを閲覧できます。)
実装の部分は試行錯誤した内容をごちゃごちゃと書いてしまいましたが、試したことを忘れないように備忘録として残しておきます。
ちなみに、最初にアクセスしたユーザーはMP3に変換する時間を待ってもらう必要があります。 頻繁にWebプレイヤーを使うユーザーはアプリをインストールして欲しいですね♪
なので待っている時間はアプリインストールを促す画面を出すようにしています。
ちゃっかり😋