2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Diagnostics;
6 using System.Windows.Forms;
10 class StrokeStyleT : FDK.ApplicationBase
13 public static SST.フォルダ フォルダ => StrokeStyleT.bs_フォルダ;
14 public static FDK.入力.Keyboard キーボード入力 => StrokeStyleT.bs_キーボード入力;
15 public static FDK.入力.MidiIn MIDI入力 => StrokeStyleT.bs_MIDI入力;
16 public static FDK.メディア.サウンド.WASAPI排他.ExclusiveDevice Wasapiデバイス => StrokeStyleT.bs_Wasapiデバイス;
17 public static Random 乱数 => StrokeStyleT.bs_乱数;
18 public static SST.ユーザ.ユーザ管理 ユーザ管理 => StrokeStyleT.bs_ユーザ管理;
19 public static SST.曲.曲ツリー管理 曲ツリー管理 => StrokeStyleT.bs_曲ツリー管理;
20 public static SSTFormat.スコア 演奏スコア { get; set; } = null;
21 public static SST.設定.Config Config => StrokeStyleT.bs_Config;
23 public static void すべての入力デバイスをポーリングする()
25 // hack: 追加の入力デバイスができたら、ここにポーリングコードを追加すること。
26 StrokeStyleT.キーボード入力?.ポーリングする();
27 StrokeStyleT.MIDI入力?.ポーリングする();
30 // get only static property の初期化。
33 // フォルダ変数を真っ先に登録する。(ほかのメンバのコンストラクタでフォルダ変数を利用できるようにするため。)
34 StrokeStyleT.bs_フォルダ = new SST.フォルダ();
35 SST.フォルダ.フォルダ変数を追加する( "Static", StrokeStyleT.フォルダ.StaticFolder );
36 SST.フォルダ.フォルダ変数を追加する( "AppData", StrokeStyleT.フォルダ.AppDataFolder );
37 SST.フォルダ.フォルダ変数を追加する( "User", null );
40 StrokeStyleT.bs_ユーザ管理 = new ユーザ.ユーザ管理();
41 StrokeStyleT.bs_曲ツリー管理 = new 曲.曲ツリー管理();
44 protected override void 初期化する()
46 FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
47 Debug.Assert( null == this.スレッド排他領域.デバイスリソース, "デバイスリソースの作成前であること。" );
49 this.Window.Text = $"StrokeStyle<T> {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()}";
50 this.設計画面サイズdpx = new SharpDX.Size2F( 1920, 1080 ); // 設計画面サイズdpx(固定)
52 #region " コンフィグ を初期化する。"
54 FDK.Log.Info( "コンフィグを初期化します。" );
55 StrokeStyleT.bs_Config = new 設定.Config();
56 StrokeStyleT.bs_Config.ConfigXmlを読み込む();
59 #region " コンフィグで指定されたウィンドウサイズに変更。"
61 this.Window.ClientSize = new System.Drawing.Size( StrokeStyleT.Config.物理画面サイズpx.Width, StrokeStyleT.Config.物理画面サイズpx.Height );
65 #region " System.Stopwatch が高解像度タイマを使わないならエラー。"
67 if( false == System.Diagnostics.Stopwatch.IsHighResolution )
68 throw new SSTException( "このシステムは、高解像度タイマをサポートしていません。" );
71 #region " MediaFoundation を起動する。"
73 SharpDX.MediaFoundation.MediaManager.Startup();
76 #region " Sleep 精度を上げる。"
78 StrokeStyleT.timeBeginPeriod( 1 );
84 FDK.Log.Info( "ユーザ情報を初期化します。" );
85 StrokeStyleT.ユーザ管理.UsersXmlを読み込む();
88 foreach( var ユーザ in StrokeStyleT.ユーザ管理.ユーザリスト )
89 ユーザ.SourcesXmlを読み込む();
92 #region " WASAPI デバイスを初期化する。"
94 StrokeStyleT.bs_Wasapiデバイス = new FDK.メディア.サウンド.WASAPI排他.ExclusiveDevice();
95 StrokeStyleT.bs_Wasapiデバイス.初期化する( 15.0f );
98 #region " キーボード入力 を初期化する。"
100 FDK.Log.Info( "キーボード入力デバイスを初期化します。" );
101 StrokeStyleT.bs_キーボード入力 = new FDK.入力.Keyboard( this.Window.Handle );
104 #region " MIDI入力 を初期化する。"
106 FDK.Log.Info( "MIDI入力デバイスを初期化します。" );
107 StrokeStyleT.bs_MIDI入力 = new FDK.入力.MidiIn();
111 this.現在のステージ = this.最初のダミーステージ;
113 //#warning 全画面モード切替えを KeyDown で仮実装。
114 this.Window.KeyDown += ( target, arg ) => {
115 // Alt+Enter → 画面モードの切り替え
116 // if( ( arg.KeyCode == System.Windows.Forms.Keys.Return ) && ( arg.Modifiers == Keys.Alt ) )
117 // this.全画面モードとウィンドウモードを切り替える();
120 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
122 protected override void 終了する()
124 FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
126 Debug.Assert( null != this.スレッド排他領域.デバイスリソース, "デバイスリソースが解放される前であること。" );
128 #region " ステージを終了し、解放する。"
130 if( ( null != this.現在のステージ ) && ( this.現在のステージ.活性化している ) )
131 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
133 this.最初のダミーステージ = null;
135 this.タイトルステージ = null;
136 this.ログインステージ = null;
143 #region " MIDI入力 を解放する。"
145 FDK.Log.Info( "MIDI入力デバイスを解放します。" );
146 FDK.Utilities.解放する( ref StrokeStyleT.bs_MIDI入力 );
149 #region " キーボード入力 を解放する。"
151 FDK.Log.Info( "キーボード入力デバイスを解放します。" );
152 FDK.Utilities.解放する( ref StrokeStyleT.bs_キーボード入力 );
155 #region " WASAPIデバイスを解放する。"
157 FDK.Log.Info( "WASAPIデバイスを解放します。" );
158 FDK.Utilities.解放する( ref StrokeStyleT.bs_Wasapiデバイス );
161 #region " コンフィグを解放する。"
163 FDK.Log.Info( "コンフィグを解放します。" );
164 StrokeStyleT.bs_Config = null;
167 #region " MediaFoundation を終了する。"
169 SharpDX.MediaFoundation.MediaManager.Shutdown();
173 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
175 protected override void シーンを描画する()
177 // このメソッドは、GUIスレッドではなく進行描画スレッドから呼び出されるので注意。
179 this.スレッド排他領域.WriteLock( () => {
183 var dr = this.スレッド排他領域.デバイスリソース;
184 var d3dDevice = (SharpDX.Direct3D11.Device) null;
185 using( var d3dLock = new FDK.同期.AutoD3DDeviceLock( dr.DXGIDeviceManager, out d3dDevice ) )
187 using( var d3dContext = d3dDevice.ImmediateContext )
189 // 既定のD3Dレンダーターゲットビューを黒でクリアする。
190 d3dContext.ClearRenderTargetView( dr.D3DRenderTargetView, SharpDX.Color4.Black );
192 // 深度バッファを 1.0f でクリアする。
193 d3dContext.ClearDepthStencilView(
194 dr.D3DDepthStencilView,
195 SharpDX.Direct3D11.DepthStencilClearFlags.Depth,
202 this.現在のステージ?.進行描画する( this.スレッド排他領域.デバイスリソース );
206 // スワップチェーンを表示する。垂直帰線待ちなどで時間がかかるので、この部分はスレッド排他領域の外に配置すること。
207 if( false == this.スレッド排他領域.アプリを終了せよ )
209 this.スレッド排他領域.デバイスリソース.SwapChain.Present(
210 ( StrokeStyleT.Config.垂直帰線待ちを行う ) ? 1 : 0,
211 SharpDX.DXGI.PresentFlags.None );
214 // ステージの状態をチェックし、必要あれば遷移またはアプリを終了する。
215 this.スレッド排他領域.WriteLock( () => {
217 if( null != this.現在のステージ )
219 switch( this.現在のステージ.GetType().Name )
221 case nameof( ステージ.ステージ ):
222 #region " 最初のダミーステージ → 起動ステージへ。"
224 this.起動ステージ.活性化する( this.スレッド排他領域.デバイスリソース );
225 this.現在のステージ = this.起動ステージ;
230 case nameof( ステージ.起動.起動ステージ ):
231 #region " 終了 → タイトルステージへ。"
233 if( this.起動ステージ.現在のフェーズ == ステージ.起動.起動ステージ.フェーズ.終了 )
235 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
236 this.現在のステージ = this.タイトルステージ;
237 this.現在のステージ.活性化する( this.スレッド排他領域.デバイスリソース );
243 case nameof( ステージ.タイトル.タイトルステージ ):
244 #region " 確定 → ログインステージへ。"
246 if( this.タイトルステージ.現在のフェーズ == ステージ.タイトル.タイトルステージ.フェーズ.確定 )
248 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
249 this.現在のステージ = this.ログインステージ;
250 this.現在のステージ.活性化する( this.スレッド排他領域.デバイスリソース );
254 #region " キャンセル → アプリを終了する。"
256 else if( this.タイトルステージ.現在のフェーズ == ステージ.タイトル.タイトルステージ.フェーズ.キャンセル )
258 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
260 this.スレッド排他領域.アプリを終了せよ = true;
266 case nameof( ステージ.ログイン.ログインステージ ):
267 #region " 確定 → ログイン処理を行って、選曲ステージへ。"
269 if( this.ログインステージ.現在のフェーズ == ステージ.ログイン.ログインステージ.フェーズ.確定 )
271 // ログイン処理(1) ユーザリストに対してユーザを選択する。
272 StrokeStyleT.ユーザ管理.ユーザを選択する( this.ログインステージ.ユーザインデックス );
273 FDK.Log.Info( $"ユーザが選択されました。[{StrokeStyleT.ユーザ管理.現在選択されているユーザ.名前}]" );
275 // ログイン処理(2) 選択ユーザの曲ツリーを構築する。
276 StrokeStyleT.ユーザ管理.現在選択されているユーザ.曲を検索して曲ツリーを構築する();
278 // ログイン処理(3) 構築した曲ツリーを曲リスト管理に登録する。
279 StrokeStyleT.曲ツリー管理.現在の管理対象ツリー = StrokeStyleT.ユーザ管理.現在選択されているユーザ.曲ツリーのルートノード;
282 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
283 this.現在のステージ = this.選曲ステージ;
284 this.現在のステージ.活性化する( this.スレッド排他領域.デバイスリソース );
288 #region " キャンセル → タイトルステージへ。"
290 else if( this.ログインステージ.現在のフェーズ == ステージ.ログイン.ログインステージ.フェーズ.キャンセル )
292 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
293 this.現在のステージ = this.タイトルステージ;
294 this.現在のステージ.活性化する( this.スレッド排他領域.デバイスリソース );
300 case nameof( ステージ.選曲.選曲ステージ ):
301 #region " 曲確定 → 曲読込ステージへ。"
303 if( this.選曲ステージ.現在のフェーズ == ステージ.選曲.選曲ステージ.フェーズ.曲確定 )
305 // 曲ノードが選択されていることを確認。
306 Trace.Assert( null != StrokeStyleT.曲ツリー管理.現在選択されているノード, "[バグあり] 選択曲が null です。" );
307 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
308 this.現在のステージ = this.曲読込ステージ;
309 this.現在のステージ.活性化する( this.スレッド排他領域.デバイスリソース );
313 #region " キャンセル → アプリを終了する。"
315 else if( this.選曲ステージ.現在のフェーズ == ステージ.選曲.選曲ステージ.フェーズ.キャンセル )
317 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
319 this.スレッド排他領域.アプリを終了せよ = true;
325 case nameof( ステージ.曲読込.曲読込ステージ ):
326 #region " 終了 → 演奏ステージへ。"
327 //--------------------
328 if( this.曲読込ステージ.現在のフェーズ == ステージ.曲読込.曲読込ステージ.フェーズ.終了 )
330 // 演奏スコアインスタンスが作成されていることを確認。
331 Debug.Assert( null != StrokeStyleT.演奏スコア );
333 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
334 this.現在のステージ = this.演奏ステージ;
335 this.現在のステージ.活性化する( this.スレッド排他領域.デバイスリソース );
337 //--------------------
341 case nameof( ステージ.演奏.演奏ステージ ):
342 #region " 演奏終了 → 結果ステージへ。"
343 //--------------------
344 if( this.演奏ステージ.現在のフェーズ == ステージ.演奏.演奏ステージ.フェーズ.クリアor失敗 )
346 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
347 this.現在のステージ = this.結果ステージ;
348 this.現在のステージ.活性化する( this.スレッド排他領域.デバイスリソース );
350 //--------------------
352 #region " キャンセル → 選曲ステージへ。"
353 //--------------------
354 if( this.演奏ステージ.現在のフェーズ == ステージ.演奏.演奏ステージ.フェーズ.キャンセル )
356 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
357 this.現在のステージ = this.選曲ステージ;
358 this.現在のステージ.活性化する( this.スレッド排他領域.デバイスリソース );
360 //--------------------
364 case nameof( ステージ.結果.結果ステージ ):
365 #region " 終了 → 選曲ステージへ。"
366 //--------------------
367 if( this.結果ステージ.現在のフェーズ == ステージ.結果.結果ステージ.フェーズ.終了 )
369 this.現在のステージ.非活性化する( this.スレッド排他領域.デバイスリソース );
370 this.現在のステージ = this.選曲ステージ;
371 this.現在のステージ.活性化する( this.スレッド排他領域.デバイスリソース );
373 //--------------------
381 protected override void デバイス依存リソースを再構築する()
383 FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
385 this.現在のステージ?.デバイス依存リソースを作成する( this.スレッド排他領域.デバイスリソース );
387 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
389 protected override void デバイス依存リソースを解放する()
391 FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
393 this.現在のステージ?.デバイス依存リソースを解放する( this.スレッド排他領域.デバイスリソース );
395 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
399 protected SST.ステージ.ステージ 最初のダミーステージ = new ステージ.ステージ();
400 protected SST.ステージ.起動.起動ステージ 起動ステージ = new ステージ.起動.起動ステージ();
401 protected SST.ステージ.タイトル.タイトルステージ タイトルステージ = new ステージ.タイトル.タイトルステージ();
402 protected SST.ステージ.ログイン.ログインステージ ログインステージ = new ステージ.ログイン.ログインステージ();
403 protected SST.ステージ.選曲.選曲ステージ 選曲ステージ = new ステージ.選曲.選曲ステージ();
404 protected SST.ステージ.曲読込.曲読込ステージ 曲読込ステージ = new ステージ.曲読込.曲読込ステージ();
405 protected SST.ステージ.演奏.演奏ステージ 演奏ステージ = new ステージ.演奏.演奏ステージ();
406 protected SST.ステージ.結果.結果ステージ 結果ステージ = new ステージ.結果.結果ステージ();
408 private SST.ステージ.ステージ 現在のステージ = null;
412 private static SST.フォルダ bs_フォルダ = null;
413 private static FDK.入力.Keyboard bs_キーボード入力 = null;
414 private static FDK.入力.MidiIn bs_MIDI入力 = null;
415 private static FDK.メディア.サウンド.WASAPI排他.ExclusiveDevice bs_Wasapiデバイス = null;
416 private static readonly Random bs_乱数 = new Random( DateTime.Now.Millisecond );
417 private static SST.ユーザ.ユーザ管理 bs_ユーザ管理 = null;
418 private static SST.曲.曲ツリー管理 bs_曲ツリー管理 = null;
419 public static SST.設定.Config bs_Config = null;
423 #region " Win32 API "
425 [ System.Runtime.InteropServices.DllImport( "winmm.dll", EntryPoint = "timeBeginPeriod" )]
426 private static extern uint timeBeginPeriod( uint uMilliseconds );
428 [System.Runtime.InteropServices.DllImport( "winmm.dll", EntryPoint = "timeEndPeriod" )]
429 private static extern uint timeEndPeriod( uint uMilliseconds );