RPGツクールと数学のブログ

RPGツクールと数学についてのブログです。

MIDIの途中再生

過去ブログの転載です。

RPGツクール2000で、相互変換モジュールを使用して、MIDIの途中再生を制約つきで強引に試す方法の記事です。

fermiumbay13.hatenablog.com

ファイナルファンタジーⅤ以降になると、戦闘が終了してマップ画面のBGMに戻るとき
戦闘が開始する直前の演奏位置から再生されるようになりました。こうすることで、長いBGMのあるダンジョンでも敵とエンカウントしながら、BGMを一通り聴くことができます。

ところがRPGツクール2000は敵と遭遇して戦闘になると、マップ画面に戻ってきたときBGMは初めから再生し直されるので、すべてを一通り聴くということが出来なかったわけですよ。まあ仕方ありません。

相互変換モジュールを用いるとイベント命令の上限値を突破して結構自由な値を指定してイベント命令を作成していくことが可能であり、BGMの演奏の「テンポ」まで上限を突破できることを確認したので、途中からBGMを再生できる兆しが見えました。

BGMの演奏では別のBGMを選択するたびに再生位置が初めに戻ってしまいますが、音量やテンポと調節するだけであれば再生位置は変化しません。初め無音ですごいスピードで再生し、途中で音量とテンポを戻せば、途中から再生したように感じられるわけです。これを利用すれば、戦闘が終わったときに途中からの再生位置でBGMを聴けるようになるはずです。

そこで色々やってみましたが、結論から言えばデフォルト戦闘では不自然になる可能性があります。「無音ですごいスピードで再生」するのに要する時間が3秒程度になってしまったためです。3秒も無音の状態が続けばあまりにも不自然ですから、どうやら使えそうにはありません。

自作戦闘か何かでなら、戦闘勝利の音をBGMではなく効果音にし、その間にその無音BGMを流せば自然になります。

ということで、戦闘で用いるならばおそらく自作戦闘に限ります。戦闘にこだわらないのであれば、何かに使えることでしょう……たぶん。FF7のフィールドは町に入っても外に出ると継続再生されるので、そういうのに使えるのかな?

ここではとりあえず、指定した時間にフェードイン再生するサンプルを提示します。というか試作品です。曲の終端に行くほど誤差が大きくなり、ちょっとずれてしまうことがありますが、ご了承下さい。

#include "rpgfunc.as"

file="動物"    // BGMのファイル名
tm=36.0    // BGMの秒数(5分なら300とか)
t=22.0    // 再生させたい位置[秒]

;--------------------------------------------

goto *@f
#deffunc time double ti
    ti2 = int(10.0*ti)
    pause ti2
    repeat int(60.0*(ti-0.1*ti2))
        pause 0
        pn++
    loop
    return
*@f

pn = 0

n=0.1*int(0.1*tm+0.99)+0.1
y=0.1*int( (100.0*n-t)/9.9 )

if n=y{
    mes
    bgm
    time n-t
    bgm file,0,0,100
    time t
}else{
    x=int(100.0*(t-y)/(n-y))
    bgm file,0,0,100
    time y
    bgm file,0,0,x
    time n-y
}
if pn>0&pn<6{
    repeat 6-pn
        pause 0
    loop
}

bgm file,0,0,100
repeat 5,1
pause 1
bgm file,0,20*cnt,100
loop

send

↑最初の3行だけ変えてください。詳しい説明は省きますが……まあ、どっちでも。数値は、うしろに「.0」を付けて下さい。実数型というものにするためです。

導出

曲の再生位置t[s]、テンポ100%で再生する時間y[s]、無音で再生する総合時間n[s]、すごいスピードで再生するときのテンポxとします。

まず、曲の再生位置tは、
t=y+(n-y)x …① と表すことが出来ます。
テンポ100%でy[s]再生し、その後テンポxで(n-y)[s]再生することで、実際の再生時間tとなるためです。
上式から、
x=(t-y)/(n-y) …②
と導かれます。

ここで、初期条件としてx≦xm という条件を設けます。xmは、xの上限とします。xの上限がxmであるためのyの上限を求めます。

②を左辺に代入して、(t-y)/(n-y)≦xm となりますから、yについて解くとy≦(xmn-t)/(xm-1) となり、これがyの上限です。

BGMは再生直後読み込みによってブランクがあいてしまう可能性があるので、yの下限は0.1[s]とします。最低でも100%の速さで少し鳴らします。

よってyの範囲は、
0.1≦y≦(xmn-t)/(xm-1) となります。

次に、nの範囲を求めます。nの範囲は曲全体の長さtmから一意に求まるようにします。

まずは下限を求めます。①より、t=tm、x=xmのとき(nが最短になるためにはxが最大となる)
tm=y+(n-y)xm ですから、nが最短となるためにはyの下限を適用します。
tm=0.1+(n-0.1)xm となるので、nについて解くと
n={tm+0.1(xm-1)}/xm となり、これがnの下限になります。
次に上限を求めます。nは長すぎてもいけません。nが最長となるためにはxの下限を適用します。

xは100%を下回らないとすると、①より、t=tm、x=1のとき、tm=y+n-y となるので、n=tm となります。
従って、nの範囲は
{tm+0.1(xm-1)}/xm≦n≦tm …③
となります。

曲の長さtmが分かると③よりnを決めることが出来ます。なるべく短い方が良いでしょうから、下限の値を採用しましょう。nが決まり、再生位置tが決まれば、yの範囲が決まります。yは長い方が誤差が小さくなりやすいので、yは上限の値を採用します。yが決まれば、xが一意に決まります。よって、再生位置tになるためのx、y、nがすべて求まります。

具体的には、tm、tが決まったとき
n={tm+0.1(xm-1)}/xm
y=(xmn-t)/(xm-1)
x=(t-y)/(n-y)
として求まります。

xmの値は100が良い所です。150とかにすると、誤差が大きくなってしまいます。
xm=100を代入すると上式は、

n=(tm+9.9)/100
y=(100n-t)/99
x=(t-y)/(n-y)

となります。
tmが決まればnが決まり、更にtが決まればyとxが決まります。
曲の長さと再生位置からすべて求まるということです。

このサンプルではHSPの方であらかじめ再生位置を入力しなければならないので、ゲーム中に好きな再生位置で再生することが出来ません。そのため、ゲーム中で再生位置を変更するようにしたい場合は、「MIDIの演奏位置」を変数で取得する方法を用います。MP3も同様にこのコマンドで取得できるらしいのですが、詳しくはわかんないです……