OSDN Git Service

AUTOヒット時にレーンフラッシュするようにした。
[strokestylet/CsWin10Desktop3.git] / StrokeStyleT / ステージ / 演奏 / 演奏ステージ.cs
index ac74bb7..2ec8baa 100644 (file)
@@ -67,7 +67,6 @@ namespace SST.ステージ.演奏
                        using( Log.Block( FDKUtilities.現在のメソッド名 ) )
                        {
                                this._活性化した直後である = true;
-                               this._演奏開始時刻sec = 0.0;
                                this._背景動画開始済み = false;
                                this._現在進行描画中の譜面スクロール速度の倍率 = App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率;
                                this._描画開始チップ番号 = -1;
@@ -145,7 +144,7 @@ namespace SST.ステージ.演奏
                        }
                }
 
-               public override void 進行する()
+               public override void é«\98é\80\9fé\80²è¡\8cã\81\99ã\82\8b()
                {
                        Debug.Assert( this.活性化している );
 
@@ -170,8 +169,8 @@ namespace SST.ステージ.演奏
                                                        double 演奏開始位置の先頭からの時間sec = 0.0;
 
                                                        // 演奏開始時刻の設定(1)
-                                                       this._演奏開始時刻sec = App.サウンドデバイス.GetDevicePosition() - 演奏開始位置の先頭からの時間sec;
-                                                       Log.Info( $"演奏開始時刻(背景動画再生チェック前): {this._演奏開始時刻sec} sec" );
+                                                       App.サウンドタイマ.リセットする( 演奏開始位置の先頭からの時間sec );
+                                                       Log.Info( $"演奏開始時刻(背景動画再生チェック前): {App.サウンドタイマ.現在時刻sec} sec" );
 
                                                        // 演奏開始時刻の設定(2) ビュアーメッセージがある場合、開始時刻を修正する。
                                                        var msg = App.最後に取得したビュアーメッセージ;
@@ -189,8 +188,8 @@ namespace SST.ステージ.演奏
                                                        this._再生中の時刻なら動画とBGMを再生開始する( 演奏開始位置の先頭からの時間sec );
 
                                                        // 演奏開始時刻の設定(3) 動画とBGMが再生された場合、ここに到達するまでに多少の遅延が生じているので、ここで演奏開始時刻を再取得しておく。
-                                                       this._演奏開始時刻sec = App.サウンドデバイス.GetDevicePosition() - 演奏開始位置の先頭からの時間sec;
-                                                       Log.Info( $"演奏開始時刻(背景動画再生チェック後): {this._演奏開始時刻sec} sec" );
+                                                       App.サウンドタイマ.リセットする( 演奏開始位置の先頭からの時間sec );
+                                                       Log.Info( $"演奏開始時刻(背景動画再生チェック後): {App.サウンドタイマ.現在時刻sec} sec" );
                                                        //----------------
                                                        #endregion
                                                }
@@ -199,58 +198,6 @@ namespace SST.ステージ.演奏
 
                                                this._FPS.FPSをカウントしプロパティを更新する();
 
