using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using FDK;

namespace StrokeStyleT
{
	class C演奏ステージ : CActivity
	{
		public int n描画開始チップ番号
		{
			get
			{
				lock( this )
				{
					return this._n描画開始チップ番号;
				}
			}
			set
			{
				lock( this )
				{
					this._n描画開始チップ番号 = value;
				}
			}
		}
		public Dictionary<Eヒット判定, int> dicヒットした回数
		{
			get
			{
				lock( this )
				{
					return this._dicヒットした回数;
				}
			}
			protected set
			{
				lock( this )
				{
					this._dicヒットした回数 = value;
				}
			}
		}
		public bool b演奏終了済み
		{
			get
			{
				lock( this )
				{
					return ( this._n描画開始チップ番号 < 0 );
				}
			}
		}
		public Song Song;	// これを演奏する。

		public C演奏ステージ()
		{
			this._n描画開始チップ番号 = -1;
			this._dicヒットした回数 = null;

			this.list子Activities.Add( this.act背景 = new CAct背景() );
			this.list子Activities.Add( this.actステージキット = new CActステージキット() );
			this.list子Activities.Add( this.actドラムキット = new CActドラムキット() );
			this.list子Activities.Add( this.actフリップボード = new CMActフリップボード( D3DFormat.A8R8G8B8 ) );
		}

		public E演奏レーン tチップが属する演奏レーンを返す( Eチップ種別 eチップ )
		{
			lock( this )
			{
				foreach( var kvp in C演奏ステージ.dic演奏レーンtoチップ )
				{
					if( kvp.Value.Contains( eチップ ) )
						return kvp.Key;
				}
				return E演奏レーン.Unknown;
			}
		}
		public E演奏レーン tチップが属する演奏レーンを返す( Cチップ cc )
		{
			return this.tチップが属する演奏レーンを返す( cc.eチップ種別 );
		}

