OSDN Git Service

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