-                                               #region " 譜面スクロール速度が変化している → 追い付き進行 "
-                                               //----------------
-                                               {
-                                                       double 倍率 = this._現在進行描画中の譜面スクロール速度の倍率;
-
-                                                       if( 倍率 < App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 )
-                                                       {
-                                                               if( 0 > this._スクロール倍率追い付き用_最後の値 )
-                                                               {
-                                                                       this._スクロール倍率追い付き用カウンタ = new LoopCounter( 0, 1000, 10 );    // 0→100; 全部で10×1000 = 10000ms = 10sec あれば十分だろう
-                                                                       this._スクロール倍率追い付き用_最後の値 = 0;
-                                                               }
-                                                               else
-                                                               {
-                                                                       while( this._スクロール倍率追い付き用_最後の値 < this._スクロール倍率追い付き用カウンタ.現在値 )
-                                                                       {
-                                                                               倍率 += 0.025;
-                                                                               this._スクロール倍率追い付き用_最後の値++;
-                                                                       }
-
-                                                                       this._現在進行描画中の譜面スクロール速度の倍率 =
-                                                                               Math.Min( 倍率, App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 );
-                                                               }
-                                                       }
-                                                       else if( 倍率 > App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 )
-                                                       {
-                                                               if( 0 > this._スクロール倍率追い付き用_最後の値 )
-                                                               {
-                                                                       this._スクロール倍率追い付き用カウンタ = new LoopCounter( 0, 1000, 10 );    // 0→100; 全部で10×1000 = 10000ms = 10sec あれば十分だろう
-                                                                       this._スクロール倍率追い付き用_最後の値 = 0;
-                                                               }
-                                                               else
-                                                               {
-                                                                       while( this._スクロール倍率追い付き用_最後の値 < this._スクロール倍率追い付き用カウンタ.現在値 )
-                                                                       {
-                                                                               倍率 -= 0.025;
-                                                                               this._スクロール倍率追い付き用_最後の値++;
-                                                                       }
-
-                                                                       this._現在進行描画中の譜面スクロール速度の倍率 =
-                                                                               Math.Max( 倍率, App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 );
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               this._スクロール倍率追い付き用_最後の値 = -1;
-                                                               this._スクロール倍率追い付き用カウンタ = null;
-                                                       }
-                                               }
-                                               //----------------
-                                               #endregion
-
                                                #region " 背景動画が再生されているのにBGMがまだ再生されていないなら、すぐに再生を開始する。"
                                                //----------------
                                                if( this._背景動画開始済み && !( this._BGM再生開始済み ) )
@@ -261,25 +208,22 @@ namespace SST.ステージ.演奏
                                                //----------------
                                                #endregion
 
-                                               this._レーンフレーム.進行する();
-                                               this._コンボ.進行する();
-                                               this._ヒットランク.進行する();
-                                               this._ドラムセット.進行する();
-                                               this._回転羽.進行する();
-
                                                double 現在の演奏時刻sec = this._演奏開始からの経過時間secを返す();
 
+                                               // 自動演奏
+
                                                #region " 自動ヒット処理。"
                                                //----------------
-                                               this._描画範囲のチップに処理を適用する( 現在の演奏時刻sec, ( chip, index, ヒット判定バーとの時間sec, ヒット判定バーとの距離 ) => {
+                                               this._描画範囲のチップに処理を適用する( 現在の演奏時刻sec, ( chip, index, ヒット判定バーと描画との時間sec, ヒット判定バーと発声との時間sec, ヒット判定バーとの距離 ) => {
 
                                                        var オプション設定 = App.ユーザ管理.選択されているユーザ.オプション設定;
                                                        var 対応表 = オプション設定.ドラムとチップと入力の対応表[ chip.チップ種別 ];
                                                        var AutoPlay = オプション設定.AutoPlay[ 対応表.AutoPlay種別 ];
 
                                                        bool チップはヒット済みである = chip.ヒット済みである;
-                                                       bool チップはMISSエリアに達している = ( ヒット判定バーとの時間sec > オプション設定.最大ヒット距離sec[ ヒットランク種別.POOR ] );
-                                                       bool チップはヒット判定バーを通過した = ( 0 <= ヒット判定バーとの距離 );
+                                                       bool チップはMISSエリアに達している = ( ヒット判定バーと描画との時間sec > オプション設定.最大ヒット距離sec[ ヒットランク種別.POOR ] );
+                                                       bool チップは描画についてヒット判定バーを通過した = ( 0 <= ヒット判定バーと描画との時間sec );
+                                                       bool チップは発声についてヒット判定バーを通過した = ( 0 <= ヒット判定バーと発声との時間sec );
 
                                                        if( チップはヒット済みである )
                                                        {
@@ -292,12 +236,12 @@ namespace SST.ステージ.演奏
                                                                // MISS判定。
                                                                if( AutoPlay && 対応表.AutoPlayON.MISS判定 )
                                                                {
-                                                                       this._チップのヒット処理を行う( chip, ヒットランク種別.MISS, 対応表.AutoPlayON.自動ヒット時処理 );
+                                                                       this._チップのヒット処理を行う( chip, ヒットランク種別.MISS, 対応表.AutoPlayON.自動ヒット時処理, ヒット判定バーと発声との時間sec );
                                                                        return;
                                                                }
                                                                else if( !AutoPlay && 対応表.AutoPlayOFF.MISS判定 )
                                                                {
-                                                                       this._チップのヒット処理を行う( chip, ヒットランク種別.MISS, 対応表.AutoPlayOFF.ユーザヒット時処理 );
+                                                                       this._チップのヒット処理を行う( chip, ヒットランク種別.MISS, 対応表.AutoPlayOFF.ユーザヒット時処理, ヒット判定バーと発声との時間sec );
                                                                        return;
                                                                }
                                                                else
@@ -306,17 +250,27 @@ namespace SST.ステージ.演奏
                                                                }
                                                        }
 
-                                                       if( チップはヒット判定バーを通過した )
+                                                       if( チップは発声についてヒット判定バーを通過した )
+                                                       {
+                                                               // 自動ヒット判定。
+                                                               if( ( AutoPlay && 対応表.AutoPlayON.自動ヒット && 対応表.AutoPlayON.自動ヒット時処理.再生 ) ||
+                                                                       ( !AutoPlay && 対応表.AutoPlayOFF.自動ヒット && 対応表.AutoPlayOFF.自動ヒット時処理.再生 ) )
+                                                               {
+                                                                       this._チップの発声を行う( chip, ヒット判定バーと発声との時間sec );
+                                                               }
+                                                       }
+
+                                                       if( チップは描画についてヒット判定バーを通過した )
                                                        {
                                                                // 自動ヒット判定。
                                                                if( AutoPlay && 対応表.AutoPlayON.自動ヒット )
                                                                {
-                                                                       this._チップのヒット処理を行う( chip, ヒットランク種別.AUTO, 対応表.AutoPlayON.自動ヒット時処理 );
+                                                                       this._チップのヒット処理を行う( chip, ヒットランク種別.AUTO, 対応表.AutoPlayON.自動ヒット時処理, ヒット判定バーと発声との時間sec );
                                                                        return;
                                                                }
                                                                else if( !AutoPlay && 対応表.AutoPlayOFF.自動ヒット )
                                                                {
-                                                                       this._チップのヒット処理を行う( chip, ヒットランク種別.AUTO, 対応表.AutoPlayOFF.自動ヒット時処理 );
+                                                                       this._チップのヒット処理を行う( chip, ヒットランク種別.AUTO, 対応表.AutoPlayOFF.自動ヒット時処理, ヒット判定バーと発声との時間sec );
                                                                        return;
                                                                }
                                                                else
@@ -329,10 +283,119 @@ namespace SST.ステージ.演奏
                                                //----------------
                                                #endregion
 
-                                               // 入力
+                                               // 入力(1) 手動演奏
 
                                                App.入力管理.すべての入力デバイスをポーリングする( 入力履歴を記録する: false );
 
+                                               #region " ユーザヒット処理。"
+                                               //----------------
+                                               {
+                                                       var オプション設定 = App.ユーザ管理.選択されているユーザ.オプション設定;
+
+                                                       #region " ヒットしてようがしてまいが起こすアクション(空打ち処理)。 "
+                                                       //----------------
+                                                       foreach( var 入力 in App.入力管理.ポーリング結果 )
+                                                       {
+                                                               if( 入力.InputEvent.離された )
+                                                                       continue;   // 押下イベントじゃないなら無視。
+
+                                                               this._ドラムセット.ヒットアニメ開始( 入力.Type, オプション設定.表示レーンの左右 );
+
+                                                               var カラム = オプション設定.ドラムとチップと入力の対応表.対応表.Where( ( kvp ) => ( kvp.Value.ドラム入力種別 == 入力.Type ) );  // カラムは struct なので FirstOrDefault() は使わない。
+                                                               if( 0 < カラム.Count() )
+                                                                       this._レーンフレーム.フラッシュ開始( カラム.First().Value.表示レーン種別 );
+                                                       }
+                                                       //----------------
+                                                       #endregion
+
+                                                       var 処理済み入力 = new List<ドラム入力イベント>(); // ヒット処理が終わった入力は、二重処理しないよう、この中に追加しておく。
+
+                                                       this._描画範囲のチップに処理を適用する( 現在の演奏時刻sec, ( chip, index, ヒット判定バーと描画との時間sec, ヒット判定バーと発声との時間sec, ヒット判定バーとの距離 ) => {
+
+                                                               #region " ヒット判定 "
+                                                               //----------------
+                                                               if( chip.ヒット済みである )
+                                                                       return;
+
+                                                               var チップの対応表 = オプション設定.ドラムとチップと入力の対応表[ chip.チップ種別 ];
+
+                                                               if( オプション設定.AutoPlay[ チップの対応表.AutoPlay種別 ] )
+                                                                       return; // チップが AutoPlay ON である。
+
+                                                               if( !( チップの対応表.AutoPlayOFF.ユーザヒット ) )
+                                                                       return; // このチップは、AutoPlay OFF 時でもユーザヒットの対象ではない。
+
+                                                               double ヒット判定バーとの時間の絶対値sec = Math.Abs( ヒット判定バーと描画との時間sec );
+
+                                                               bool チップはMISSエリアに達している = ( ヒット判定バーと描画との時間sec > オプション設定.最大ヒット距離sec[ ヒットランク種別.POOR ] );
+                                                               if( !( ヒット判定バーと描画との時間sec >= -( オプション設定.最大ヒット距離sec[ ヒットランク種別.POOR ] ) && !チップはMISSエリアに達している ) )
+                                                                       return; // チップはヒット可能エリアにある。
+
+                                                               var ヒット入力 = App.入力管理.ポーリング結果.FirstOrDefault( ( 入力 ) => {
+                                                                       #region " chip にヒットする入力があれば true を返す。"
+                                                                       //----------------
+                                                                       if( !( 入力.InputEvent.押された ) )
+                                                                               return false;   // 押下入力じゃない。
+
+                                                                       if( 処理済み入力.Contains( 入力 ) )
+                                                                               return false;   // すでに今回のターンで処理済み(=処理済み入力リストに追加済み)。
+
+                                                                       // chip がシンバルフリーの対象なら、chip に直接対応する入力の他にも、ヒット判定すべき入力がある。
+                                                                       if( チップの対応表.シンバルフリーの対象 && オプション設定.シンバルフリー )
+                                                                       {
+                                                                               // この入力に対応するカラムのうち、
+                                                                               var カラムs = オプション設定.ドラムとチップと入力の対応表.対応表.Where( ( kvp ) => ( kvp.Value.ドラム入力種別 == 入力.Type ) );
+                                                                               foreach( var カラム in カラムs )
+                                                                               {
+                                                                                       // シンバルフリーなチップがあるなら true。
+                                                                                       if( カラム.Value.シンバルフリーの対象 )
+                                                                                               return true;
+                                                                               }
+                                                                               // まったくなかったら false。
+                                                                               return false;
+                                                                       }
+
+                                                                       // chip に対応する入力なら true。
+                                                                       return ( チップの対応表.ドラム入力種別 == 入力.Type );
+                                                                       //----------------
+                                                                       #endregion
+                                                               } );
+
+                                                               if( null == ヒット入力 )
+                                                                       return;
+                                                               //----------------
+                                                               #endregion
+
+                                                               処理済み入力.Add( ヒット入力 );
+
+                                                               #region " ヒットランクを判定する。"
+                                                               //----------------
+                                                               var ヒットランク = ヒットランク種別.POOR;
+
+                                                               if( ヒット判定バーとの時間の絶対値sec <= オプション設定.最大ヒット距離sec[ ヒットランク種別.PERFECT ] )
+                                                               {
+                                                                       ヒットランク = ヒットランク種別.PERFECT;
+                                                               }
+                                                               else if( ヒット判定バーとの時間の絶対値sec <= オプション設定.最大ヒット距離sec[ ヒットランク種別.GREAT ] )
+                                                               {
+                                                                       ヒットランク = ヒットランク種別.GREAT;
+                                                               }
+                                                               else if( ヒット判定バーとの時間の絶対値sec <= オプション設定.最大ヒット距離sec[ ヒットランク種別.GOOD ] )
+                                                               {
+                                                                       ヒットランク = ヒットランク種別.GOOD;
+                                                               }
+                                                               //----------------
+                                                               #endregion
+
+                                                               this._チップのヒット処理を行う( chip, ヒットランク, チップの対応表.AutoPlayOFF.ユーザヒット時処理, ヒット判定バーと発声との時間sec );
+
+                                                       } );
+                                               }
+                                               //----------------
+                                               #endregion
+
+                                               // 入力(2) アプリ操作
+
                                                if( App.入力管理.キーボードデバイス.キーが押された( 0, Key.Escape ) )
                                                {
                                                        #region " ESC → ステージキャンセル "
@@ -378,130 +441,78 @@ namespace SST.ステージ.演奏
                                                        //----------------
                                                        #endregion
                                                }
+                                       }
+                                       //----------------
+                                       #endregion
+                                       break;
+                       }
+               }
 
-                                               #region " ユーザヒット処理。"
-                                               //----------------
-                                               foreach( var 入力 in App.入力管理.ポーリング結果 )
-                                               {
-                                                       if( 入力.InputEvent.離された )
-                                                               continue;   // 押下イベントじゃないなら無視。
-
-                                                       this._ドラムセット.ヒットアニメ開始( 入力.Type, App.ユーザ管理.選択されているユーザ.オプション設定.表示レーンの左右 );
-                                               }
-
-                                               var 処理済み入力 = new List<ドラム入力イベント>(); // ヒット処理が終わった入力は、二重処理しないよう、この中に追加しておく。
-
-                                               this._描画範囲のチップに処理を適用する( 現在の演奏時刻sec, ( chip, index, ヒット判定バーとの時間sec, ヒット判定バーとの距離 ) => {
-
-                                                       var オプション設定 = App.ユーザ管理.選択されているユーザ.オプション設定;
-                                                       var 対応表 = オプション設定.ドラムとチップと入力の対応表[ chip.チップ種別 ];
-                                                       bool AutoPlay = オプション設定.AutoPlay[ 対応表.AutoPlay種別 ];
-                                                       double ヒット判定バーとの時間の絶対値sec = Math.Abs( ヒット判定バーとの時間sec );
-                                                       bool チップはヒット済みである = chip.ヒット済みである;
-                                                       bool チップはMISSエリアに達している = ( ヒット判定バーとの時間sec > オプション設定.最大ヒット距離sec[ ヒットランク種別.POOR ] );
-                                                       bool チップはヒット可能エリアにある = ( ヒット判定バーとの時間sec >= -( オプション設定.最大ヒット距離sec[ ヒットランク種別.POOR ] ) && !チップはMISSエリアに達している );
-
-                                                       if( AutoPlay || チップはヒット済みである || !( 対応表.AutoPlayOFF.ユーザヒット ) || !( チップはヒット可能エリアにある ) )
-                                                               return;
-
-                                                       // チップにヒットする入力を探す。
-                                                       var ヒット入力 = App.入力管理.ポーリング結果.FirstOrDefault( ( 入力 ) => {
+               public override void 進行描画する( グラフィックデバイス gd )
+               {
+                       Debug.Assert( this.活性化している );
+                       Debug.Assert( null != gd );
 
-                                                               if( !( 入力.InputEvent.押された ) )
-                                                                       return false;   // 押下入力じゃない。
+                       switch( this.現在のフェーズ )
+                       {
+                               case フェーズ.演奏中:
+                                       if( this._活性化した直後である )
+                                               break;  // 進行処理がまだ行われていない。
 
-                                                               if( 処理済み入力.Contains( 入力 ) )
-                                                                       return false;   // すでに今回のターンで処理済み(=処理済み入力リストに追加済み)。
+                                       #region " 譜面スクロール速度が変化している → 追い付き進行 "
+                                       //----------------
+                                       {
+                                               double 倍率 = this._現在進行描画中の譜面スクロール速度の倍率;
 
-                                                               if( 対応表.シンバルフリーの対象 && オプション設定.シンバルフリー )
-                                                               {
-                                                                       // (A) シンバルフリーの対象チップであり、かつシンバルフリーが ON である場合
-                                                                       var 入力の対応表 = オプション設定.ドラムとチップと入力の対応表.対応表.FirstOrDefault( ( kvp ) => ( kvp.Value.ドラム入力種別 == 入力.Type ) ).Value;        // 見つからなきゃバグだ
-                                                                       if( !( 入力の対応表.シンバルフリーの対象 ) )
-                                                                               return false;   // この入力はシンバルフリーの対象ではない。
-                                                               }
-                                                               else
+                                               if( 倍率 < App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 )
+                                               {
+                                                       if( 0 > this._スクロール倍率追い付き用_最後の値 )
+                                                       {
+                                                               this._スクロール倍率追い付き用カウンタ = new LoopCounter( 0, 1000, 10 );    // 0→100; 全部で10×1000 = 10000ms = 10sec あれば十分だろう
+                                                               this._スクロール倍率追い付き用_最後の値 = 0;
+                                                       }
+                                                       else
+                                                       {
+                                                               while( this._スクロール倍率追い付き用_最後の値 < this._スクロール倍率追い付き用カウンタ.現在値 )
                                                                {
-                                                                       // (B) シンバルフリーの対象ではないチップ、またはシンバルフリーが OFF である場合
-                                                                       if( 対応表.ドラム入力種別 != 入力.Type )
-                                                                               return false;   // チップに対応している入力じゃない。
+                                                                       倍率 += 0.025;
+                                                                       this._スクロール倍率追い付き用_最後の値++;
                                                                }
 
-                                                               return true;    // この 入力 はこの chip にヒットしている。
-                                                       } );
-
-                                                       if( null == ヒット入力 )
-                                                               return;
-
-                                                       処理済み入力.Add( ヒット入力 );
-
-                                                       // ヒットランクを判定する。
-
-                                                       var ヒットランク = ヒットランク種別.POOR;
-
-                                                       if( ヒット判定バーとの時間の絶対値sec <= オプション設定.最大ヒット距離sec[ ヒットランク種別.PERFECT ] )
-                                                       {
-                                                               ヒットランク = ヒットランク種別.PERFECT;
+                                                               this._現在進行描画中の譜面スクロール速度の倍率 =
+                                                                       Math.Min( 倍率, App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 );
                                                        }
-                                                       else if( ヒット判定バーとの時間の絶対値sec <= オプション設定.最大ヒット距離sec[ ヒットランク種別.GREAT ] )
+                                               }
+                                               else if( 倍率 > App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 )
+                                               {
+                                                       if( 0 > this._スクロール倍率追い付き用_最後の値 )
                                                        {
-                                                               ヒットランク = ヒットランク種別.GREAT;
+                                                               this._スクロール倍率追い付き用カウンタ = new LoopCounter( 0, 1000, 10 );    // 0→100; 全部で10×1000 = 10000ms = 10sec あれば十分だろう
+                                                               this._スクロール倍率追い付き用_最後の値 = 0;
                                                        }
-                                                       else if( ヒット判定バーとの時間の絶対値sec <= オプション設定.最大ヒット距離sec[ ヒットランク種別.GOOD ] )
+                                                       else
                                                        {
-                                                               ヒットランク = ヒットランク種別.GOOD;
-                                                       }
-
-                                                       // ヒット処理。
-
-                                                       this._チップのヒット処理を行う( chip, ヒットランク, 対応表.AutoPlayOFF.ユーザヒット時処理 );
-
-                                               } );
-                                               //----------------
-                                               #endregion
-                                       }
-                                       //----------------
-                                       #endregion
-                                       break;
-
-                               case フェーズ.クリア時フェードアウト:
-                                       #region " *** "
-                                       //----------------
-                                       if( this._クリア時フェードアウト.開始されていない )
-                                       {
-                                               this._クリア時フェードアウト.開始する();
-                                       }
-                                       else
-                                       {
-                                               this._クリア時フェードアウト.進行する();
+                                                               while( this._スクロール倍率追い付き用_最後の値 < this._スクロール倍率追い付き用カウンタ.現在値 )
+                                                               {
+                                                                       倍率 -= 0.025;
+                                                                       this._スクロール倍率追い付き用_最後の値++;
+                                                               }
 
-                                               if( this._クリア時フェードアウト.完了した )
+                                                               this._現在進行描画中の譜面スクロール速度の倍率 =
+                                                                       Math.Max( 倍率, App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 );
+                                                       }
+                                               }
+                                               else
                                                {
-                                                       this.現在のフェーズ = フェーズ.クリア;
+                                                       this._スクロール倍率追い付き用_最後の値 = -1;
+                                                       this._スクロール倍率追い付き用カウンタ = null;
                                                }
                                        }
                                        //----------------
                                        #endregion
-                                       break;
-
-                               case フェーズ.Failed:
-                               case フェーズ.クリア:
-                               case フェーズ.キャンセル:
-                               case フェーズ.ビュアーメッセージ待機:
-                                       break;
-                       }
-               }
-
-               public override void 描画する( グラフィックデバイス gd )
-               {
-                       Debug.Assert( this.活性化している );
-                       Debug.Assert( null != gd );
 
-                       switch( this.現在のフェーズ )
-                       {
-                               case フェーズ.演奏中:
-                                       if( this._活性化した直後である )
-                                               break;  // 進行処理がまだ行われていない。
+                                       this._コンボ.進行描画する( gd );
+                                       this._回転羽.進行描画する( gd );
 
                                        double 演奏時刻sec = this._演奏開始からの経過時間secを返す();
 
@@ -551,25 +562,36 @@ namespace SST.ステージ.演奏
                                        #endregion
 
                                        this._ステージ台.描画する( gd, 0f, 0f );
-                                       this._レーンフレーム.描画する( gd, レーンフレームの左端位置 );
-                                       this._コンボ.描画する( gd );
-                                       this._ヒットランク.描画する( gd, レーンフレームの左端位置 );
+                                       this._レーンフレーム.進行描画する( gd, レーンフレームの左端位置 );
+                                       this._コンボ.進行描画する( gd );
+                                       this._ヒットランク.進行描画する( gd, レーンフレームの左端位置 );
                                        this._小節線拍線を描画する( gd, 演奏時刻sec );
                                        this._ヒット判定バー.描画する( gd, 597f, ヒット判定バーの中央Y座標 - 43f );
-                                       this._ドラムセット.描画する( gd );
+                                       this._ドラムセット.進行描画する( gd );
                                        this._チップを描画する( gd, 演奏時刻sec );
-                                       this._回転羽.描画する( gd );
+                                       this._回転羽.進行描画する( gd );
                                        this._FPS.VPSをカウントする();
                                        this._FPS.描画する( gd, 0f, 0f );
                                        break;
 
                                case フェーズ.クリア時フェードアウト:
+                                       if( this._クリア時フェードアウト.開始されていない )
+                                       {
+                                               this._クリア時フェードアウト.開始する();
+                                       }
+                                       else
+                                       {
+                                               if( this._クリア時フェードアウト.完了した )
+                                               {
+                                                       this.現在のフェーズ = フェーズ.クリア;
+                                               }
+                                       }
                                        this._ステージ台.描画する( gd, 0f, 0f );
-                                       this._レーンフレーム.描画する( gd, レーンフレームの左端位置 );
+                                       this._レーンフレーム.進行描画する( gd, レーンフレームの左端位置 );
                                        this._ヒット判定バー.描画する( gd, 597f, ヒット判定バーの中央Y座標 - 43f );
-                                       this._ドラムセット.描画する( gd );
-                                       this._回転羽.描画する( gd );
-                                       this._クリア時フェードアウト.描画する( gd, this._白パネル );
+                                       this._ドラムセット.進行描画する( gd );
+                                       this._回転羽.進行描画する( gd );
+                                       this._クリア時フェードアウト.進行描画する( gd, this._白パネル );
                                        this._FPS.VPSをカウントする();
                                        this._FPS.描画する( gd, 0f, 0f );
                                        break;
@@ -622,8 +644,6 @@ namespace SST.ステージ.演奏
                }
 
 
-               private double _演奏開始時刻sec = 0.0;
-
                private double _現在進行描画中の譜面スクロール速度の倍率 = 1.0;
 
                /// <summary>
@@ -694,6 +714,7 @@ namespace SST.ステージ.演奏
 
                private bool _活性化した直後である;
 
+
                // 譜面描画。
 
                /// <summary>
@@ -765,7 +786,7 @@ namespace SST.ステージ.演奏
                                        double 再生開始時刻sec = ( 時刻sec - 背景動画チップ.発声時刻sec );
                                        this._背景動画?.再生を開始する( 再生開始時刻sec );
                                        this._背景動画開始済み = true;
-                                       this._BGM?.Play( 再生開始時刻sec );
+                                       this._BGM?.Play( 再生開始時刻sec - App.サウンドデバイス.遅延sec );
                                        this._BGM再生開始済み = true;
                                        Log.Info( $"背景動画の再生を開始しました。(再生開始時刻: {再生開始時刻sec} sec)" );
                                }
@@ -778,7 +799,7 @@ namespace SST.ステージ.演奏
 
                private double _演奏開始からの経過時間secを返す()
                {
-                       return App.ã\82µã\82¦ã\83³ã\83\89ã\83\87ã\83\90ã\82¤ã\82¹.GetDevicePosition() - this._æ¼\94å¥\8fé\96\8bå§\8b時刻sec;
+                       return App.ã\82µã\82¦ã\83³ã\83\89ã\82¿ã\82¤ã\83\9e\8f¾å\9c¨時刻sec;
                }
 
                // 小節線・拍線 と チップ は描画階層(奥行き)が異なるので、別々のメソッドに分ける。
@@ -790,7 +811,7 @@ namespace SST.ステージ.演奏
 
                        this._チップ画像.加算合成 = false;
 
-                       this._描画範囲のチップに処理を適用する( 現在の演奏時刻sec, ( chip, index, ヒット判定バーとの時間sec, ヒット判定バーとの距離 ) => {
+                       this._描画範囲のチップに処理を適用する( 現在の演奏時刻sec, ( chip, index, ヒット判定バーと描画との時間sec, ヒット判定バーと発声との時間sec, ヒット判定バーとの距離 ) => {
 
                                if( chip.チップ種別 == チップ種別.小節線 )
                                {
@@ -831,7 +852,7 @@ namespace SST.ステージ.演奏
 
                        this._チップ画像.加算合成 = false;
 
-                       this._描画範囲のチップに処理を適用する( 現在の演奏時刻sec, ( chip, index, ヒット判定バーとの時間sec, ヒット判定バーとの距離 ) => {
+                       this._描画範囲のチップに処理を適用する( 現在の演奏時刻sec, ( chip, index, ヒット判定バーと描画との時間sec, ヒット判定バーと発声との時間sec, ヒット判定バーとの距離 ) => {
 
                                float 縦中央位置 = (float)( ヒット判定バーの中央Y座標 + ヒット判定バーとの距離 );
 
@@ -1029,7 +1050,7 @@ namespace SST.ステージ.演奏
                ///             <see cref="_描画開始チップ番号"/> から画面上端にはみ出すまでの間の各チップに対して、指定された処理を適用する。
                /// </summary>
                /// <param name="適用する処理">引数は、順に、対象のチップ、チップ番号、ヒット判定バーとの時間sec、ヒット判定バーとの距離</param>
-               private void _描画範囲のチップに処理を適用する( double 現在の演奏時刻sec, Action<チップ, int, double, double> 適用する処理 )
+               private void _描画範囲のチップに処理を適用する( double 現在の演奏時刻sec, Action<チップ, int, double, double, double> 適用する処理 )
                {
                        var スコア = App.演奏スコア;
                        if( null == スコア )
@@ -1040,8 +1061,9 @@ namespace SST.ステージ.演奏
                                var チップ = スコア.チップリスト[ i ];
 
                                // ヒット判定バーとチップの間の、時間 と 距離 を算出。→ いずれも、負数ならバー未達、0でバー直上、正数でバー通過。
-                               double ヒット判定バーとの時間sec = 現在の演奏時刻sec - チップ.描画時刻sec;
-                               double ヒット判定バーとの距離 = スコア.指定された時間secに対応する符号付きピクセル数を返す( this._現在進行描画中の譜面スクロール速度の倍率, ヒット判定バーとの時間sec );
+                               double ヒット判定バーと描画との時間sec = 現在の演奏時刻sec - チップ.描画時刻sec;
+                               double ヒット判定バーと発声との時間sec = 現在の演奏時刻sec - チップ.発声時刻sec;
+                               double ヒット判定バーとの距離 = スコア.指定された時間secに対応する符号付きピクセル数を返す( this._現在進行描画中の譜面スクロール速度の倍率, ヒット判定バーと描画との時間sec );
 
                                // 終了判定。
                                bool チップは画面上端より上に出ている = ( ( ヒット判定バーの中央Y座標 + ヒット判定バーとの距離 ) < -40.0 );   // -40 はチップが隠れるであろう適当なマージン。
@@ -1049,33 +1071,20 @@ namespace SST.ステージ.演奏
                                        break;
 
                                // 処理実行。開始判定(描画開始チップ番号の更新)もこの中で。
-                               適用する処理( チップ, i, ヒット判定バーとの時間sec, ヒット判定バーとの距離 );
+                               適用する処理( チップ, i, ヒット判定バーと描画との時間sec, ヒット判定バーと発声との時間sec, ヒット判定バーとの距離 );
                        }
                }
 
-               private void _チップのヒット処理を行う( チップ chip, ヒットランク種別 hitRankType, ドラムとチップと入力の対応表.Column.Columnヒット処理 ヒット処理表 )
+               private void _チップのヒット処理を行う( チップ chip, ヒットランク種別 hitRankType, ドラムとチップと入力の対応表.Column.Columnヒット処理 ヒット処理表, double ヒット判定バーと発声との時間sec )
                {
                        chip.ヒット済みである = true;
 
                        if( ヒット処理表.再生 )
                        {
-                               #region " ã\83\81ã\83\83ã\83\97ã\82\92å\86\8dç\94\9fã\81\99ã\82\8b。"
+                               #region " ã\83\81ã\83\83ã\83\97ã\81®ç\99ºå£°ã\82\92è¡\8cã\81\86。"
                                //----------------
-                               if( chip.チップ種別 == チップ種別.背景動画 )
-                               {
-                                       // 背景動画の再生を開始する。
-                                       this._背景動画?.再生を開始する();
-                                       this._背景動画開始済み = true;
-
-                                       // BGMの再生を開始する。
-                                       this._BGM?.Play();
-                                       this._BGM再生開始済み = true;
-                               }
-                               else
-                               {
-                                       if( App.システム設定.Autoチップのドラム音を再生する )
-                                               this._ドラムサウンド.発声する( chip.チップ種別, ( chip.音量 / (float) チップ.最大音量 ) );
-                               }
+                               if( chip.発声されていない )
+                                       this._チップの発声を行う( chip, ヒット判定バーと発声との時間sec );
                                //----------------
                                #endregion
                        }
@@ -1097,7 +1106,9 @@ namespace SST.ステージ.演奏
                                                        ヒット判定バーの中央Y座標 ) );
 
                                        this._ドラムセット.ヒットアニメ開始( 対応表.ドラム入力種別, App.ユーザ管理.選択されているユーザ.オプション設定.表示レーンの左右 );
-                                       this._レーンフレーム.フラッシュ開始( 対応表.表示レーン種別 );
+
+                                       if( hitRankType == ヒットランク種別.AUTO )
+                                               this._レーンフレーム.フラッシュ開始( 対応表.表示レーン種別 );   // レーンフラッシュは Auto 時のみ。
 
                                        this._ヒットランク.表示開始( chip.チップ種別, hitRankType );
                                        this.ヒットランク別ヒット回数[ hitRankType ]++;
@@ -1131,5 +1142,34 @@ namespace SST.ステージ.演奏
                                #endregion
                        }
                }
+
+               private void _チップの発声を行う( チップ chip, double 再生開始位置sec )
+               {
+                       if( chip.発声済みである )
+                               return;
+
+                       chip.発声済みである = true;
+
+                       if( chip.チップ種別 == チップ種別.背景動画 )
+                       {
+                               App.サウンドタイマ.一時停止する();
+
+                               // 背景動画の再生を開始する。
+                               this._背景動画?.再生を開始する();
+                               this._背景動画開始済み = true;
+
+                               // BGMの再生を開始する。
+                               this._BGM?.Play( 再生開始位置sec );
+                               this._BGM再生開始済み = true;
+
+                               App.サウンドタイマ.再開する();
+                       }
+                       else
+                       {
+                               // BGM以外のサウンドについては、再生開始位置sec は反映せず、常に最初から再生する。
+                               if( App.システム設定.Autoチップのドラム音を再生する )
+                                       this._ドラムサウンド.発声する( chip.チップ種別, ( chip.音量 / (float) チップ.最大音量 ) );
+                       }
+               }
        }
 }