		protected override void Onデバイスリソースの作成()
		{
			lock( this )
			{
				Global.tデバイスをロックして処理を行う( ( hLockedDevice ) => {

					this.txドラムチップ = new CTexture( hLockedDevice, Path.Combine( Global.Folders.stgシステムフォルダパス, @"Images\Drum Chips.png" ) );
					this.txスコアフレーム = new CTexture( hLockedDevice, Path.Combine( Global.Folders.stgシステムフォルダパス, @"Images\ScreenPlay ScoreFrame.png" ) );
					this.txスコアフレーム.n透明度 = Global.Users.SelectedUser.Options.n譜面ボード透明度;
					this.tx判定バー = new CTexture( hLockedDevice, Path.Combine( Global.Folders.stgシステムフォルダパス, @"Images\ScreenPlay HitBar.png" ) );

				} );

				Cチップ.t全チップのデバイスリソースの作成();
			}
		}
		protected override void Onデバイスリソースの解放()
		{
			lock( this )
			{
				if( null != this.txドラムチップ )
				{
					this.txドラムチップ.Dispose();
					this.txドラムチップ = null;
				}
				if( null != this.txスコアフレーム )
				{
					this.txスコアフレーム.Dispose();
					this.txスコアフレーム = null;
				}
				if( null != this.tx判定バー )
				{
					this.tx判定バー.Dispose();
					this.tx判定バー = null;
				}

				Cチップ.t全チップのデバイスリソースの解放();

				base.Onデバイスリソースの解放();
			}
		}
		protected override void On活性化()
		{
			lock( this )
			{
				this._n描画開始チップ番号 = 0;
				this._dicヒットした回数 = new Dictionary<Eヒット判定, int>();

				foreach( var obj in Enum.GetValues( typeof( Eヒット判定 ) ) )
					this._dicヒットした回数.Add( (Eヒット判定) obj, 0 );

				this.b背景動画チップの発火を待たずに最初の進行で背景動画の再生を開始する = false;

				this.ctチップアニメ = new CCounter( 0, 48, 10 );

				User user = Global.Users.SelectedUser;
				//this.Song = new Song( user.Songs.SelectedSong );		// Global の曲読み込みステージで複製＆生成済み。
				Options options = user.Options;

				#region [ 背景動画を生成。]
				//-----------------
				if( null != this.Song )
				{
					try
					{
						this.Song.ms背景動画 = new CMediaSession( Program.App.hWindow, Program.App.dwMediaSessionスレッドID, this.Song.stg背景動画ファイルパス, b音声あり: false, bループする: false );
						Trace.TraceInformation( "動画を生成しました。({0})", Path.GetFileName( this.Song.stg背景動画ファイルパス ) );

						// あらかじめ再生開始処理を行い、一時停止しておく。（譜面上での動画再生時のブロックを削減するため。）
						this.Song.ms背景動画.t再生する( 0 );
						this.Song.ms背景動画.t再生処理が完了するまで待機する();
						this.Song.ms背景動画.t一時停止する();
					}
					catch( Exception e )
					{
						Trace.TraceWarning( "動画の生成に失敗しました。({0})", Global.Folders.tファイルパスをマクロ付きパスに変換する( this.Song.stg背景動画ファイルパス ) );
						Utils.t例外の詳細をログに出力する( e );
						this.Song.ms背景動画 = null;
					}
				}
				//-----------------
				#endregion

				#region [ 各チップの発声時刻に、再生遅延を反映。]
				//-----------------
				int n再生遅延ms = 0;	// 描画されるチップよりどれだけ先に発声するか？（例: n再生遅延ms = 10 → チップが判定バー上に描画されるより 10ms 前に発声する。）

				int n作成時のバッファ長bytes = this.Song.n作成時のWASAPIバッファ長bytes;
				if( 0 < n作成時のバッファ長bytes )		// 記載されていて、かつ、0ms を超える値である場合に（他の値も）有効値と見なす。
				{
					#region [ 相対的な再生遅延量を計算する。]
					//-----------------
					var sound = Global.Sound;

					int n作成時のバッファ長ms = ( n作成時のバッファ長bytes * 1000 ) / ( this.Song.n作成時のWASAPIサンプリングバイト数 * this.Song.n作成時のWASAPI周波数Hz * this.Song.n作成時のWASAPIチャンネル数 );
					int n演奏時のバッファ長ms = ( sound.nバッファ長bytes * 1000 ) / ( sound.nサンプリングバイト数 * sound.nサンプリング周波数Hz * sound.nチャンネル数 );

					n再生遅延ms = n演奏時のバッファ長ms - n作成時のバッファ長ms;
					//-----------------
					#endregion

					#region [ BGM の発声位置を n再生遅延ms 前に修正する。]
					//-----------------
					if( null != this.Song.sdBGM )
					{
						if( this.b背景動画チップの発火を待たずに最初の進行で背景動画の再生を開始する )
						{
							#region [ (A) 既に背景動画チップがヒット処理されている場合 → 動画とBGMの再生開始位置をずらす ]
							//-----------------
							long n新しい再生開始位置ms = Math.Max( this.Song.ms背景動画.n現在のグラフの再生位置ms - n再生遅延ms, 0 );	// 再生位置が負数になった場合は 0 とする。

							this.Song.ms背景動画.t再生する( (ulong) ( n新しい再生開始位置ms * 1000 * 10 ) );	// 100ns 単位で指定。
							this.Song.sdBGM.t再生位置を変更する( n新しい再生開始位置ms );
							//-----------------
							#endregion
						}
						else
						{
							#region [ (B) まだ背景動画チップがヒット処理されていない場合 → 背景動画チップの発声時刻をずらす ]
							//-----------------
							foreach( var chip in this.Song.listチップ )
							{
								if( Eチップ種別.背景動画 == chip.eチップ種別 )
								{
									// 発声時刻をずらす。

									chip.n発声時刻ms -= n再生遅延ms;


									// 発声時刻が負数になった場合は、プレイヤーモードと同じ方法で、いきなり途中から再生を開始する準備をする。

									if( 0 > chip.n発声時刻ms )
									{
										long n超過分ms = -chip.n発声時刻ms;

										this.Song.ms背景動画.t再生する( (ulong) ( n超過分ms * 1000 * 10 ) );
										this.Song.sdBGM.t再生位置を変更する( n超過分ms );

										this.b背景動画チップの発火を待たずに最初の進行で背景動画の再生を開始する = true;

										chip.bヒット済み = true;	// 既にヒットしたことになる。
										chip.b可視 = false;
									}

									break;
								}
							}
							//-----------------
							#endregion
						}
					}
					//-----------------
					#endregion

					#region [ 自動演奏 ON の場合、ドラムチップの発声時刻を修正する。]
					//-----------------
					if( ( null != this.Song ) && options.b自動演奏 )
					{
						foreach( var chip in this.Song.listチップ )
						{
							if( 0 < Cチップ.dicチップ種別toドラム入力[ chip.eチップ種別 ].Count )
							{
								chip.n発声時刻ms -= n再生遅延ms;
								break;
							}
						}
					}
					//-----------------
					#endregion
				}
				else
				{
					Trace.TraceWarning( "この曲データには、サウンドデバイス情報の記載が不足しています。演奏時に音ズレが発生する恐れがあります。" );
				}
				//-----------------
				#endregion
			}
		}
		protected override void On非活性化()
		{
			lock( this )
			{
				if( null != this.Song )
				{
					this.Song.Dispose();
					this.Song = null;
				}
				if( null != this.FPS )
				{
					this.FPS.Dispose();
					this.FPS = null;
				}
				if( null != this.VPS )
				{
					this.VPS.Dispose();
					this.VPS = null;
				}
				if( null != ctチップアニメ )
				{
					this.ctチップアニメ.Dispose();
					this.ctチップアニメ = null;
				}
			}
		}
		protected override int On進行()
		{
			lock( this )
			{
				if( this.b活性化してない )
					return (int) E進行結果.完了;

				// タイマの更新と描画開始チップの決定。

				User user = Global.Users.SelectedUser;

				#region [ 初めての進行（演奏開始）]
				//-----------------
				if( this.b初めての進行 )
				{
					// 指示があれば背景動画を再生開始。

					if( this.b背景動画チップの発火を待たずに最初の進行で背景動画の再生を開始する )
					{
						// 背景動画を再生開始。BGMも背景動画に同期して再生開始する。
						this.Song.ms背景動画.t再生する( 0, this.Song.sdBGM );
					}


					// 現在蓄積されているバッファ入力をすべてポーリング（＝クリア）する。

					Global.Input.tポーリング();


					// FPS/VPS 測定開始。

					this.FPS = new CFPS();
					this.VPS = new CFPS();


					// 演奏タイマをリセットする。
					// ・プレイヤーモード時は途中から開始されることが多いので、this.nプレイヤーモード時の演奏開始時刻ms を加算する。（通常演奏では 0）。
					// ・また、チップ発声時刻シフト値（Global.Global.Song.r現在演奏中の曲のスコア.r環境依存プロパティ.nチップ発声時刻シフト値ms）を加算する必要は無い。
					//　（加算は Act演奏チップ にて行われるため。）

					Global.SoundTimer.tリセット();
					//CApplication.SoundTimer.n現在時刻ms = this.nプレイヤーモード時の演奏開始時刻ms;


					this.b初めての進行 = false;
				}
				//-----------------
				#endregion

				#region [ 譜面データ自体空っぽならステージクリア。]
				//-----------------
				if( ( null != this.Song.listチップ ) && ( 0 == this.Song.listチップ.Count ) )
				{
					this.n描画開始チップ番号 = -1;
					return (int) E進行結果.クリア;
				}
				//-----------------
				#endregion
				#region [ タイマ更新。]
				//-----------------
				Global.SoundTimer.t更新();
				//-----------------
				#endregion
				#region [ 描画開始チップ交代？]
				//-----------------
				if( 0 <= this._n描画開始チップ番号 )
				{
					int nヒットバーとチップの距離符号付きpx =
						this.n指定された時間msに対応する符号付きピクセル数を返す( Global.SoundTimer.n現在時刻ms - this.Song.listチップ[ this._n描画開始チップ番号 ].n描画時刻ms );

					int nチップのY座標 =
						Theme.sz描画サイズ原寸px.Height - Theme.n画面下端からヒットバー中央までの距離px + nヒットバーとチップの距離符号付きpx;


					// チップのY座標が画面下外に出ていれば、描画開始チップ番号を１つ後ろのチップに交代する。後ろのチップがなければ -1（演奏終了）。

					if( nチップのY座標 > Theme.sz描画サイズ原寸px.Height + 20 )	// +20 は適当なマージン
					{
						this._n描画開始チップ番号++;

						if( this._n描画開始チップ番号 >= this.Song.listチップ.Count )
							this._n描画開始チップ番号 = -1;		// 演奏終了。
					}
				}
				//-----------------
				#endregion
				#region [ 譜面が終了したならステージクリア。]
				//-----------------
				if( 0 > this._n描画開始チップ番号 )
					return (int) E進行結果.クリア;
				//-----------------
				#endregion

				#region [ FPS 更新 ]
				//-----------------
				this.FPS.tカウンタ更新();
				//-----------------
				#endregion
				#region [ 背景動画 ]
				//-----------------
				if( ( null != this.Song.chip背景動画 ) && this.Song.chip背景動画.bヒットしてない )
				{
					// n距離px = ヒットバーとチップの距離[pixel;符号付き]を算出する。
					// ・背景動画チップにはチップシフトは反映されない。
					// ・背景動画チップの再生遅延は、チップを移動することで既に反映済み（On活性化()参照）。

					int n距離px = this.n指定された時間msに対応する符号付きピクセル数を返す( Global.SoundTimer.n現在時刻ms - this.Song.chip背景動画.n発声時刻ms );


					// 背景動画チップのヒット判定と処理。

					if( 0 <= n距離px )
					{
						this.Song.ms背景動画.t再生を再開する();		// 動画開始; On活性化で一時停止状態（この場所で CMediaSessiont再生する() を呼び出すと、再生開始が完了するまでブロックされるため。）
						this.Song.sdBGM.t先頭から再生する();		// 音声開始

						this.Song.chip背景動画.bヒット済み = true;
						this.Song.chip背景動画.b可視 = false;
					}
				}
				//-----------------
				#endregion
				#region [ 譜面 ]
				//-------------------------
				this.ctチップアニメ.t進行Loop();

				for( int i = this.n描画開始チップ番号; i < this.Song.listチップ.Count; i++ )
				{
					var cc = this.Song.listチップ[ i ];

					#region [ チップが発声対象じゃないならスキップ。]
					//-----------------
					if( cc.eチップ種別 == Eチップ種別.BPM ||
						cc.eチップ種別 == Eチップ種別.背景動画 ||
						cc.eチップ種別 == Eチップ種別.小節線 ||
						cc.eチップ種別 == Eチップ種別.拍線 ||
						cc.eチップ種別 == Eチップ種別.小節メモ )
						continue;
					//-----------------
					#endregion


					// n描画時間差ms, n発声時間差ms を算出。

					long n描画時間差ms = Global.SoundTimer.n現在時刻ms - cc.n描画時刻ms;
					long n発声時間差ms = Global.SoundTimer.n現在時刻ms - cc.n発声時刻ms;


					#region [ 描画時間差 が PoorRange を正方向に超えているなら Miss を発火。]
					//-------------------------
					if( cc.eチップ種別 != Eチップ種別.BPM &&
						cc.eチップ種別 != Eチップ種別.背景動画 &&
						cc.eチップ種別 != Eチップ種別.小節メモ &&
						cc.bヒットしてない &&
						n描画時間差ms > user.Options.dicヒット判定toRange[ Eヒット判定.POOR ] )
					{
						if( cc.eチップ種別 == Eチップ種別.SnareGhost ||
							cc.eチップ種別 == Eチップ種別.FootPedal )
						{
							// スネアゴーストとフットペダルについては、Miss 判定を行わない。
						}
						else
						{
							// 判定文字列(MISS)表示。
							//this.Act演奏判定文字列.t表示( this.tチップが属する演奏レーンを返す( cc ), Eヒット判定.MISS );S

							// COMBOリセット。
							//this.Act演奏コンボ.nCOMBO値 = 0;
						}
						cc.bヒット済み = true;
					}
					//-------------------------
					#endregion


					// 判定バーとチップの距離（符号付き）を算出する。

					int n描画距離px = this.n指定された時間msに対応する符号付きピクセル数を返す( n描画時間差ms );
					int n発声距離px = this.n指定された時間msに対応する符号付きピクセル数を返す( n発声時間差ms );


					// チップのY座標を算出する。y が画面上外に出ていればそこでループ終了。

					int y = Theme.sz描画サイズ原寸px.Height - Theme.n画面下端からヒットバー中央までの距離px + n描画距離px;
					if( y < -20 )	// -20 は適当なマージン
						break;


					// オートチップの自動発声処理。

					if( ( user.Options.b自動演奏 ) && ( Cチップ.dicチップ種別toドラム入力[ cc.eチップ種別 ].Count > 0 ) )
					{
						if( ( cc.b発声してない ) && ( 0 <= n発声距離px ) )
						{
							// 発声する。

							Cチップ.tチップサウンドを再生する( cc.eチップ種別, 36 + cc.n音量 * 16 );		// 音量は、 Cチップの 小:1～4:大 を CSound の 小:52～100:大 くらいに変換。
							cc.b発声済み = true;
						}

						if( ( cc.bヒットしてない ) && ( 0 <= n描画距離px ) )
						{
							// ヒット処理をする。

							if( cc.eチップ種別 != Eチップ種別.SnareGhost && cc.eチップ種別 != Eチップ種別.FootPedal ) // Snare_Ghost と FootPedal はヒット対象としない。Missにもならない。
							{
								//this.Act演奏パッド.tヒット( lane );					// パッドアニメ開始。
								//this.Act演奏チップファイア.t発火( lane, 0 );		// チップファイア発火。
								//if( this.Act演奏ビッグファイア.b発火する( cc ) )
								//	this.Act演奏ビッグファイア.t発火();				// ビッグファイア発火。

								//this.Act演奏判定文字列.t表示( lane, E判定.AUTO );	// 判定文字列(AUTO)表示。
								//Global.Stage.演奏.dicヒットした回数[ E判定.AUTO ]++;// 判定別ヒット回数の増加。
								//if( Global.User.Config.b全オートである )
								//	this.Act演奏コンボ.nCOMBO値++;					// 全AUTOのときだけCOMBO増加。
							}

							cc.bヒット済み = true;
							cc.b不可視 = true;
						}
					}
				}
				//-------------------------
				#endregion
				#region [ フリップボード ]
				//-------------------------
				this.actフリップボード.On進行();
				//-------------------------
				#endregion


				// 入力。

				#region [ ESC → キャンセル ]
				//-----------------
				if( Global.Input.Keyboard.bキーが押された( Key.Escape ) )
					return (int) E進行結果.キャンセル;
				//-----------------
				#endregion

				#region [ F1,F2 → 譜面ボード透明度Down,Up]
				//-----------------
				if( Global.Input.Keyboard.bキーが押された( Key.F1 ) )
				{
					this.txスコアフレーム.n透明度 = Math.Min( Math.Max( ( this.txスコアフレーム.n透明度 - 10 ), 5 ), 255 );	// 5～255まで10ずつ Down
					Global.Users.SelectedUser.Options.n譜面ボード透明度 = this.txスコアフレーム.n透明度;

					this.actフリップボード.t表示( "譜面ボード不透明度: " + (this.txスコアフレーム.n透明度/255.0).ToString("##0.0 %") );
				}

				if( Global.Input.Keyboard.bキーが押された( Key.F2 ) )
				{
					this.txスコアフレーム.n透明度 = Math.Min( Math.Max( ( this.txスコアフレーム.n透明度 + 10 ), 5 ), 255 );	// 5～255まで10ずつ Up
					Global.Users.SelectedUser.Options.n譜面ボード透明度 = this.txスコアフレーム.n透明度;

					this.actフリップボード.t表示( "譜面ボード不透明度: " + ( this.txスコアフレーム.n透明度 / 255.0 ).ToString( "##0.0 %" ) );
				}
				//-----------------
				#endregion


				return (int) E進行結果.継続;
			}
		}
		protected override void On描画( IntPtr hDevice )
		{
			lock( this )
			{
				if( this.b活性化してない )
					return;

				#region [ VPS 更新 ]
				//-----------------
				if( null != this.VPS )	// 曲が再生されるまでは、この描画メソッドは呼ばれるが VPS は生成されていない。
					this.VPS.tカウンタ更新();
				//-----------------
				#endregion

				#region [ 背景 ]
				//-----------------
				this.act背景.t描画( hDevice );
				//-----------------
				#endregion
				#region [ 背景動画 ]
				//-----------------
				if( ( null != this.Song.chip背景動画 ) && ( this.Song.chip背景動画.bヒット済み ) && ( null != this.Song.ms背景動画 ) )
				{
					// 表示位置とサイズは固定。
					this.Song.ms背景動画.t最新のサンプルを描画する( x: 447, y: 178, w: 1024, h: 576 );
				}
				//-----------------
				#endregion
				#region [ ステージキット ]
				//-----------------
				this.actステージキット.t描画( hDevice );
				//-----------------
				#endregion
				#region [ スコアフレーム ]
				//-----------------
				if( null != this.txスコアフレーム )
					this.txスコアフレーム.t2D描画( hDevice, 570, 0 );
				//-----------------
				#endregion
				#region [ 譜面（小節線、拍線）]
				//-------------------------
				for( int i = this.n描画開始チップ番号; i < this.Song.listチップ.Count; i++ )
				{
					var cc = this.Song.listチップ[ i ];

					#region [ チップが描画対象じゃないならスキップ。]
					//-----------------
					if( cc.eチップ種別 != Eチップ種別.小節線 &&
						cc.eチップ種別 != Eチップ種別.拍線 )
						continue;
					//-----------------
					#endregion


					// 時間差を算出する。

					long n描画時間差ms = Global.SoundTimer.n現在時刻ms - cc.n描画時刻ms;


					// ヒットバーとチップの距離[pixel;符号付き]を算出する。

					int n描画距離px = this.n指定された時間msに対応する符号付きピクセル数を返す( n描画時間差ms );


					// チップのY座標を算出する。y が画面上外に出ていればそこでループ終了。

					int y = Theme.sz描画サイズ原寸px.Height - Theme.n画面下端からヒットバー中央までの距離px + n描画距離px;
					if( y < -20 )	// -20 は適当なマージン
						break;


					// 描画。

					if( cc.b可視 )
					{
						// 画像は幅 300px しかないので、２つ横につなげて600pxにする。

						Cチップ.tチップの画像を描画する( hDevice, cc, 652 + 150, y, 0 );
						Cチップ.tチップの画像を描画する( hDevice, cc, 652 + 450, y, 0 );
					}
				}
				//-------------------------
				#endregion
				#region [ 判定バー ]
				//-----------------
				if( null != this.tx判定バー )
					this.tx判定バー.t2D描画( hDevice, 612, 877 );
				//-----------------
				#endregion
				#region [ ドラムキット ]
				//-----------------
				this.actドラムキット.t描画( hDevice );
				//-----------------
				#endregion
				#region [ 譜面 ]
				//-------------------------
				for( int i = this.n描画開始チップ番号; i < this.Song.listチップ.Count; i++ )
				{
					var cc = this.Song.listチップ[ i ];

					#region [ チップが描画対象じゃないならスキップ。]
					//-----------------
					if( cc.eチップ種別 == Eチップ種別.小節線 ||
						cc.eチップ種別 == Eチップ種別.拍線 ||
						cc.eチップ種別 == Eチップ種別.BPM ||
						cc.eチップ種別 == Eチップ種別.背景動画 ||
						cc.eチップ種別 == Eチップ種別.小節メモ )
						continue;
					//-----------------
					#endregion


					// 時間差を算出する。

					long n描画時間差ms = Global.SoundTimer.n現在時刻ms - cc.n描画時刻ms;


					// ヒットバーとチップの距離[pixel;符号付き]を算出する。

					int n描画距離px = this.n指定された時間msに対応する符号付きピクセル数を返す( n描画時間差ms );


					// チップのY座標を算出する。y が画面上外に出ていればそこでループ終了。

					int y = Theme.sz描画サイズ原寸px.Height - Theme.n画面下端からヒットバー中央までの距離px + n描画距離px;
					if( -20 > y )	// -20 は適当なマージン
						break;


					// チップのX座標を算出して描画する。

					E演奏レーン e演奏レーン = E演奏レーン.Unknown;
					foreach( var kvp in C演奏ステージ.dic演奏レーンtoチップ )
					{
						if( kvp.Value.Contains( cc.eチップ種別 ) )
						{
							e演奏レーン = kvp.Key;
							break;
						}
					}

					if( e演奏レーン == E演奏レーン.Unknown )
						continue;

					int x = C演奏ステージ.dic演奏レーンto中央Xpt[ e演奏レーン ];

					if( cc.b可視 )
						Cチップ.tチップの画像を描画する( hDevice, cc, x, y, this.ctチップアニメ.n現在の値 );
				}
				//-------------------------
				#endregion
				#region [ フリップボード ]
				//-------------------------
				this.actフリップボード.On描画( hDevice );
				//-------------------------
				#endregion
			}
		}

