OSDN Git Service

66bdbba098a4f8a2b466835b2e80dafa6d270f25
[strokestylet/CsWin10Desktop3.git] / StrokeStyleT / StrokeStyleT.cs
1 using System;
2 using System.Collections.Concurrent;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.Linq;
6 using System.ServiceModel;
7 using System.Windows.Forms;
8 using FDK;
9
10 namespace SST
11 {
12         [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]     // サービスインターフェースをシングルスレッドで呼び出す。
13         class StrokeStyleT : SST.IStrokeStyleTService
14         {
15                 // グローバルリソース (static) 
16
17                 public static SST.フォルダ フォルダ
18                 {
19                         get { return StrokeStyleT._フォルダ; }
20                 }
21
22                 public static FDK.メディア.サウンド.WASAPI.Device サウンドデバイス
23                 {
24                         get { return StrokeStyleT._サウンドデバイス; }
25                 }
26
27                 public static System.Random 乱数
28                 {
29                         get { return StrokeStyleT._乱数; }
30                 }
31
32                 public static SST.ユーザ.ユーザ管理 ユーザ管理
33                 {
34                         get { return StrokeStyleT._ユーザ管理; }
35                 }
36
37                 public static SST.曲.曲ツリー管理 曲ツリー管理
38                 {
39                         get { return StrokeStyleT._曲ツリー管理; }
40                 }
41
42                 public static SSTFormat.スコア 演奏スコア
43                 {
44                         get;
45                         set;
46                 } = null;
47
48                 public static SST.設定.Config Config
49                 {
50                         get { return StrokeStyleT._Config; }
51                 }
52
53                 public static bool ビュアーモードである
54                 {
55                         get;
56                         set;
57                 } = false;
58
59                 public static bool ビュアーモードではない
60                 {
61                         get { return !StrokeStyleT.ビュアーモードである; }
62                         set { StrokeStyleT.ビュアーモードである = !value; }
63                 }
64
65                 public static SST.ViewerMessage 最後に取得したビュアーメッセージ
66                 {
67                         get;
68                         protected set;
69                 } = null;
70
71                 public static FDK.入力.Keyboard キーボード入力
72                 {
73                         get { return StrokeStyleT._キーボード入力; }
74                 }
75
76                 public static FDK.入力.MidiIn MIDI入力
77                 {
78                         get { return StrokeStyleT._MIDI入力; }
79                 }
80
81                 public static void すべての入力デバイスをポーリングする()
82                 {
83                         // hack: 追加の入力デバイスができたら、ここにポーリングコードを追加すること。
84                         StrokeStyleT.キーボード入力?.ポーリングする();
85                         StrokeStyleT.MIDI入力?.ポーリングする();
86                 }
87
88
89                 static StrokeStyleT()
90                 {
91                         // フォルダ変数を真っ先に登録する。(ほかのメンバのコンストラクタでフォルダ変数を利用できるようにするため。)
92                         StrokeStyleT._フォルダ = new SST.フォルダ();
93                         SST.フォルダ.フォルダ変数を追加する( "Static", StrokeStyleT.フォルダ.StaticFolder );
94                         SST.フォルダ.フォルダ変数を追加する( "AppData", StrokeStyleT.フォルダ.AppDataFolder );
95                         SST.フォルダ.フォルダ変数を追加する( "User", null );
96                 }
97
98                 public StrokeStyleT( string[] args )
99                 {
100                         #region " ビュアーモードかどうかを確認する。"
101                         //----------------
102                         foreach( var arg in args )
103                         {
104                                 if( ( "-v" == arg.ToLower() ) || ( "-viewer" == arg.ToLower() ) )
105                                 {
106                                         StrokeStyleT.ビュアーモードである = true;
107                                         break;
108                                 }
109                         }
110                         //----------------
111                         #endregion
112
113                         this._State = ApplicationState.起動;
114
115                         this._MainForm = new RenderForm();
116                         this._MainForm.BackColor = System.Drawing.Color.Black;
117                         this._MainForm.AllowUserResizing = false;       // ユーザはフォームサイズを変更できない。
118                         this._MainForm.UserResized += フォームサイズが変更された;
119                         this._MainForm.FormClosing += ( sender, e ) => {
120                                 this.終了する();
121                         };
122                 }
123
124                 /// <summary>
125                 ///             アプリのメインエントリ。
126                 /// </summary>
127                 public void Run()
128                 {
129                         Debug.Assert( null != this._MainForm );
130
131                         SharpDX.Windows.RenderLoop.Run( this._MainForm, () => {
132
133                                 // アプリケーションの状態に応じて処理分岐。
134                                 switch( this._State )
135                                 {
136                                         case ApplicationState.起動:
137                                                 this.起動する();
138                                                 this._State = ApplicationState.初期化;
139                                                 break;  // 初期化に移る前に、一度ウィンドウメッセージ処理を行わせて、画面を再描画させる。
140
141                                         case ApplicationState.初期化:
142                                                 this.初期化する();
143                                                 this._State = ApplicationState.進行描画;
144                                                 break;
145
146                                         case ApplicationState.進行描画:
147                                                 this.進行描画する();
148                                                 if( this._State == ApplicationState.終了 )
149                                                         this.終了する();
150                                                 break;
151
152                                         case ApplicationState.終了:
153                                                 // 何もしない
154                                                 break;
155                                 }
156
157                         } );
158                 }
159
160                 #region " WCF サービスインターフェースの実装 "
161                 //----------------
162                 // ・このサービスインターフェースは、シングルスレッド(GUIスレッド)で同期実行される。(StrokeStyleT クラスの ServiceBehavior属性を参照。)
163                 // ・このサービスホストはシングルトンであり、すべてのクライアントセッションは同一(単一)のサービスインスタンスへ接続される。(Program.Main() を参照。)
164
165                 /// <summary>
166                 ///             曲を読み込み、演奏を開始する。
167                 /// </summary>
168                 /// <remarks>
169                 ///             ビュアーモードのときのみ有効。
170                 /// </remarks>
171                 /// <param name="path">
172                 ///             曲ファイルパス
173                 ///     </param>
174                 /// <param name="startPart">
175                 ///             演奏開始小節番号(0~)
176                 ///     </param>
177                 /// <param name="drumsSound">
178                 ///             ドラムチップ音を発声させるなら true。
179                 /// </param>
180                 public void ViewerPlay( string path, int startPart = 0, bool drumsSound = true )
181                 {
182                         if( StrokeStyleT.ビュアーモードではない )
183                                 return;
184
185                         this._ビュアーメッセージキュー.Enqueue( new ViewerMessage() {
186                                 種別 = ViewerMessageType.演奏開始,
187                                 曲ファイルパス = path,
188                                 演奏開始小節番号 = startPart,
189                                 ドラムチップ発声 = drumsSound,
190                         } );
191                 }
192
193                 /// <summary>
194                 ///             現在の演奏を停止する。
195                 /// </summary>
196                 /// <remarks>
197                 ///             ビュアーモードのときのみ有効。
198                 /// </remarks>
199                 public void ViewerStop()
200                 {
201                         if( StrokeStyleT.ビュアーモードではない )
202                                 return;
203
204                         this._ビュアーメッセージキュー.Enqueue( new ViewerMessage() {
205                                 種別 = ViewerMessageType.演奏停止,
206                         } );
207                 }
208
209                 /// <summary>
210                 ///             サウンドデバイスの発声遅延[ms]を返す。
211                 /// </summary>
212                 /// <returns>
213                 ///             遅延量[ms]
214                 ///     </returns>
215                 public float GetSoundDelay()
216                 {
217                         if( StrokeStyleT.ビュアーモードではない )
218                                 return 0f;
219
220                         return ( null != StrokeStyleT.サウンドデバイス ) ? (float) ( StrokeStyleT.サウンドデバイス.遅延sec * 1000.0 ) : 0f;
221                 }
222                 //----------------
223                 #endregion
224
225                 private enum ApplicationState { 起動, 初期化, 進行描画, 終了 }
226                 private ApplicationState _State;
227                 private SST.RenderForm _MainForm = null;
228                 private SharpDX.Size2F _設計画面サイズdpx = SharpDX.Size2F.Empty;
229                 private ConcurrentQueue<ViewerMessage> _ビュアーメッセージキュー = new ConcurrentQueue<ViewerMessage>();
230                 private FDK.メディア.デバイスリソース _デバイスリソース = null;
231                 private SST.ステージ.ステージ _最初のダミーステージ = null;
232                 private SST.ステージ.起動.起動ステージ _起動ステージ = null;
233                 private SST.ステージ.タイトル.タイトルステージ _タイトルステージ = null;
234                 private SST.ステージ.ログイン.ログインステージ _ログインステージ = null;
235                 private SST.ステージ.選曲.選曲ステージ _選曲ステージ = null;
236                 private SST.ステージ.曲読込.曲読込ステージ _曲読込ステージ = null;
237                 private SST.ステージ.演奏.演奏ステージ _演奏ステージ = null;
238                 private SST.ステージ.結果.結果ステージ _結果ステージ = null;
239                 private SST.ステージ.ステージ _現在のステージ = null;
240                 private static SST.フォルダ _フォルダ = null;
241                 private static FDK.入力.Keyboard _キーボード入力 = null;
242                 private static FDK.入力.MidiIn _MIDI入力 = null;
243                 private static FDK.メディア.サウンド.WASAPI.Device _サウンドデバイス = null;
244                 private static readonly System.Random _乱数 = new Random( DateTime.Now.Millisecond );
245                 private static SST.ユーザ.ユーザ管理 _ユーザ管理 = null;
246                 private static SST.曲.曲ツリー管理 _曲ツリー管理 = null;
247                 private static SST.設定.Config _Config = null;
248
249                 private void 起動する()
250                 {
251                         FDK.Log.現在のスレッドに名前をつける( "Render" );
252                         FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
253
254                         #region " コンフィグ を初期化する。"
255                         //----------------
256                         StrokeStyleT._Config = new 設定.Config();
257                         StrokeStyleT._Config.ConfigXmlを読み込む();
258                         //----------------
259                         #endregion
260
261                         #region " コンフィグで指定されたウィンドウサイズに変更する。"
262                         //----------------
263                         this._MainForm.ClientSize = new System.Drawing.Size( StrokeStyleT.Config.物理画面サイズpx.Width, StrokeStyleT.Config.物理画面サイズpx.Height );
264                         //----------------
265                         #endregion
266                         #region " フォームタイトルと設計画面サイズを設定する。"
267                         //----------------
268                         this._MainForm.Text = $"StrokeStyle<T> {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()}";
269                         if( StrokeStyleT.ビュアーモードである )
270                                 this._MainForm.Text += " (Viewer)";
271
272                         // 設計画面サイズは、フォームサイズ(物理画面サイズ)とは独立して固定。
273                         this._設計画面サイズdpx = new SharpDX.Size2F( 1920f, 1080f );
274                         //----------------
275                         #endregion
276
277                         FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
278                 }
279
280                 private void 初期化する()
281                 {
282                         FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
283
284                         // 開始条件チェック。
285                         Debug.Assert( null == this._デバイスリソース, "デバイスリソースの作成前であること。" );
286
287                         StrokeStyleT._ユーザ管理 = new ユーザ.ユーザ管理();
288                         StrokeStyleT._曲ツリー管理 = new 曲.曲ツリー管理();
289
290                         #region " 高解像度タイマを使えないならエラー。"
291                         //-----------------
292                         if( false == System.Diagnostics.Stopwatch.IsHighResolution )
293                                 throw new SSTException( "このシステムは、高解像度タイマをサポートしていません。" );
294                         //-----------------
295                         #endregion
296                         #region " MediaFoundation を起動する。"
297                         //-----------------
298                         SharpDX.MediaFoundation.MediaManager.Startup();
299                         //-----------------
300                         #endregion
301                         #region " Sleep 精度を上げる。"
302                         //-----------------
303                         Win32.timeBeginPeriod( 1 );
304                         //-----------------
305                         #endregion
306
307                         #region " デバイスリソースを作成する。"
308                         //----------------
309                         FDK.Log.Info( $"設計画面サイズ: {this._設計画面サイズdpx}" );
310                         FDK.Log.Info( $"物理画面サイズ: {this._MainForm.ClientSize}" );
311                         this._デバイスリソース = new FDK.メディア.デバイスリソース( this._設計画面サイズdpx );
312                         this._デバイスリソース.すべてのリソースを作成する( this._MainForm.ClientSize, this._MainForm.Handle );
313                         //----------------
314                         #endregion
315
316                         #region " ステージを生成する。"
317                         //----------------
318                         this._最初のダミーステージ = new ステージ.ステージ();
319                         this._起動ステージ = new ステージ.起動.起動ステージ();
320                         this._タイトルステージ = new ステージ.タイトル.タイトルステージ();
321                         this._ログインステージ = new ステージ.ログイン.ログインステージ();
322                         this._選曲ステージ = new ステージ.選曲.選曲ステージ();
323                         this._曲読込ステージ = new ステージ.曲読込.曲読込ステージ();
324                         this._演奏ステージ = new ステージ.演奏.演奏ステージ();
325                         this._結果ステージ = new ステージ.結果.結果ステージ();
326
327                         // 外部依存アクションを接続する。
328                         this._曲読込ステージ.読込曲のファイルパスを取得する = () => ( ( StrokeStyleT.曲ツリー管理.現在選択されているノード as SST.曲.MusicNode )?.sstfファイルパス );
329                         this._結果ステージ.演奏ステージインスタンスを取得する = () => ( this._演奏ステージ );
330                         this._結果ステージ.BGMを終了する = () => { this._演奏ステージ.BGMを解放する(); };
331                         //----------------
332                         #endregion
333                         #region " ユーザを初期化する。"
334                         //-----------------
335                         // Users.xml を読み込む。
336                         StrokeStyleT.ユーザ管理.UsersXmlを読み込む();
337
338                         // ユーザ別の初期化。
339                         foreach( var ユーザ in StrokeStyleT.ユーザ管理.ユーザリスト )
340                                 ユーザ.SourcesXmlを読み込む();
341                         //-----------------
342                         #endregion
343                         #region " サウンドデバイスを初期化する。"
344                         //----------------
345                         StrokeStyleT._サウンドデバイス = new FDK.メディア.サウンド.WASAPI.Device( CSCore.CoreAudioAPI.AudioClientShareMode.Shared );
346                         //----------------
347                         #endregion
348                         #region " キーボード入力 を初期化する。"
349                         //-----------------
350                         StrokeStyleT._キーボード入力 = new FDK.入力.Keyboard( this._MainForm.Handle );
351                         //-----------------
352                         #endregion
353                         #region " MIDI入力 を初期化する。"
354                         //-----------------
355                         StrokeStyleT._MIDI入力 = new FDK.入力.MidiIn();
356                         //-----------------
357                         #endregion
358
359                         FDK.Log.Info( "最初のダミーステージを開始します。" );
360                         this._現在のステージ = this._最初のダミーステージ;
361
362                         //#warning 全画面モード切替えを仮実装。
363                         this._MainForm.KeyDown += ( target, arg ) => {
364
365                                 // F11 → 画面モードの切り替え
366                                 if( arg.KeyCode == System.Windows.Forms.Keys.F11 )
367                                 {
368                                         this.全画面モードとウィンドウモードを切り替える();
369                                         arg.Handled = true;
370                                 }
371                         };
372
373                         // 終了条件チェック。
374                         Debug.Assert( SharpDX.Size2F.Empty != this._設計画面サイズdpx, "設計画面サイズが設定されていません。" );
375
376                         FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
377                 }
378
379                 private void 終了する()
380                 {
381                         FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
382                         Debug.Assert( null != this._デバイスリソース, "デバイスリソースが解放される前であること。" );
383
384                         this._State = ApplicationState.終了;
385
386                         #region " ステージを終了し、解放する。"
387                         //----------------
388                         if( ( null != this._現在のステージ ) && ( this._現在のステージ.活性化している ) )      // 念のため
389                                 this._現在のステージ.非活性化する( this._デバイスリソース );
390
391                         this._演奏ステージ.BGMを解放する();
392
393                         this._最初のダミーステージ = null;
394                         this._起動ステージ = null;
395                         this._タイトルステージ = null;
396                         this._ログインステージ = null;
397                         this._選曲ステージ = null;
398                         this._曲読込ステージ = null;
399                         this._演奏ステージ = null;
400                         this._結果ステージ = null;
401                         //----------------
402                         #endregion
403
404                         #region " ユーザ情報を保存する。"
405                         //----------------
406                         // 要素が増えたときのため、変更がなくても保存する。
407                         StrokeStyleT.ユーザ管理.UsersXmlを保存する();
408                         //----------------
409                         #endregion
410
411                         #region " デバイスリソースを解放する。"
412                         //----------------
413                         FDK.Utilities.解放する( ref this._デバイスリソース );
414                         //----------------
415                         #endregion
416
417                         #region " MIDI入力デバイスを解放する。"
418                         //-----------------
419                         FDK.Log.Info( "MIDI入力デバイスを解放します。" );
420                         FDK.Utilities.解放する( ref StrokeStyleT._MIDI入力 );
421                         //-----------------
422                         #endregion
423                         #region " キーボード入力デバイスを解放する。"
424                         //-----------------
425                         FDK.Log.Info( "キーボード入力デバイスを解放します。" );
426                         FDK.Utilities.解放する( ref StrokeStyleT._キーボード入力 );
427                         //-----------------
428                         #endregion
429                         #region " サウンドデバイスを解放する。"
430                         //----------------
431                         FDK.Log.Info( "サウンドデバイスを解放します。" );
432                         FDK.Utilities.解放する( ref StrokeStyleT._サウンドデバイス );
433                         //----------------
434                         #endregion
435                         
436                         #region " コンフィグを保存し、解放する。"
437                         //----------------
438                         FDK.Log.Info( "コンフィグを解放します。" );
439                         StrokeStyleT._Config.ConfigXmlを保存する();
440                         StrokeStyleT._Config = null;
441                         //----------------
442                         #endregion
443                         #region " フォームを閉じる。"
444                         //----------------
445                         FDK.Utilities.解放する( ref this._MainForm );
446                         //----------------
447                         #endregion
448
449                         #region " MediaFoundation を終了する。"
450                         //-----------------
451                         SharpDX.MediaFoundation.MediaManager.Shutdown();
452                         //-----------------
453                         #endregion
454
455                         FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
456                 }
457
458                 private void 進行描画する()
459                 {
460                         #region " D3Dデバイスが消失していれば再構築する。"
461                         //----------------
462                         bool 異常発生 = false;
463                         this._デバイスリソース.D3Dデバイスが消失していれば再構築する( out 異常発生 );
464
465                         // 再構築不可能な異常の場合は、即終了する。
466                         if( 異常発生 )
467                         {
468                                 this._State = ApplicationState.終了;
469                                 return;
470                         }
471                         //----------------
472                         #endregion
473                         #region " 描画の前処理を行う。"
474                         //----------------
475                         {
476                                 var d3dDevice = (SharpDX.Direct3D11.Device) null;
477                                 using( var d3dLock = new FDK.同期.AutoD3DDeviceLock( this._デバイスリソース.DXGIDeviceManager, out d3dDevice ) )
478                                 using( d3dDevice )
479                                 using( var d3dContext = d3dDevice.ImmediateContext )
480                                 {
481                                         // 既定のD3Dレンダーターゲットビューを黒でクリアする。
482                                         d3dContext.ClearRenderTargetView( this._デバイスリソース.D3DRenderTargetView, SharpDX.Color4.Black );
483
484                                         // 深度バッファを 1.0f でクリアする。
485                                         d3dContext.ClearDepthStencilView(
486                                                 this._デバイスリソース.D3DDepthStencilView,
487                                                 SharpDX.Direct3D11.DepthStencilClearFlags.Depth,
488                                                 depth: 1.0f,
489                                                 stencil: 0 );
490                                 }
491                         }
492                         //----------------
493                         #endregion
494                         #region " 現在のステージを進行描画する。"
495                         //----------------
496                         this._現在のステージ?.進行描画する( this._デバイスリソース );
497                         //----------------
498                         #endregion
499                         #region " スワップチェーンを表示する。"
500                         //----------------
501                         if( StrokeStyleT.Config.垂直帰線待ちを行う )
502                         {
503                                 // We recommend that you use Flush when the CPU waits for an arbitrary amount of time
504                                 // (such as when you call the Sleep function). 
505                                 // → https://msdn.microsoft.com/ja-jp/library/windows/desktop/ff476425(v=vs.85).aspx
506
507                                 var d3dDevice = (SharpDX.Direct3D11.Device) null;
508                                 using( var d3dLock = new FDK.同期.AutoD3DDeviceLock( this._デバイスリソース.DXGIDeviceManager, out d3dDevice ) )
509                                 using( d3dDevice )
510                                 using( var d3dContext = d3dDevice.ImmediateContext )
511                                 {
512                                         d3dContext.Flush();
513                                 }
514                         }
515
516                         this._デバイスリソース.SwapChain1.Present(
517                                 ( StrokeStyleT.Config.垂直帰線待ちを行う ) ? 1 : 0,
518                                 SharpDX.DXGI.PresentFlags.None );
519                         //----------------
520                         #endregion
521
522                         #region " ステージの状態をチェックし、必要あれば遷移またはアプリを終了する。また、必要あればビュアーメッセージキューの処理も行う。"
523                         //----------------
524                         if( null != this._現在のステージ )
525                         {
526                                 switch( this._現在のステージ.GetType().Name )
527                                 {
528                                         case nameof( ステージ.ステージ ):
529                                                 #region " ビュアーモード → AutoPlayerでログインして演奏ステージへ。"
530                                                 //----------------
531                                                 if( StrokeStyleT.ビュアーモードである )
532                                                 {
533                                                         // ビュアー用ユーザでログインする。
534                                                         FDK.Log.Info( "ビュアーモード: AutoPlayer ユーザでログインします。" );
535                                                         this.ログインする( Properties.Resources.AUTOPLAYER );
536
537                                                         // 曲読込ステージ向けの初期設定。
538                                                         StrokeStyleT.曲ツリー管理.現在の管理対象ツリー = null;
539                                                         StrokeStyleT.曲ツリー管理.現在選択されているノード = null;
540                                                         
541                                                         // 演奏ステージ向けの初期設定。
542                                                         StrokeStyleT.演奏スコア = null;
543
544                                                         // 演奏ステージへ。
545                                                         this._演奏ステージ.活性化する( this._デバイスリソース );
546                                                         this._現在のステージ = this._演奏ステージ;
547                                                 }
548                                                 //----------------
549                                                 #endregion
550                                                 #region " 通常モード → 起動ステージへ。"
551                                                 //----------------
552                                                 else
553                                                 {
554                                                         this._起動ステージ.活性化する( this._デバイスリソース );
555                                                         this._現在のステージ = this._起動ステージ;
556                                                 }
557                                                 //----------------
558                                                 #endregion
559                                                 break;
560
561                                         case nameof( ステージ.起動.起動ステージ ):
562                                                 #region " 終了 → タイトルステージへ。"
563                                                 //---------------
564                                                 if( this._起動ステージ.現在のフェーズ == ステージ.起動.起動ステージ.フェーズ.終了 )
565                                                 {
566                                                         this._現在のステージ.非活性化する( this._デバイスリソース );
567                                                         this._現在のステージ = this._タイトルステージ;
568                                                         this._現在のステージ.活性化する( this._デバイスリソース );
569                                                 }
570                                                 //---------------
571                                                 #endregion
572                                                 break;
573
574                                         case nameof( ステージ.タイトル.タイトルステージ ):
575                                                 #region " 確定 → ログインステージへ。"
576                                                 //---------------
577                                                 if( this._タイトルステージ.現在のフェーズ == ステージ.タイトル.タイトルステージ.フェーズ.確定 )
578                                                 {
579                                                         this._現在のステージ.非活性化する( this._デバイスリソース );
580                                                         this._現在のステージ = this._ログインステージ;
581                                                         this._現在のステージ.活性化する( this._デバイスリソース );
582                                                 }
583                                                 //---------------
584                                                 #endregion
585                                                 #region " キャンセル → アプリを終了する。"
586                                                 //---------------
587                                                 else if( this._タイトルステージ.現在のフェーズ == ステージ.タイトル.タイトルステージ.フェーズ.キャンセル )
588                                                 {
589                                                         this._現在のステージ.非活性化する( this._デバイスリソース );
590                                                         this._現在のステージ = null;
591                                                         this._State = ApplicationState.終了;
592                                                 }
593                                                 //---------------
594                                                 #endregion
595                                                 break;
596
597                                         case nameof( ステージ.ログイン.ログインステージ ):
598                                                 #region " 確定 → ログイン処理を行って、選曲ステージへ。"
599                                                 //---------------
600                                                 if( this._ログインステージ.現在のフェーズ == ステージ.ログイン.ログインステージ.フェーズ.確定 )
601                                                 {
602                                                         var user = StrokeStyleT.ユーザ管理.現在選択されているユーザ;
603
604                                                         if( null != user )
605                                                         {
606                                                                 foreach( var path in user.曲の検索元フォルダパスのリスト )
607                                                                         SST.曲.曲ツリー管理.フォルダから曲を再帰的に検索して子ノードリストに追加する( user.曲ツリーのルートノード, path );
608
609                                                                 StrokeStyleT.曲ツリー管理.現在の管理対象ツリー = StrokeStyleT.ユーザ管理.現在選択されているユーザ.曲ツリーのルートノード;
610                                                         }
611
612                                                         this._現在のステージ.非活性化する( this._デバイスリソース );
613                                                         this._現在のステージ = this._選曲ステージ;
614                                                         this._現在のステージ.活性化する( this._デバイスリソース );
615                                                 }
616                                                 //---------------
617                                                 #endregion
618                                                 #region " キャンセル → タイトルステージへ。"
619                                                 //---------------
620                                                 else if( this._ログインステージ.現在のフェーズ == ステージ.ログイン.ログインステージ.フェーズ.キャンセル )
621                                                 {
622                                                         this._現在のステージ.非活性化する( this._デバイスリソース );
623                                                         this._現在のステージ = this._タイトルステージ;
624                                                         this._現在のステージ.活性化する( this._デバイスリソース );
625                                                 }
626                                                 //---------------
627                                                 #endregion
628                                                 break;
629
630                                         case nameof( ステージ.選曲.選曲ステージ ):
631                                                 #region " 曲確定 → 曲読込ステージへ。"
632                                                 //---------------
633                                                 if( this._選曲ステージ.現在のフェーズ == ステージ.選曲.選曲ステージ.フェーズ.曲確定 )
634                                                 {
635                                                         // 曲ノードが選択されていることを確認。
636                                                         Trace.Assert( null != StrokeStyleT.曲ツリー管理.現在選択されているノード, "[バグあり] 選択曲が null です。" );
637                                                         this._現在のステージ.非活性化する( this._デバイスリソース );
638                                                         this._現在のステージ = this._曲読込ステージ;
639                                                         this._現在のステージ.活性化する( this._デバイスリソース );
640                                                 }
641                                                 //---------------
642                                                 #endregion
643                                                 #region " キャンセル → アプリを終了する。"
644                                                 //---------------
645                                                 else if( this._選曲ステージ.現在のフェーズ == ステージ.選曲.選曲ステージ.フェーズ.キャンセル )
646                                                 {
647                                                         this._現在のステージ.非活性化する( this._デバイスリソース );
648                                                         this._現在のステージ = null;
649                                                         this._State = ApplicationState.終了;
650                                                 }
651                                                 //---------------
652                                                 #endregion
653                                                 break;
654
655                                         case nameof( ステージ.曲読込.曲読込ステージ ):
656                                                 #region " 終了 → 演奏ステージへ。"
657                                                 //--------------------
658                                                 if( this._曲読込ステージ.現在のフェーズ == ステージ.曲読込.曲読込ステージ.フェーズ.終了 )
659                                                 {
660                                                         this._現在のステージ.非活性化する( this._デバイスリソース );
661                                                         this._現在のステージ = this._演奏ステージ;
662                                                         this._現在のステージ.活性化する( this._デバイスリソース );
663                                                 }
664                                                 //--------------------
665                                                 #endregion
666                                                 break;
667
668                                         case nameof( ステージ.演奏.演奏ステージ ):
669                                                 if( StrokeStyleT.ビュアーモードである )
670                                                 {
671                                                         // (A) ビュアーモード
672
673                                                         #region " ビュアーメッセージがあれば処理する。"
674                                                         //----------------
675                                                         var msg = this.ビュアーメッセージを取得する();
676
677                                                         StrokeStyleT.最後に取得したビュアーメッセージ = msg;    // 保存する。(演奏ステージに対して公開する)
678
679                                                         if( null != msg )
680                                                         {
681                                                                 FDK.Log.Info( msg.ToString() );
682
683                                                                 #region " (A) 演奏開始 "
684                                                                 //----------------
685                                                                 if( msg.種別 == ViewerMessageType.演奏開始 &&
686                                                                         ( this._演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.ビュアーメッセージ待機中 ||
687                                                                           this._演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.演奏中 ) )
688                                                                 {
689                                                                         try
690                                                                         {
691                                                                                 // MusicNode を作成し、現在選択されているノードとして登録する。
692                                                                                 StrokeStyleT.曲ツリー管理.現在選択されているノード = new 曲.MusicNode( msg.曲ファイルパス );
693
694                                                                                 // 演奏を停止する。
695                                                                                 this._演奏ステージ.非活性化する( this._デバイスリソース );
696                                                                                 this._演奏ステージ.BGMを解放する();
697
698                                                                                 // 曲読込ステージへ。
699                                                                                 this._現在のステージ = this._曲読込ステージ;
700                                                                                 this._現在のステージ.活性化する( this._デバイスリソース );
701                                                                         }
702                                                                         catch
703                                                                         {
704                                                                                 FDK.Log.ERROR( $"MusicNode の作成に失敗しました。" );
705                                                                         }
706                                                                 }
707                                                                 //----------------
708                                                                 #endregion
709                                                                 #region " (B) 演奏停止 "
710                                                                 //----------------
711                                                                 else if( msg.種別 == ViewerMessageType.演奏停止 && (
712                                                                         this._演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.ビュアーメッセージ待機中 ||
713                                                                         this._演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.演奏中 )
714                                                                         )
715                                                                 {
716                                                                         this._演奏ステージ.演奏を停止する();
717                                                                         this._演奏ステージ.現在のフェーズ.Value = ステージ.演奏.演奏ステージ.フェーズ.ビュアーメッセージ待機中;
718                                                                 }
719                                                                 //----------------
720                                                                 #endregion
721                                                                 #region " その他 "
722                                                                 //----------------
723                                                                 else
724                                                                 {
725                                                                         // その他のメッセージは無視。
726                                                                 }
727                                                                 //----------------
728                                                                 #endregion
729                                                         }
730                                                         //----------------
731                                                         #endregion
732                                                         #region " クリア/キャンセル → メッセージ待機へ。"
733                                                         //----------------
734                                                         if( this._演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.クリアor失敗 ||
735                                                                 this._演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.キャンセル )
736                                                         {
737                                                                 // フェーズのみ変更。BGM は止めない。
738                                                                 this._演奏ステージ.現在のフェーズ.Value = ステージ.演奏.演奏ステージ.フェーズ.ビュアーメッセージ待機中;
739                                                         }
740                                                         //----------------
741                                                         #endregion
742                                                 }
743                                                 else
744                                                 {
745                                                         // (B) 通常モード
746
747                                                         #region " 演奏終了 → 結果ステージへ。"
748                                                         //--------------------
749                                                         if( this._演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.クリアor失敗 )
750                                                         {
751                                                                 this._現在のステージ.非活性化する( this._デバイスリソース );
752                                                                 this._現在のステージ = this._結果ステージ;
753                                                                 this._現在のステージ.活性化する( this._デバイスリソース );
754                                                         }
755                                                         //--------------------
756                                                         #endregion
757                                                         #region " キャンセル → 選曲ステージへ。"
758                                                         //--------------------
759                                                         if( this._演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.キャンセル )
760                                                         {
761                                                                 this._現在のステージ.非活性化する( this._デバイスリソース );
762                                                                 this._現在のステージ = this._選曲ステージ;
763                                                                 this._現在のステージ.活性化する( this._デバイスリソース );
764                                                         }
765                                                         //--------------------
766                                                         #endregion
767                                                 }
768                                                 break;
769
770                                         case nameof( ステージ.結果.結果ステージ ):
771                                                 #region " 終了 → 選曲ステージへ。"
772                                                 //--------------------
773                                                 if( this._結果ステージ.現在のフェーズ == ステージ.結果.結果ステージ.フェーズ.終了 )
774                                                 {
775                                                         this._現在のステージ.非活性化する( this._デバイスリソース );
776                                                         this._現在のステージ = this._選曲ステージ;
777                                                         this._現在のステージ.活性化する( this._デバイスリソース );
778                                                 }
779                                                 //--------------------
780                                                 #endregion
781                                                 break;
782                                 }
783                         }
784                         //----------------
785                         #endregion
786                 }
787
788                 private void デバイス依存リソースを解放する()
789                 {
790                         FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
791                         Debug.Assert( null != this._デバイスリソース );  // 解放前であること。
792
793                         this._現在のステージ?.デバイス依存リソースを解放する( this._デバイスリソース );
794
795                         FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
796                 }
797
798                 private void デバイス依存リソースを再構築する()
799                 {
800                         FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
801                         Debug.Assert( null != this._デバイスリソース );  // 再生成済みであること。
802
803                         this._現在のステージ?.デバイス依存リソースを作成する( this._デバイスリソース );
804
805                         FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
806                 }
807
808                 private void フォームサイズが変更された( object sender, EventArgs e )
809                 {
810                         FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
811                         FDK.Log.Info( $"新しいクライアントサイズ = {this._MainForm.ClientSize}" );
812
813                         #region " 実行条件チェック。"
814                         //----------------
815                         if( null == this._デバイスリソース )
816                         {
817                                 FDK.Log.Info( " まだ初期化されてないので、何もしません。" );
818                                 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
819                                 return;
820                         }
821                         if( this._MainForm.WindowState == FormWindowState.Minimized )
822                         {
823                                 FDK.Log.Info( "最小化されました。" );
824                                 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
825                                 return; // 何もしない
826                         }
827                         if( this._State != ApplicationState.進行描画 )
828                         {
829                                 FDK.Log.Info( " アプリケーションの状態が進行描画じゃないので、何もしません。" );
830                                 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
831                                 return;
832                         }
833                         //----------------
834                         #endregion
835
836                         Debug.Assert( null != this._デバイスリソース, "デバイスリソースが作成済みであること。" );
837
838                         // 現在の画面モードを取得しておく。(Alt+TABなど、勝手に全画面を解除されることもあるので。)
839                         SharpDX.Mathematics.Interop.RawBool fullscreen;
840                         SharpDX.DXGI.Output outputTarget;
841                         this._デバイスリソース.SwapChain1.GetFullscreenState( out fullscreen, out outputTarget );
842                         this._MainForm.IsFullscreen = fullscreen;
843                         outputTarget?.Dispose();
844                         FDK.Log.Info( $"現在、全画面モードである = {this._MainForm.IsFullscreen}" );
845
846                         // (1) リソースを解放して、
847                         this.デバイス依存リソースを解放する();
848                         this._デバイスリソース.サイズに依存するリソースを解放する();
849
850                         // (2) 物理画面サイズを変更して、
851                         this._デバイスリソース.物理画面サイズpx = new SharpDX.Size2F( this._MainForm.ClientSize.Width, this._MainForm.ClientSize.Height );
852
853                         // (3) リソースを再構築する。
854                         this._デバイスリソース.サイズに依存するリソースを作成する();
855                         this.デバイス依存リソースを再構築する();
856
857                         FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
858                 }
859
860                 private void 全画面モードとウィンドウモードを切り替える()
861                 {
862                         FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
863
864                         this._MainForm.IsFullscreen = !this._MainForm.IsFullscreen;   // 切り替え
865                         this._デバイスリソース.SwapChain1.SetFullscreenState( this._MainForm.IsFullscreen, null );
866
867                         FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
868                 }
869
870                 private void ログインする( string ユーザ名 )
871                 {
872                         StrokeStyleT.ユーザ管理.ユーザを選択する( ユーザ名 );
873                         FDK.Log.Info( $"ユーザが選択されました。[{StrokeStyleT.ユーザ管理.現在選択されているユーザ.名前}]" );
874                 }
875
876                 /// <returns>
877                 ///             メッセージがない場合は null を返す。
878                 ///     </returns>
879                 private ViewerMessage ビュアーメッセージを取得する()
880                 {
881                         var msg = (ViewerMessage) null;
882
883                         if( StrokeStyleT.ビュアーモードである &&
884                                 ( 0 < this._ビュアーメッセージキュー.Count ) &&
885                                 this._ビュアーメッセージキュー.TryDequeue( out msg ) )
886                         {
887                                 return msg;
888                         }
889
890                         return null;
891                 }
892         }
893 }