X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=StrokeStyleT%2FStrokeStyleT.cs;h=607fe88094decf69af7472b9cd2ac8f7fa208e1c;hb=28329767c64d76e5606efeeb685a25a13c6a157b;hp=49e13821448ba58a5934caf7f40f0c79cc1b2ebc;hpb=d09565f8aee5a5899105fd1cedea2f6e32ca4fd9;p=strokestylet%2FCsWin10Desktop3.git diff --git a/StrokeStyleT/StrokeStyleT.cs b/StrokeStyleT/StrokeStyleT.cs index 49e1382..607fe88 100644 --- a/StrokeStyleT/StrokeStyleT.cs +++ b/StrokeStyleT/StrokeStyleT.cs @@ -1,23 +1,66 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Linq; +using System.ServiceModel; using System.Windows.Forms; +using FDK; namespace SST { - class StrokeStyleT : FDK.ApplicationFormBase + [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] // サービスインターフェースをシングルスレッドで呼び出す。 + class StrokeStyleT : SST.IStrokeStyleTService { // グローバルリソース (static) - public static SST.フォルダ フォルダ => StrokeStyleT.bs_フォルダ; - public static FDK.入力.Keyboard キーボード入力 => StrokeStyleT.bs_キーボード入力; - public static FDK.入力.MidiIn MIDI入力 => StrokeStyleT.bs_MIDI入力; - public static FDK.メディア.サウンド.WASAPI排他.ExclusiveDevice Wasapiデバイス => StrokeStyleT.bs_Wasapiデバイス; - public static Random 乱数 => StrokeStyleT.bs_乱数; - public static SST.ユーザ.ユーザ管理 ユーザ管理 => StrokeStyleT.bs_ユーザ管理; - public static SST.曲.曲ツリー管理 曲ツリー管理 => StrokeStyleT.bs_曲ツリー管理; - public static SSTFormat.スコア 演奏スコア { get; set; } = null; + + public static SST.フォルダ フォルダ + { + get { return StrokeStyleT.bs_フォルダ; } + } + public static FDK.メディア.サウンド.WASAPI排他.ExclusiveDevice Wasapiデバイス + { + get { return StrokeStyleT.bs_Wasapiデバイス; } + } + public static System.Random 乱数 + { + get { return StrokeStyleT.bs_乱数; } + } + public static SST.ユーザ.ユーザ管理 ユーザ管理 + { + get { return StrokeStyleT.bs_ユーザ管理; } + } + public static SST.曲.曲ツリー管理 曲ツリー管理 + { + get { return StrokeStyleT.bs_曲ツリー管理; } + } + public static SSTFormat.スコア 演奏スコア + { + get; + set; + } = null; + public static SST.設定.Config Config + { + get { return StrokeStyleT.bs_Config; } + } + public static bool ビュアーモードである + { + get; + set; + } = false; + public static bool ビュアーモードではない + { + get { return !StrokeStyleT.ビュアーモードである; } + set { StrokeStyleT.ビュアーモードである = !value; } + } + public static FDK.入力.Keyboard キーボード入力 + { + get { return StrokeStyleT.bs_キーボード入力; } + } + public static FDK.入力.MidiIn MIDI入力 + { + get { return StrokeStyleT.bs_MIDI入力; } + } public static void すべての入力デバイスをポーリングする() { @@ -26,7 +69,6 @@ namespace SST StrokeStyleT.MIDI入力?.ポーリングする(); } - // get only static property の初期化。 static StrokeStyleT() { // フォルダ変数を真っ先に登録する。(ほかのメンバのコンストラクタでフォルダ変数を利用できるようにするため。) @@ -34,24 +76,121 @@ namespace SST SST.フォルダ.フォルダ変数を追加する( "Static", StrokeStyleT.フォルダ.StaticFolder ); SST.フォルダ.フォルダ変数を追加する( "AppData", StrokeStyleT.フォルダ.AppDataFolder ); SST.フォルダ.フォルダ変数を追加する( "User", null ); + } + public StrokeStyleT() + { + #region " ビュアーモードかどうかを確認する。" + //---------------- + foreach( var arg in Environment.GetCommandLineArgs() ) + { + if( ( "-v" == arg.ToLower() ) || ( "-viewer" == arg.ToLower() ) ) + { + StrokeStyleT.ビュアーモードである = true; + break; + } + } + //---------------- + #endregion - // その他の static の生成。 - StrokeStyleT.bs_ユーザ管理 = new ユーザ.ユーザ管理(); - StrokeStyleT.bs_曲ツリー管理 = new 曲.曲ツリー管理(); + this.State = ApplicationState.起動; + + this.MainForm = new SharpDX.Windows.RenderForm(); + this.MainForm.AllowUserResizing = false; // ユーザはフォームサイズを変更できない。 + this.MainForm.UserResized += フォームサイズが変更された; + } + + public void Run() + { + Debug.Assert( null != this.MainForm ); + + SharpDX.Windows.RenderLoop.Run( this.MainForm, () => { + + // アプリケーションの状態に応じて処理分岐。 + switch( this.State ) + { + case ApplicationState.起動: + this.初期化する(); + this.State = ApplicationState.進行描画; + break; + + case ApplicationState.進行描画: + this.進行描画する(); + if( this.State == ApplicationState.終了 ) + this.終了する(); + break; + + case ApplicationState.終了: + // 何もしない + break; + } + + } ); } - public bool 垂直帰線待ちを行う { get; set; } = true; + #region " WCF サービスインターフェースの実装 " + //---------------- + // ・このサービスインターフェースは、シングルスレッド(GUIスレッド)で同期実行される。(StrokeStyleT クラスの ServiceBehavior属性を参照。) + // ・このサービスホストはシングルトンであり、すべてのクライアントセッションは同一(単一)のサービスインスタンスへ接続される。(Program.Main() を参照。) + + public void ViewerPlay( string path, int startPart = 0, bool drumsSound = true ) + { + if( StrokeStyleT.ビュアーモードではない ) + return; + + this.ビュアーメッセージキュー.Enqueue( new ViewerMessage() { + 種別 = ViewerMessageType.演奏開始, + 曲ファイルパス = path, + 演奏開始小節番号 = startPart, + ドラムチップ発声 = drumsSound, + } ); + } + public void ViewerStop() + { + if( StrokeStyleT.ビュアーモードではない ) + return; - public override void 初期化する() + this.ビュアーメッセージキュー.Enqueue( new ViewerMessage() { + 種別 = ViewerMessageType.演奏停止, + } ); + } + public float GetSoundDelay() { - Debug.Assert( null == this.デバイスリソース ); // 作成される前である。 - FDK.Log.現在のスレッドに名前をつける( "GUI" ); + if( StrokeStyleT.ビュアーモードではない ) + return 0f; + + return ( null != StrokeStyleT.Wasapiデバイス ) ? StrokeStyleT.Wasapiデバイス.遅延ms : 0f; + } + //---------------- + #endregion - this.Text = $"StrokeStyle {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()}"; - this.ClientSize = new System.Drawing.Size( 1920, 1080 ); // 物理画面サイズpx - this.設計画面サイズdpx = new SharpDX.Size2F( 1920, 1080 ); // 設計画面サイズdpx + protected enum ApplicationState { 起動, 進行描画, 終了 } + protected ApplicationState State; + protected SharpDX.Windows.RenderForm MainForm = null; + protected SharpDX.Size2F 設計画面サイズdpx = SharpDX.Size2F.Empty; + protected ConcurrentQueue ビュアーメッセージキュー = new ConcurrentQueue(); + protected FDK.メディア.デバイスリソース デバイスリソース = null; + protected SST.ステージ.ステージ 最初のダミーステージ = null; + protected SST.ステージ.起動.起動ステージ 起動ステージ = null; + protected SST.ステージ.タイトル.タイトルステージ タイトルステージ = null; + protected SST.ステージ.ログイン.ログインステージ ログインステージ = null; + protected SST.ステージ.選曲.選曲ステージ 選曲ステージ = null; + protected SST.ステージ.曲読込.曲読込ステージ 曲読込ステージ = null; + protected SST.ステージ.演奏.演奏ステージ 演奏ステージ = null; + protected SST.ステージ.結果.結果ステージ 結果ステージ = null; + protected SST.ステージ.ステージ 現在のステージ = null; + + protected void 初期化する() + { + FDK.Log.現在のスレッドに名前をつける( "Render" ); + FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" ); - #region " System.Stopwatch が高解像度タイマを使わないならエラー。" + // 開始条件チェック。 + Debug.Assert( null == this.デバイスリソース, "デバイスリソースの作成前であること。" ); + + StrokeStyleT.bs_ユーザ管理 = new ユーザ.ユーザ管理(); + StrokeStyleT.bs_曲ツリー管理 = new 曲.曲ツリー管理(); + + #region " 高解像度タイマを使えないならエラー。" //----------------- if( false == System.Diagnostics.Stopwatch.IsHighResolution ) throw new SSTException( "このシステムは、高解像度タイマをサポートしていません。" ); @@ -64,13 +203,61 @@ namespace SST #endregion #region " Sleep 精度を上げる。" //----------------- - StrokeStyleT.timeBeginPeriod( 1 ); + Win32.timeBeginPeriod( 1 ); //----------------- #endregion + #region " コンフィグ を初期化する。" + //---------------- + StrokeStyleT.bs_Config = new 設定.Config(); + StrokeStyleT.bs_Config.ConfigXmlを読み込む(); + //---------------- + #endregion + + #region " コンフィグで指定されたウィンドウサイズに変更する。" + //---------------- + this.MainForm.ClientSize = new System.Drawing.Size( StrokeStyleT.Config.物理画面サイズpx.Width, StrokeStyleT.Config.物理画面サイズpx.Height ); + //---------------- + #endregion + #region " フォームタイトルと設計画面サイズを設定する。" + //---------------- + this.MainForm.Text = $"StrokeStyle {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()}"; + if( StrokeStyleT.ビュアーモードである ) + this.MainForm.Text += " (Viewer)"; + + this.設計画面サイズdpx = new SharpDX.Size2F( 1920f, 1080f ); + //---------------- + #endregion + + #region " デバイスリソースを作成する。" + //---------------- + FDK.Log.Info( $"設計画面サイズ: {this.設計画面サイズdpx}" ); + FDK.Log.Info( $"物理画面サイズ: {this.MainForm.ClientSize}" ); + this.デバイスリソース = new FDK.メディア.デバイスリソース( this.設計画面サイズdpx ); + this.デバイスリソース.すべてのリソースを作成する( this.MainForm.ClientSize, this.MainForm.Handle ); + //---------------- + #endregion + + #region " ステージを生成する。" + //---------------- + this.最初のダミーステージ = new ステージ.ステージ(); + this.起動ステージ = new ステージ.起動.起動ステージ(); + this.タイトルステージ = new ステージ.タイトル.タイトルステージ(); + this.ログインステージ = new ステージ.ログイン.ログインステージ(); + this.選曲ステージ = new ステージ.選曲.選曲ステージ(); + this.曲読込ステージ = new ステージ.曲読込.曲読込ステージ(); + this.演奏ステージ = new ステージ.演奏.演奏ステージ(); + this.結果ステージ = new ステージ.結果.結果ステージ(); + + // 外部依存アクションを接続する。 + this.曲読込ステージ.読込曲のファイルパスを取得する = () => ( ( StrokeStyleT.曲ツリー管理.現在選択されているノード as SST.曲.MusicNode )?.sstfファイルパス ); + this.結果ステージ.演奏ステージインスタンスを取得する = () => ( this.演奏ステージ ); + this.結果ステージ.BGMを終了する = () => { this.演奏ステージ.BGMを解放する(); }; + //---------------- + #endregion #region " ユーザを初期化する。" //----------------- - FDK.Log.Info( "ユーザ情報を初期化します。" ); + // Users.xml を読み込む。 StrokeStyleT.ユーザ管理.UsersXmlを読み込む(); // ユーザ別の初期化。 @@ -86,35 +273,45 @@ namespace SST #endregion #region " キーボード入力 を初期化する。" //----------------- - FDK.Log.Info( "キーボード入力デバイスを初期化します。" ); - StrokeStyleT.bs_キーボード入力 = new FDK.入力.Keyboard( this.Handle ); + StrokeStyleT.bs_キーボード入力 = new FDK.入力.Keyboard( this.MainForm.Handle ); //----------------- #endregion #region " MIDI入力 を初期化する。" //----------------- - FDK.Log.Info( "MIDI入力デバイスを初期化します。" ); StrokeStyleT.bs_MIDI入力 = new FDK.入力.MidiIn(); //----------------- #endregion + FDK.Log.Info( "最初のダミーステージを開始します。" ); this.現在のステージ = this.最初のダミーステージ; -#warning テストコード。 - this.KeyDown += ( target, arg ) => { - // Alt+Enter → 画面モードの切り替え - if( ( arg.KeyCode == System.Windows.Forms.Keys.Return ) && ( arg.Modifiers == Keys.Alt ) ) + //#warning 全画面モード切替えを仮実装。 + this.MainForm.KeyDown += ( target, arg ) => { + + // F11 → 画面モードの切り替え + if( arg.KeyCode == System.Windows.Forms.Keys.F11 ) + { this.全画面モードとウィンドウモードを切り替える(); + arg.Handled = true; + } }; + + // 終了条件チェック。 + Debug.Assert( SharpDX.Size2F.Empty != this.設計画面サイズdpx, "設計画面サイズが設定されていません。" ); + + FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" ); } - public override void 終了する() + protected void 終了する() { - Debug.Assert( null == this.デバイスリソース ); // 解放された後である。 + FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" ); + Debug.Assert( null != this.デバイスリソース, "デバイスリソースが解放される前であること。" ); #region " ステージを終了し、解放する。" //---------------- - //if( this.現在のステージ.活性化している ) - // this.現在のステージ.非活性化する( this.デバイスリソース ); → デバイスリソース解放後なので、ここで非活性化してはならない。 - this.最初のダミーステージ = null; + if( ( null != this.現在のステージ ) && ( this.現在のステージ.活性化している ) ) // 念のため + this.現在のステージ.非活性化する( this.デバイスリソース ); + + this.最初のダミーステージ = null; this.起動ステージ = null; this.タイトルステージ = null; this.ログインステージ = null; @@ -124,13 +321,20 @@ namespace SST this.結果ステージ = null; //---------------- #endregion - #region " MIDI入力 を解放する。" + + #region " デバイスリソースを解放する。" + //---------------- + FDK.Utilities.解放する( ref this.デバイスリソース ); + //---------------- + #endregion + + #region " MIDI入力デバイスを解放する。" //----------------- FDK.Log.Info( "MIDI入力デバイスを解放します。" ); FDK.Utilities.解放する( ref StrokeStyleT.bs_MIDI入力 ); //----------------- #endregion - #region " キーボード入力 を解放する。" + #region " キーボード入力デバイスを解放する。" //----------------- FDK.Log.Info( "キーボード入力デバイスを解放します。" ); FDK.Utilities.解放する( ref StrokeStyleT.bs_キーボード入力 ); @@ -138,57 +342,131 @@ namespace SST #endregion #region " WASAPIデバイスを解放する。" //---------------- + FDK.Log.Info( "WASAPIデバイスを解放します。" ); FDK.Utilities.解放する( ref StrokeStyleT.bs_Wasapiデバイス ); //---------------- #endregion + + #region " コンフィグを保存し、解放する。" + //---------------- + FDK.Log.Info( "コンフィグを解放します。" ); + StrokeStyleT.bs_Config.ConfigXmlを保存する(); + StrokeStyleT.bs_Config = null; + //---------------- + #endregion + #region " フォームを閉じる。" + //---------------- + FDK.Utilities.解放する( ref this.MainForm ); + //---------------- + #endregion + #region " MediaFoundation を終了する。" //----------------- SharpDX.MediaFoundation.MediaManager.Shutdown(); //----------------- #endregion + + FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" ); } - public override void シーンを描画する() + protected void 進行描画する() { - #region " 描画を準備する。" + #region " D3Dデバイスが消失していれば再構築する。" //---------------- - var d3dDevice = (SharpDX.Direct3D11.Device) null; - using( var d3dLock = new FDK.同期.AutoD3DDeviceLock( this.デバイスリソース.DXGIDeviceManager, out d3dDevice ) ) - using( d3dDevice ) - using( var d3dContext = d3dDevice.ImmediateContext ) + bool 異常発生 = false; + this.デバイスリソース.D3Dデバイスが消失していれば再構築する( out 異常発生 ); + + // 再構築不可能な異常の場合は、即終了する。 + if( 異常発生 ) { - // 既定のD3Dレンダーターゲットビューを黒でクリアする。 - d3dContext.ClearRenderTargetView( - this.デバイスリソース.D3DRenderTargetView, SharpDX.Color4.Black ); - - // 深度バッファを 1.0f でクリアする。 - d3dContext.ClearDepthStencilView( - this.デバイスリソース.D3DDepthStencilView, - SharpDX.Direct3D11.DepthStencilClearFlags.Depth, - depth: 1.0f, - stencil: 0 ); + this.State = ApplicationState.終了; + return; } //---------------- #endregion - - // 現在のステージを進行描画する。 + #region " 描画の前処理を行う。" + //---------------- + { + var d3dDevice = (SharpDX.Direct3D11.Device) null; + using( var d3dLock = new FDK.同期.AutoD3DDeviceLock( this.デバイスリソース.DXGIDeviceManager, out d3dDevice ) ) + using( d3dDevice ) + using( var d3dContext = d3dDevice.ImmediateContext ) + { + // 既定のD3Dレンダーターゲットビューを黒でクリアする。 + d3dContext.ClearRenderTargetView( this.デバイスリソース.D3DRenderTargetView, SharpDX.Color4.Black ); + + // 深度バッファを 1.0f でクリアする。 + d3dContext.ClearDepthStencilView( + this.デバイスリソース.D3DDepthStencilView, + SharpDX.Direct3D11.DepthStencilClearFlags.Depth, + depth: 1.0f, + stencil: 0 ); + } + } + //---------------- + #endregion + #region " 現在のステージを進行描画する。" + //---------------- this.現在のステージ?.進行描画する( this.デバイスリソース ); + //---------------- + #endregion + #region " スワップチェーンを表示する。" + //---------------- + if( StrokeStyleT.Config.垂直帰線待ちを行う ) + { + // We recommend that you use Flush when the CPU waits for an arbitrary amount of time + // (such as when you call the Sleep function). + // → https://msdn.microsoft.com/ja-jp/library/windows/desktop/ff476425(v=vs.85).aspx + + var d3dDevice = (SharpDX.Direct3D11.Device) null; + using( var d3dLock = new FDK.同期.AutoD3DDeviceLock( this.デバイスリソース.DXGIDeviceManager, out d3dDevice ) ) + using( d3dDevice ) + using( var d3dContext = d3dDevice.ImmediateContext ) + { + d3dContext.Flush(); + } + } - // 表示する。 - this.デバイスリソース.SwapChain.Present( - (this.垂直帰線待ちを行う) ? 1 : 0, + this.デバイスリソース.SwapChain1.Present( + ( StrokeStyleT.Config.垂直帰線待ちを行う ) ? 1 : 0, SharpDX.DXGI.PresentFlags.None ); + //---------------- + #endregion + #region " ステージの状態をチェックし、必要あれば遷移またはアプリを終了する。" + //---------------- if( null != this.現在のステージ ) { - // ステージの状態チェックを行い、必要あれば遷移、またはアプリを終了する。 switch( this.現在のステージ.GetType().Name ) { case nameof( ステージ.ステージ ): - #region " 最初のダミーステージ " + #region " ビュアーモード → AutoPlayerでログインして演奏ステージへ。" + //---------------- + if( StrokeStyleT.ビュアーモードである ) + { + // ビュアー用ユーザでログインする。 + FDK.Log.Info( "ビュアーモード: AutoPlayer ユーザでログインします。" ); + this.ログインする( Properties.Resources.AUTOPLAYER ); + + // 曲読込ステージ向けの初期設定。 + StrokeStyleT.曲ツリー管理.現在の管理対象ツリー = null; + StrokeStyleT.曲ツリー管理.現在選択されているノード = null; + + // 演奏ステージ向けの初期設定。 + StrokeStyleT.演奏スコア = null; + + // 演奏ステージへ。 + this.演奏ステージ.活性化する( this.デバイスリソース ); + this.現在のステージ = this.演奏ステージ; + } //---------------- - // 起動ステージへ遷移する。 - this.起動ステージ.活性化する( this.デバイスリソース ); - this.現在のステージ = this.起動ステージ; + #endregion + #region " 通常モード → 起動ステージへ。" + //---------------- + else + { + this.起動ステージ.活性化する( this.デバイスリソース ); + this.現在のステージ = this.起動ステージ; + } //---------------- #endregion break; @@ -223,7 +501,7 @@ namespace SST { this.現在のステージ.非活性化する( this.デバイスリソース ); this.現在のステージ = null; - this.アプリを終了せよ = true; + this.State = ApplicationState.終了; } //--------------- #endregion @@ -234,17 +512,16 @@ namespace SST //--------------- if( this.ログインステージ.現在のフェーズ == ステージ.ログイン.ログインステージ.フェーズ.確定 ) { - // ログイン処理(1) ユーザリストに対してユーザを選択する。 - StrokeStyleT.ユーザ管理.ユーザを選択する( this.ログインステージ.ユーザインデックス ); - FDK.Log.Info( $"ユーザが選択されました。[{StrokeStyleT.ユーザ管理.現在選択されているユーザ.名前}]" ); + var user = StrokeStyleT.ユーザ管理.現在選択されているユーザ; - // ログイン処理(2) 選択ユーザの曲ツリーを構築する。 - StrokeStyleT.ユーザ管理.現在選択されているユーザ.曲を検索して曲ツリーを構築する(); + if( null != user ) + { + foreach( var path in user.曲の検索元フォルダパスのリスト ) + SST.曲.曲ツリー管理.フォルダから曲を再帰的に検索して子ノードリストに追加する( user.曲ツリーのルートノード, path ); - // ログイン処理(3) 構築した曲ツリーを曲リスト管理に登録する。 - StrokeStyleT.曲ツリー管理.現在の管理対象ツリー = StrokeStyleT.ユーザ管理.現在選択されているユーザ.曲ツリーのルートノード; + StrokeStyleT.曲ツリー管理.現在の管理対象ツリー = StrokeStyleT.ユーザ管理.現在選択されているユーザ.曲ツリーのルートノード; + } - // 選曲ステージへ。 this.現在のステージ.非活性化する( this.デバイスリソース ); this.現在のステージ = this.選曲ステージ; this.現在のステージ.活性化する( this.デバイスリソース ); @@ -282,7 +559,7 @@ namespace SST { this.現在のステージ.非活性化する( this.デバイスリソース ); this.現在のステージ = null; - this.アプリを終了せよ = true; + this.State = ApplicationState.終了; } //--------------- #endregion @@ -293,9 +570,6 @@ namespace SST //-------------------- if( this.曲読込ステージ.現在のフェーズ == ステージ.曲読込.曲読込ステージ.フェーズ.終了 ) { - // 演奏スコアインスタンスが作成されていることを確認。 - Debug.Assert( null != StrokeStyleT.演奏スコア ); - this.現在のステージ.非活性化する( this.デバイスリソース ); this.現在のステージ = this.演奏ステージ; this.現在のステージ.活性化する( this.デバイスリソース ); @@ -305,26 +579,113 @@ namespace SST break; case nameof( ステージ.演奏.演奏ステージ ): - #region " 演奏終了 → 結果ステージへ。" - //-------------------- - if( this.演奏ステージ.現在のフェーズ == ステージ.演奏.演奏ステージ.フェーズ.クリアor失敗 ) + if( StrokeStyleT.ビュアーモードである ) { - this.現在のステージ.非活性化する( this.デバイスリソース ); - this.現在のステージ = this.結果ステージ; - this.現在のステージ.活性化する( this.デバイスリソース ); + // (A) ビュアーモード + + #region " ビュアーメッセージがあれば処理する。" + //---------------- + var msg = this.ビュアーメッセージを取得する(); + + if( null != msg ) + { + #region " (A) 演奏開始 " + //---------------- + if( msg.種別 == ViewerMessageType.演奏開始 && ( + this.演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.ビュアーメッセージ待機中 || + this.演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.演奏中 ) + ) + { + // MusicNode を作成する。 + var node = (SST.曲.MusicNode) null; + try + { + node = new 曲.MusicNode( msg.曲ファイルパス ); + } + catch + { + FDK.Log.ERROR( $"MusicNode の作成に失敗しました。" ); + node = null; + } + + // 作成に成功したときのみ次へ進む。 + if( null != node ) + { + // 演奏を停止する。 + this.演奏ステージ.非活性化する( this.デバイスリソース ); + this.演奏ステージ.BGMを解放する(); + + // 現在選択されているノードとして登録する。 + StrokeStyleT.曲ツリー管理.現在選択されているノード = node; + + // todo: 演奏開始小節番号を保存し、反映する。 + + // 曲読込ステージへ。 + this.現在のステージ = this.曲読込ステージ; + this.曲読込ステージ.活性化する( this.デバイスリソース ); + } + } + //---------------- + #endregion + #region " (B) 演奏停止 " + //---------------- + else if( msg.種別 == ViewerMessageType.演奏停止 && ( + this.演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.ビュアーメッセージ待機中 || + this.演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.演奏中 ) + ) + { + this.演奏ステージ.演奏を停止する(); + this.演奏ステージ.現在のフェーズ.Value = ステージ.演奏.演奏ステージ.フェーズ.ビュアーメッセージ待機中; + } + //---------------- + #endregion + #region " その他 " + //---------------- + else + { + // その他のメッセージは無視。 + } + //---------------- + #endregion + } + //---------------- + #endregion + #region " クリア/キャンセル → メッセージ待機へ。" + //---------------- + if( this.演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.クリアor失敗 || + this.演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.キャンセル ) + { + // フェーズのみ変更。BGM は止めない。 + this.演奏ステージ.現在のフェーズ.Value = ステージ.演奏.演奏ステージ.フェーズ.ビュアーメッセージ待機中; + } + //---------------- + #endregion } - //-------------------- - #endregion - #region " キャンセル → 選曲ステージへ。" - //-------------------- - if( this.演奏ステージ.現在のフェーズ == ステージ.演奏.演奏ステージ.フェーズ.キャンセル ) + else { - this.現在のステージ.非活性化する( this.デバイスリソース ); - this.現在のステージ = this.選曲ステージ; - this.現在のステージ.活性化する( this.デバイスリソース ); + // (B) 通常モード + + #region " 演奏終了 → 結果ステージへ。" + //-------------------- + if( this.演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.クリアor失敗 ) + { + this.現在のステージ.非活性化する( this.デバイスリソース ); + this.現在のステージ = this.結果ステージ; + this.現在のステージ.活性化する( this.デバイスリソース ); + } + //-------------------- + #endregion + #region " キャンセル → 選曲ステージへ。" + //-------------------- + if( this.演奏ステージ.現在のフェーズ.Value == ステージ.演奏.演奏ステージ.フェーズ.キャンセル ) + { + this.現在のステージ.非活性化する( this.デバイスリソース ); + this.現在のステージ = this.選曲ステージ; + this.現在のステージ.活性化する( this.デバイスリソース ); + } + //-------------------- + #endregion } - //-------------------- - #endregion break; case nameof( ステージ.結果.結果ステージ ): @@ -341,37 +702,107 @@ namespace SST break; } } + //---------------- + #endregion } - protected override void OnClosing( CancelEventArgs e ) + protected void デバイス依存リソースを解放する() { - if( ( null != this.現在のステージ ) && ( this.現在のステージ.活性化している ) ) - this.現在のステージ.非活性化する( this.デバイスリソース ); + FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" ); + Debug.Assert( null != this.デバイスリソース ); // 解放前であること。 - base.OnClosing( e ); - } - protected override void OnSizeChanged( EventArgs e ) - { - // すべてのサイズ依存リソースを解放する。 this.現在のステージ?.デバイス依存リソースを解放する( this.デバイスリソース ); - // デバイス等の変更処理を行う。 - base.OnSizeChanged( e ); + FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" ); + } + protected void デバイス依存リソースを再構築する() + { + FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" ); + Debug.Assert( null != this.デバイスリソース ); // 再生成済みであること。 - // すべてのサイズ依存リソースを作成し直す。 this.現在のステージ?.デバイス依存リソースを作成する( this.デバイスリソース ); + + FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" ); } - // 各ステージの、唯一のインスタンス。 - protected SST.ステージ.ステージ 最初のダミーステージ = new ステージ.ステージ(); - protected SST.ステージ.起動.起動ステージ 起動ステージ = new ステージ.起動.起動ステージ(); - protected SST.ステージ.タイトル.タイトルステージ タイトルステージ = new ステージ.タイトル.タイトルステージ(); - protected SST.ステージ.ログイン.ログインステージ ログインステージ = new ステージ.ログイン.ログインステージ(); - protected SST.ステージ.選曲.選曲ステージ 選曲ステージ = new ステージ.選曲.選曲ステージ(); - protected SST.ステージ.曲読込.曲読込ステージ 曲読込ステージ = new ステージ.曲読込.曲読込ステージ(); - protected SST.ステージ.演奏.演奏ステージ 演奏ステージ = new ステージ.演奏.演奏ステージ(); - protected SST.ステージ.結果.結果ステージ 結果ステージ = new ステージ.結果.結果ステージ(); + private void フォームサイズが変更された( object sender, EventArgs e ) + { + FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" ); + FDK.Log.Info( $"新しいクライアントサイズ = {this.MainForm.ClientSize}" ); - private SST.ステージ.ステージ 現在のステージ = null; + #region " 実行条件チェック。" + //---------------- + if( null == this.デバイスリソース ) + { + FDK.Log.Info( " まだ初期化されてないので、何もしません。" ); + FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" ); + return; + } + if( this.MainForm.WindowState == FormWindowState.Minimized ) + { + FDK.Log.Info( "最小化されました。" ); + FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" ); + return; // 何もしない + } + if( this.State != ApplicationState.進行描画 ) + { + FDK.Log.Info( " アプリケーションの状態が進行描画じゃないので、何もしません。" ); + FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" ); + return; + } + //---------------- + #endregion + + Debug.Assert( null != this.デバイスリソース, "デバイスリソースが作成済みであること。" ); + + // 現在の画面モードを取得しておく。(Alt+TABなど、勝手に全画面を解除されることもあるので。) + SharpDX.Mathematics.Interop.RawBool fullscreen; + SharpDX.DXGI.Output outputTarget; + this.デバイスリソース.SwapChain1.GetFullscreenState( out fullscreen, out outputTarget ); + this.MainForm.IsFullscreen = fullscreen; + outputTarget?.Dispose(); + FDK.Log.Info( $"現在、全画面モードである = {this.MainForm.IsFullscreen}" ); + + // (1) リソースを解放して、 + this.デバイス依存リソースを解放する(); + this.デバイスリソース.サイズに依存するリソースを解放する(); + + // (2) 物理画面サイズを変更して、 + this.デバイスリソース.物理画面サイズpx = new SharpDX.Size2F( this.MainForm.ClientSize.Width, this.MainForm.ClientSize.Height ); + + // (3) リソースを再構築する。 + this.デバイスリソース.サイズに依存するリソースを作成する(); + this.デバイス依存リソースを再構築する(); + + FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" ); + } + private void 全画面モードとウィンドウモードを切り替える() + { + FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" ); + + this.MainForm.IsFullscreen = !this.MainForm.IsFullscreen; // 切り替え + this.デバイスリソース.SwapChain1.SetFullscreenState( this.MainForm.IsFullscreen, null ); + + FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" ); + } + private void ログインする( string ユーザ名 ) + { + StrokeStyleT.ユーザ管理.ユーザを選択する( ユーザ名 ); + FDK.Log.Info( $"ユーザが選択されました。[{StrokeStyleT.ユーザ管理.現在選択されているユーザ.名前}]" ); + } + /// メッセージがない場合は null を返す。 + private ViewerMessage ビュアーメッセージを取得する() + { + var msg = (ViewerMessage) null; + + if( StrokeStyleT.ビュアーモードである && + ( 0 < this.ビュアーメッセージキュー.Count ) && + this.ビュアーメッセージキュー.TryDequeue( out msg ) ) + { + return msg; + } + + return null; + } #region " バックストア。" //---------------- @@ -379,20 +810,11 @@ namespace SST private static FDK.入力.Keyboard bs_キーボード入力 = null; private static FDK.入力.MidiIn bs_MIDI入力 = null; private static FDK.メディア.サウンド.WASAPI排他.ExclusiveDevice bs_Wasapiデバイス = null; - private static readonly Random bs_乱数 = new Random( DateTime.Now.Millisecond ); + private static readonly System.Random bs_乱数 = new Random( DateTime.Now.Millisecond ); private static SST.ユーザ.ユーザ管理 bs_ユーザ管理 = null; private static SST.曲.曲ツリー管理 bs_曲ツリー管理 = null; + private static SST.設定.Config bs_Config = null; //---------------- #endregion - - #region " Win32 API " - //----------------- - [System.Runtime.InteropServices.DllImport( "winmm.dll", EntryPoint = "timeBeginPeriod" )] - private static extern uint timeBeginPeriod( uint uMilliseconds ); - - [System.Runtime.InteropServices.DllImport( "winmm.dll", EntryPoint = "timeEndPeriod" )] - private static extern uint timeEndPeriod( uint uMilliseconds ); - //----------------- - #endregion } }