		private bool b背景動画チップの発火を待たずに最初の進行で背景動画の再生を開始する = false;
		private CTexture txドラムチップ = null;
		private CTexture txスコアフレーム = null;
		private CTexture tx判定バー = null;
		private CAct背景 act背景 = null;
		private CActステージキット actステージキット = null;
		private CActドラムキット actドラムキット = null;
		private CMActフリップボード actフリップボード = null;
		private CFPS FPS = null;
		private CFPS VPS = null;
		private CCounter ctチップアニメ = null;

		#region [ 演奏レーン to List<Eチップ種別>（static）]
		//-------------------------
		private static readonly Dictionary<E演奏レーン, List<Eチップ種別>> dic演奏レーンtoチップ = new Dictionary<E演奏レーン, List<Eチップ種別>>()
		{
			{ E演奏レーン.LeftCymbal, new List<Eチップ種別>() { Eチップ種別.LeftCrash, Eチップ種別.LeftChina, Eチップ種別.LeftCymbalMute, Eチップ種別.LeftRide, Eチップ種別.LeftRideCup, Eチップ種別.LeftSplash } },
			{ E演奏レーン.FootPedal, new List<Eチップ種別>() { Eチップ種別.FootPedal } },
			{ E演奏レーン.HiHat, new List<Eチップ種別>() { Eチップ種別.HiHatClose, Eチップ種別.HiHatHalfOpen, Eチップ種別.HiHatOpen } },
			{ E演奏レーン.Snare, new List<Eチップ種別>() { Eチップ種別.Snare, Eチップ種別.SnareClosedRim, Eチップ種別.SnareGhost, Eチップ種別.SnareOpenRim } },
			{ E演奏レーン.Tom1, new List<Eチップ種別>() { Eチップ種別.Tom1, Eチップ種別.Tom1Rim } },
			{ E演奏レーン.Bass, new List<Eチップ種別>() { Eチップ種別.Bass } },
			{ E演奏レーン.Tom2, new List<Eチップ種別>() { Eチップ種別.Tom2, Eチップ種別.Tom2Rim } },
			{ E演奏レーン.Tom3, new List<Eチップ種別>() { Eチップ種別.Tom3, Eチップ種別.Tom3Rim } },
			{ E演奏レーン.RightCymbal, new List<Eチップ種別>() { Eチップ種別.RightCrash, Eチップ種別.RightChina, Eチップ種別.RightCymbalMute, Eチップ種別.RightRide, Eチップ種別.RightRideCup, Eチップ種別.RightSplash } },
			{ E演奏レーン.Unknown, new List<Eチップ種別>() },
		};
		//-------------------------
		#endregion
		#region [ E演奏レーン to 中央位置X（static）]
		//-------------------------
		public static readonly Dictionary<E演奏レーン, int> dic演奏レーンto中央Xpt = new Dictionary<E演奏レーン, int>() {
			{ E演奏レーン.LeftCymbal, 656 },
			{ E演奏レーン.HiHat, 742 },
			{ E演奏レーン.FootPedal, 742 },
			{ E演奏レーン.Snare, 834 },
			{ E演奏レーン.Tom1, 929 },
			{ E演奏レーン.Bass, 1000 },
			{ E演奏レーン.Tom2, 1068 },
			{ E演奏レーン.Tom3, 1164 },
			{ E演奏レーン.RightCymbal, 1251 },
			{ E演奏レーン.Unknown, 0 },
		};
		//-------------------------
		#endregion

		private int n指定された時間msに対応する符号付きピクセル数を返す( long n指定時間ms )
		{
			return (int) ( n指定時間ms * Song.db基準譜面速度pxms * Global.Users.SelectedUser.Options.fスクロールスピード );
		}

		private int _n描画開始チップ番号;
		private Dictionary<Eヒット判定, int> _dicヒットした回数;
	}
}
