待辦事項 #23347

ピアノロールレインでキー押下タイミングとピアノロールがずれる

啟用日期: 2010-10-05 01:04 最後更新: 2010-10-13 22:51

回報者:
負責人:
類型:
狀態:
關閉
優先權:
7
嚴重程度:
7
處理結果:
修正
檔案:

細節

ピアノロールレインで演奏時間が長い曲を再生すると、曲が進むにつれて、キー押下タイミングとピアノロールのスクロールがずれる。

ピアノロールバーがキーに届く前に、キー押下状態になる。 再生時間10分で約0.1秒、再生時間20分で約0.2秒のズレが生じる。 5分程度の曲ではズレの発生には気づかない。

Ticket History (3/6 Histories)

2010-10-05 01:04 Updated by: yknk
  • New Ticket "ピアノロールレインでキー押下タイミングとピアノロールがずれる" created
2010-10-05 01:24 Updated by: yknk
評語

解析

ピアノロールレインでは、ノートONになる前からキーの押下を開始しなければならないため、 シーンオブジェクト側においてノートごとのON/OFF発生時間をあらかじめ保持している。 ところが、シーケンサからノートON/OFFと共に通知される演奏時間の値が、 曲が進行するにつれてシーンオブジェクト側で想定している値より(わずかではあるが)大きくなっている。

シーンオブジェクト側は「この時間でノートONになるはず」と予測して、キー押下アニメーションを開始するが、 シーケンサがノートON発生時に通知する時刻の値はこれより大きくなる(遅くなる)ため、ピアノロールとずれてしまう。

問題の発生例:

  • シーンオブジェクトはAミリ秒でノートONになると想定している。
  • シーケンサから定期的に通知される時刻がAミリ秒直前になったので、キー押下を開始する。
  • ところがシーケンサがノートONを処理したのはA+100ミリ秒の時点だった。
2010-10-05 01:43 Updated by: yknk
評語

解析続き

(A) シーンオブジェクトはトラックから取得したノートリストで、ノートON/OFFの発生時間を管理している。 この発生時間は、デルタタイム(チックタイム)を実時間(double型)に変換した値を積算して導き出している。

SMTrack::_GetNoteList()
  totalRealtime += _ConvTick2TimeMsec(deltaTime, tempo, timeDivision);

(B) 一方シーケンサも同様に、タイマー処理においてイベント発生ごとに、デルタタイム(チックタイム)を 実時間(double型)に変換した値を積算して、次回イベント発生時刻を導き出している。

SMSequencer::_OnTimer()
  m_NextEventTime += _ConvTick2TimeMsec(deltaTime);

(A)と(B)は同じ計算式でdouble型の値を積算しているにも関わらず、 (B)のほうが徐々に(A)よりも大きな値になっていくようである。

doubleの計算誤差による問題と思われるが、決定的な原因が分からない・・・。

SMIDILib.dllにおいて、コンパイルオプションの浮動小数点モデルの指定を、変更してみたが改善されず。

  • Precise (/fp:precise) ←これがデフォルト
  • Strict (/fp:strict)
  • Fast (/fp:fast)

#ここ一ヶ月近く悩んでいるけど、一向に進展せず。プロジェクト始まって以来、最難関の不具合です。

2010-10-07 01:48 Updated by: yknk
評語

原因

概要

浮動小数点の演算精度の設定が、描画処理を担当するメインスレッドは単精度、 MIDIデータ演奏処理を担当するスレッドは倍精度になっていた。 このため、それぞれのスレッドで全く同じ計算を処理したにもかかわらず、 計算結果に差異が発生した。 メインスレッドが単精度になった原因は、Direct3Dを利用しているため。

以上により、キー押下とピアノロールの描画が徐々にずれる現象が発生した。

詳細

Win32アプリケーションにおいて、丸め方/演算精度/例外といった 浮動小数点プロセッサの動作は、スレッドごとに制御される。

INFO: How Windows handles floating-point calculations
http://support.microsoft.com/kb/102555/en-us/

演算精度はデフォルトで倍精度であるが、IDirect3D9::CreateDeviceを呼び出すと、 DirectXの仕様により、呼び出したスレッドの演算精度が単精度に切り替わる。 これを抑止するには、IDirect3D9::CreateDeviceの引数でD3DCREATE_FPU_PRESERVEを指定すればよいが、 性能の低下や予期しない動作を招く可能性がある。

一方MIDIデータ演奏処理は、メインスレッドからtimeSetEventによって起動される マルチメディアタイマー処理のスレッドで実行している。 このため、メインスレッドとは異なる演算精度が適用されていた。

なお、浮動小数点プロセッサの動作設定は、_controlfpで参照/変更することが可能である。

2010-10-13 22:32 Updated by: yknk
  • 處理結果 Update from to 修正
評語

対策

対策方針

SMIDILibにおいて、doubleを使用する演算処理の前に、浮動小数点の演算精度を倍精度に設定する。 演算処理が完了した後は、変更前の状態に戻す。

対策詳細

浮動点小数プロセッサ制御クラスを新規追加する。

  • SMFPUCtrl

対策方針に従い、特定区間で演算精度を倍精度に保つ。

  • SMSeqData::_CalcTotalTime 総演奏時間算出処理
  • SMSequencer::Play 演奏開始前のチックタイム→実時間変換処理
  • SMSequencer::_OnTimer マルチメディアタイマースレッド開始/終了時
  • SMSequencer::_ProcUserRequest マルチメディアタイマースレッド終了時
  • SMTrack::_GetNoteList ノートリスト取得時のチックタイム→実時間変換処理
2010-10-13 22:51 Updated by: yknk
  • 狀態 Update from 開啟 to 關閉
  • Ticket Close date is changed to 2010-10-13 22:51

Attachment File List

No attachments

編輯

Please login to add comment to this ticket » 登入