ちゃんと音楽に同期させるには・・・


前のエントリでバイクランの動画を公開しましたが、
あの動画は、以下のようなプログラムで描写しています。


                              • -

(音楽再生開始)
repeat
Time++
(各種計算)
(描画処理)
if( Time ==(ある値))
 { (シーン番号)+1 }
waitbyfps 30
loop

                              • -



これのマズイところは、「音楽と描画の同期がまったくとれていない」ことにあります。
音楽の再生タイミングがずれたり、描画をトチったりすると、たちまち音楽とシーン切り替えがずれてしまいます。


上記のプログラムでどこが問題なのかと申しますと、

waitbyfps 30 ; ←ここです。
FPS 30待ちましょうというのは、音楽の再生経過時刻にマッチしない可能性が大いにあります。
FPS 30で描画することができないマシンはもとより、十分な性能を持つマシンでも、
他のプログラムの負荷が一時的に増大してずれることが十分に考えられます。


ですから、「waitbyfpsではなくて、音楽の時間でウェイトをかける」ことが必要です。
従来のwaitbyfpsは、使わないことになります。


まず、準備として、音楽の現在の再生時刻を取得します。
E3Dのサウンド機能だと、MIDIを除いて、音楽の再生時刻は取得できないと思うので、mciの機能を使いました。

                                                                    • -

mci "status (サウンドにつけた名前) position"
ms = stat

                                                                    • -

これで、音楽の再生時刻がミリ秒単位で取得できます。


さらに、FPS 30というのは、1秒間に30回描画しましょうということですから、
およそ33ミリ秒に一回、描画を繰り返せば、FPS 30ということになります。
ということで、「FPS 30で、音楽に同期した描画を行う」プログラムを以下の通り…に書いて失敗しました。

==================================
(音楽再生開始)
repeat
Time++
(各種計算)
(描画処理)
if( Time ==(ある値))
 { (シーン番号)+1 }

mci "status snds position"
ms = stat
ms_diff = 0
ms_start = ms
while ms_diff < 34 ; ←ここで33ミリ秒待って、FPS 30を出そうとしているつもり。
mci "status snds position"
ms = stat
ms_diff = ms - ms_start
await
wend

loop
==================================

失敗(1) whileループの中が重い。ループ中に33ミリ秒を超えてしまうことがあります。
失敗(2) FPSは描画の時間も含めて33ミリ秒だから、描画の後に33ミリ秒待っても描画の時間分ずれます。


ということで、描画に必要な時間を計って、待ち時間から引き算しました。また、ループもやめました。

==================================

(音楽再生開始)
repeat
wait 0
mci "status snds position"
first_time = stat

Time++
(各種計算)
(描画処理)
if( Time ==(ある値))
 { (シーン番号)+1 }

mci "status snds position"
ms = stat
await (1000/30) - ( ms - first_time )

loop
==================================

(1000/30) - ( ms - first_time )がマイナスになった場合は、描画処理を飛ばすなどして帳尻を合わせると、
どんなPCでも、同じタイミングでシーンが切り替わってくれますです。