たまには開発日誌らしいことを。
自分は自宅で作業をするとき、よくゲームの戦闘曲を聞いています。元々戦闘シーンのBGMとして作られている曲なだけあって、これらの曲を流していると作業に集中できます。ゲームのサウンドトラックCDを購入するなどして集めた曲を一曲リピートでずっと流しながらの作業が、最近続いています。
しかし、それでも集中を阻むもの。それは「普通のサウンドトラックの曲データは、ゲーム内BGMのように、ループ再生ができない」ということ。いくら戦闘曲とはいえ1ループはそれほど長くなく、だいたい1分ちょっとで終わってしまいます。サウンドトラックのデータは、大体2~3ループぶん収録されてますが、それでも2,3分で終わり。この曲の切れ目の無音期間と、再度イントロから始まってしまう感覚が、どうしても集中力を削いでしまいます。
いちおうMP3の波形をいじって同じ曲が延々とループする曲を作ることは可能ではありますが、データ容量を食うし、なにより手間がかかるのであんまり沢山作るわけにはいかない*1。ゲーム中と同様、BGMを無限ループさせるような音楽プレイヤーはないものか…。
まずは、探す。
とりあえず、Windows用とAndroid用を中心に、いくつか探してみます。RPGツクールなどは楽譜データであるMIDIファイルには拡張コマンドを、オーディオデータであるOGGファイルに拡張ヘッダを用意することで、正確なBGMループ機構を用意しています。しかしどちらも当然正式仕様ではないので、一般的に使える音楽プレイヤーはそれをサポートしていない。
一応、OGGファイルの拡張ヘッダを読み込むプレイヤーはありましたが…。
ただ、RPGツクールの拡張ヘッダはサンプル単位という非常に高精度なループカウンタを指定しているのに対し、こちらはそれをミリ秒単位で判別してしまうためか、ループ精度がイマイチよくなく、曲によってはループ端が非常に目立ってしまいます。
他にもそれらしい方法を探してはみましたが、プログラム上からOGGファイルのループを実現する方法(XNAゲームスタジオあたりには、そういう機能があるらしい?)はあるものの、実際の音楽プレイヤーについてはなかった模様。
・・・ということで、作成してみた
さらに探してみると、HSPのhmm.dllという拡張プラグインは、ミリ秒単位でのOGGファイル無限ループをサポートしているらしい。ミリ秒単位なのでずれが怖かったですが、他にはないのでひとまずこれでやってみる。
HSPはあまり分からないので、非効率な部分はあるかも。
#include "hmm.as" #include "llmod3/llmod3.hsp" #include "obj.as" #include "hspdef.as" #packopt name "bgmplayer" *init ; 初期化処理 scrx = 208: scry = 320 screen 0, scrx, scry pos 4 , 4 mes "BGM Player" pos 4, 30 button "再生" , *playfile pos 80, 30 button "ポーズ" , *pausefile dirlist dir, "*.ogg", 0 listbox list , 300 , dir s = 200,120,4,60 resizeobj 2, s pos 4, 300 chkbox "継ぎ目チェックモード", checkmode s = 120,20 resizeobj 3, s, 1 pauseflag = 0 title "" onexit *exit dshinit if stat == 0: end stop *playfile ; 再生ボタン押下 if( pauseflag == 1){ ; ポーズ→再開 dshplay 0, 1, -1, playstart, playstart + playlen }else{ ; 再生開始 pauseflag = 1 notesel dir noteget fn, list conffn = dir_cur + "\\" + strmid(fn, 0, strlen(fn) - 3) + "txt" file = dir_cur + "\\" + fn playstart = 0 playlen = 0 exist conffn if( strsize != -1 ){ ; confファイルを読み込む 一行目 ループの開始位置、二行目、ループの長さ(ともにミリ秒) notesel conf noteload conffn, -1 noteget ps, 0 noteget pl, 1 playstart = int(ps) playlen = int(pl) } ; 再生失敗エラーの発生頻度抑制 dshend await 1000 dshinit dshloadfname file, 0 if(checkmode == 1){ ; 継ぎ目チェックモード 継ぎ目のちょっと前から再生してみる dshplay 0, 1, -1, playstart, playstart + playlen dshsetseek 0, playstart + playlen - 2000 }else{ ; ふつうに再生 dshplay 0, 1, 0, playstart, playstart + playlen } } title "再生中" pauseflag = 0 goto *playloop stop *pausefile ; ポーズボタン押下 pauseflag = 1 dshpause 0 title "ポーズ" stop *playloop await 10 ; プレイループ if(pauseflag != 1){ ; ポーズ中でなければ、再生位置情報の取得・表示 dshgetplayposition 0, playposition, stopposition gosub *__redraw pos 4 , 180 mes "再生中\n@" + strf("%06d", playposition) + "ms" if(playstart != 0){ mes "ループ開始位置:" + playstart + "ms" mes "再生範囲:" + playlen + "ms" }else{ mes "ループ位置未指定" } redraw 1 goto *playloop }else{ ; 再生位置情報の削除 gosub *__redraw redraw 1 } stop *__redraw ; 再描画処理 redraw 0, 4, 180, 329, 329 color 255, 255, 255 boxf 4, 180, 320, 320 color 0, 0, 0 return *exit dshend end
ソースを見て察しは付くかもしれませんが、このアプリ(ソースコード)があるのと同じフォルダのOGGファイルをリストアップし、指定したファイルを再生するだけです。ただし、OGGファイル再生時に、同じファイル名のテキストファイルが同じフォルダにあれば、その中身を読み込み、以下のルールでループ位置を設定しています。
- 一行目に書かれている数字を、ループ開始のミリ秒とみなす
- 二行目に書かれている数字を、ループ期間のミリ秒とみなす
自分専用なのでエラーチェックは一切していません。たぶんおかしなデータを置いていたりしたら処理が止まります。
実際これで手持ちのファイルを再生してみたところ、多少気になるところもなくはないものの、実用は何とか可能。OGGファイルはWindows標準では再生できないので、Audacityなどでファイルを変換した上、Windows Media PlayerでOggファイルを再生する | 豆知識を参考にコーデックを入れないと再生できないなどと、面倒ではありますが、とりあえず集中は維持できる程度のレベルにおさまっているので、とりあえずこれで使ってみています。
なお、今のところ確認できてる問題点として、特に継ぎ目チェックモード(ループポイントの二秒前から再生し、ループ端の接続状態を確認するモード)の動作が怪しく、ちょくちょく強制終了します(調子が悪いと、それ以外の場所でも、ちょくちょく落ちます。ひょっとしたらどこかでメモリリークでもしてるのかもしれない)。が、とりあえずBGMプレイヤーとしての役目は果たしているので、これでよし。
そのほか
参考までに、ひとまず自分の手持ちデータのループ情報をメモ。なお、今回ソースは全て公式サウンドトラックCDです。どなたかが耳コピして作成したMIDIファイルが元だったり、iTunes Musicで購入したデータが元だったりすると、若干違う可能性はあります。
曲名 | ゲームタイトル | ループ開始ミリ秒 | 再生範囲 |
---|---|---|---|
Time To Make History | Persona 4 Golden | 6448ms | 70713ms |
Mass Desrruction - P3fes version | Persona 3 Fes | 8649ms | 84713ms |
I'll Face Myself - Battle - | Persona 4 | 23894ms | 79588ms |
Period | Persona 4 | 485ms | 46500ms*2 |
Wiping All Out | Persona 3 Portable | 15884ms | 78000ms |
女神の騎士 | FF13-2 | 8982ms | 64886ms |
名誉のファンファーレ | FF13-2 | 10936ms | 48042ms |
祝福のファンファーレ | FF13-2 | 18707ms | 47991ms |
理想としては、波形を見ればどこがループ端かはなんとなく分かると思うので、それで自動的にループするようなプログラムを作成することですが、自分はオーディオフォーマットには詳しくないし、そこまでの余力はなかったので、残念ながらそこまではできませんでした。
余力があれば、もうちょっと高精度なアプリを作れる環境で作り直したいですね。HSPも決して悪い環境ではないと思いますが、元々ゲーム用の言語なのでUI部品があまりにも貧弱ですし、動作が不安定なのも気になりますし。
調べてみたところ、C#用の音楽ライブラリにNAudioというものがあるそうなので、それを使えばもしかしたら実現可能かもしれないですね(ライブラリ自体は、Visual StudioのNuGetを使って導入可能)。
他によい方法、よいアプリなどあれば、教えていただけると助かります。