/// 子リストに Activity を登録すると、活性化・非活性化・デバイス依存リソースの生成・解放が、親と連動するようになる。
/// </summary>
/// <remarks>
- /// 子リストには静的・動的の2種類があり、それぞれ以下のように使い分ける。
+ /// 子リストには静的・動的の2種類があり、それぞれ以下のように使い分ける。
///
- /// (A) メンバとして定義する静的な子の場合:
- /// ・子Activity の生成と子リストへの追加は、親Activity のコンストラクタで行う。
- /// ・子リストからの削除は不要。
+ /// (A) メンバとして定義する静的な子の場合:
+ /// ・子Activity の生成と子リストへの追加は、親Activity のコンストラクタで行う。
+ /// ・子リストからの削除は不要。
///
- /// (B) 活性化時に生成する動的な子の場合:
- /// ・子Activity の生成と子リストへの追加は、親Activity の On活性化() で行う。
- /// ・子リストからの削除は、親Activity の On非活性化() で行う。
- ///
+ /// (B) 活性化時に生成する動的な子の場合:
+ /// ・子Activity の生成と子リストへの追加は、親Activity の On活性化() で行う。
+ /// ・子リストからの削除は、親Activity の On非活性化() で行う。
/// </remarks>
- public List<Activity> 子リスト { get; } = new List<Activity>();
-
- public bool 活性化している { get; private set; } = false; // 派生クラスからも設定は禁止。
+ public List<Activity> 子リスト
+ {
+ get;
+ } = new List<Activity>();
+ public bool 活性化している
+ {
+ get;
+ private set;
+ } = false; // 派生クラスからも設定は禁止。
public bool 活性化していない
{
- get { return !this.活性化している; }
- private set { this.活性化している = !value; } // 派生クラスからも設定は禁止。
- }
+ get
+ => !( this.活性化している );
- public bool デバイス依存リソースが生成されている { get; private set; } = false; // 派生クラスからも設定は禁止。
+ private set
+ => this.活性化している = !( value ); // 派生クラスからも設定は禁止。
+ }
+ public bool デバイス依存リソースが生成されている
+ {
+ get;
+ private set;
+ } = false; // 派生クラスからも設定は禁止。
public bool デバイス依存リソースが生成されていない
{
- get { return !this.デバイス依存リソースが生成されている; }
- private set { this.デバイス依存リソースが生成されている = !value; } // 派生クラスからも設定は禁止。
+ get
+ => !( this.デバイス依存リソースが生成されている );
+
+ private set
+ => this.デバイス依存リソースが生成されている = !( value ); // 派生クラスからも設定は禁止。
}
/// <summary>
// (3) すべての子Activityを活性化し、デバイス依存リソースを作成する。
foreach( var child in this.子リスト )
+ {
child.活性化する( dr ); // この中でデバイス依存リソースが作成される。
+ }
}
/// <summary>
// (1) すべての子Activityのデバイス依存リソースを解放し、非活性化する。
foreach( var child in this.子リスト )
- child.非活性化する( dr ); // この中でデバイス依存リソースも解放される。
+ {
+ child.非活性化する( dr ); // この中でデバイス依存リソースも解放される。
+ }
// (2) 自分のデバイス依存リソースを解放する。
this.デバイス依存リソースを解放する( dr, 子へ再帰する: false ); // 自分だけ。
{
// (2) すべての子Activityのデバイス依存リソースを生成する。
foreach( var child in this.子リスト )
+ {
child.デバイス依存リソースを作成する( dr, 子へ再帰する );
+ }
}
}
{
// (1) すべての子Activityのデバイス依存リソースを解放する。
foreach( var child in this.子リスト )
+ {
child.デバイス依存リソースを解放する( dr, 子へ再帰する );
+ }
}
// (2) 自分のデバイス依存リソースを解放する。
protected virtual void On活性化( デバイスリソース dr )
{
}
-
protected virtual void On非活性化( デバイスリソース dr )
{
}
-
protected virtual void Onデバイス依存リソースの作成( デバイスリソース dr )
{
}
-
protected virtual void Onデバイス依存リソースの解放( デバイスリソース dr )
{
}
/// 文字列が Null でも空でもないなら true を返す。
/// </summary>
public static bool Nullでも空でもない( this string 検査対象 )
- {
- return !string.IsNullOrEmpty( 検査対象 );
- }
-
+ => !string.IsNullOrEmpty( 検査対象 );
+
/// <summary>
/// 文字列が Null または空なら true を返す。
/// </summary>
public static bool Nullまたは空である( this string 検査対象 )
- {
- return string.IsNullOrEmpty( 検査対象 );
- }
+ => string.IsNullOrEmpty( 検査対象 );
}
}
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CSCore, Version=1.1.5992.18249, Culture=neutral, PublicKeyToken=5a08f2b6f4415dea, processorArchitecture=MSIL">
- <HintPath>..\packages\CSCore.1.1.0\lib\net35-client\CSCore.dll</HintPath>
- <Private>True</Private>
+ <Reference Include="CSCore, Version=1.1.6245.30570, Culture=neutral, PublicKeyToken=5a08f2b6f4415dea, processorArchitecture=MSIL">
+ <HintPath>..\packages\CSCore.1.2.0\lib\net35-client\CSCore.dll</HintPath>
</Reference>
<Reference Include="SharpDX, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<HintPath>..\packages\SharpDX.3.1.1\lib\net45\SharpDX.dll</HintPath>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
+ <Reference Include="System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+ <HintPath>..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
+ </Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
</EmbeddedResource>
<EmbeddedResource Include="メディア\テクスチャ用シェーダコード.hlsl" />
</ItemGroup>
- <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Trace.TraceInformation( $"{Log._日時とスレッドID}{Log._インデックスを返す( Log._深さ )}{出力}" );
}
}
-
- public static void ERROR( string 出力 )
+ public static void WARNING( string 出力 )
{
lock( Log._スレッド間同期 )
{
Log._一定時間が経過していたら区切り線を表示する();
- Trace.TraceError( $"{Log._日時とスレッドID} {出力}" );
+ Trace.TraceWarning( $"{Log._日時とスレッドID} {出力}" );
}
}
-
- public static void ERRORandTHROW( string 出力, Exception inner = null )
- {
- ERROR( 出力 );
- throw new FDKException( 出力, inner );
- }
-
- public static void WARNING( string 出力 )
+ public static void ERROR( string 出力 )
{
lock( Log._スレッド間同期 )
{
Log._一定時間が経過していたら区切り線を表示する();
- Trace.TraceWarning( $"{Log._日時とスレッドID} {出力}" );
+ Trace.TraceError( $"{Log._日時とスレッドID} {出力}" );
}
}
+ public static void ERRORandTHROW( string 出力, Exception inner = null )
+ {
+ Log.ERROR( 出力 );
+ throw new FDKException( 出力, inner );
+ }
public static void BeginInfo( string 開始ブロック名 )
{
Log._深さ += 4;
}
}
-
public static void EndInfo( string 終了ブロック名 )
{
lock( Log._スレッド間同期 )
}
private const double _最小区切り時間 = 2.0; // 区切り線を入れる最小の間隔[秒]。
-
private static string _日時とスレッドID
{
get
return $"{DateTime.Now.ToLongTimeString()} [{NETスレッドID:00},0x{Win32スレッドID:x}{スレッド識別文字列}]";
}
}
-
private static readonly Dictionary<uint, string> _IDto名前 = new Dictionary<uint, string>();
-
private static Dictionary<string, DateTime> _識別キー別最終表示時刻 = new Dictionary<string, DateTime>();
-
private static TimeSpan _経過時間
{
get
return 経過時間;
}
}
-
private static DateTime _最終表示時刻 = DateTime.Now;
-
private static int _深さ = 0;
-
private static readonly object _スレッド間同期 = new object();
private static void _一定時間が経過していたら区切り線を表示する()
{
if( Log._最小区切り時間 < Log._経過時間.TotalSeconds )
+ {
Trace.TraceInformation( "・・・" );
+ }
}
-
private static string _インデックスを返す( int 長さ )
{
string index = " ";
return index;
}
- #region " Win32 API "
+ #region " Win32 "
//-----------------
[System.Runtime.InteropServices.DllImport( "kernel32.dll" )]
private static extern uint GetCurrentThreadId();
/// </param>
public static void D2DBatchDraw( SharpDX.Direct2D1.RenderTarget target, Action 描画処理 )
{
- target.BeginDraw();
try
{
+ target.BeginDraw();
描画処理();
}
finally
return n;
}
-
public static int 最小公倍数を返す( int m, int n )
{
if( ( 0 >= m ) || ( 0 >= n ) )
public static double 変換_100ns単位からsec単位へ( long 数値100ns )
{
- return 数値100ns / ( 10.0 * 1000.0 * 1000.0 );
+ return 数値100ns / 10_000_000.0;
}
-
public static long 変換_sec単位から100ns単位へ( double 数値sec )
{
- return (long) ( 数値sec * 10.0 * 1000.0 * 1000.0 + 0.5 ); // +0.5 で四捨五入できる。
+ return ( long ) ( 数値sec * 10_000_000.0 + 0.5 ); // +0.5 で四捨五入できる。
}
/// <summary>
/// <remarks>
/// (参考: http://qiita.com/oguna/items/c516e09ee57d931892b6 )
/// </remarks>
- public static SharpDX.Direct3D11.ShaderResourceView CreateShaderResourceViewFromFile(
+ public static (SharpDX.Direct3D11.ShaderResourceView srv, Size2F viewSize, SharpDX.Direct3D11.Texture2D texture) CreateShaderResourceViewFromFile(
SharpDX.Direct3D11.Device d3dDevice,
SharpDX.Direct3D11.BindFlags bindFlags,
- string 画像ファイルパス,
- out Size2F ビューのサイズ,
- out SharpDX.Direct3D11.Texture2D テクスチャ )
+ string 画像ファイルパス )
{
- ビューのサイズ = new Size2F( 0, 0 );
- var srv = (SharpDX.Direct3D11.ShaderResourceView) null;
+ var 出力 = (
+ srv:(SharpDX.Direct3D11.ShaderResourceView) null,
+ viewSize: new Size2F(0,0),
+ texture: (SharpDX.Direct3D11.Texture2D) null );
+
using( var image = new System.Drawing.Bitmap( 画像ファイルパス ) )
{
var 画像の矩形 = new System.Drawing.Rectangle( 0, 0, image.Width, image.Height );
+
using( var bitmap = image.Clone( 画像の矩形, System.Drawing.Imaging.PixelFormat.Format32bppArgb ) )
{
var ロック領域 = bitmap.LockBits( 画像の矩形, System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat );
};
var texture = new SharpDX.Direct3D11.Texture2D( d3dDevice, textureDesc, dataBox );
bitmap.UnlockBits( ロック領域 );
- srv = new SharpDX.Direct3D11.ShaderResourceView( d3dDevice, texture );
-
- // 戻り値2:テクスチャ
- テクスチャ = texture;
+ 出力.srv = new SharpDX.Direct3D11.ShaderResourceView( d3dDevice, texture );
+ 出力.texture = texture;
}
- // 戻り値1:ビューのサイズ
- ビューのサイズ = new Size2F( 画像の矩形.Width, 画像の矩形.Height );
+
+ 出力.viewSize = new Size2F( 画像の矩形.Width, 画像の矩形.Height );
}
- // 戻り値3:シェーダリソースビュー
- return srv;
+
+ return 出力;
}
/// <summary>
/// 空のテクスチャとそのシェーダーリソースビューを作成し、返す。
/// </summary>
- public static SharpDX.Direct3D11.ShaderResourceView CreateShaderResourceView(
+ public static (SharpDX.Direct3D11.ShaderResourceView srv, SharpDX.Direct3D11.Texture2D texture) CreateShaderResourceView(
SharpDX.Direct3D11.Device d3dDevice,
SharpDX.Direct3D11.BindFlags bindFlags,
- Size2 サイズdpx,
- out SharpDX.Direct3D11.Texture2D テクスチャ )
+ Size2 サイズdpx )
{
var textureDesc = new SharpDX.Direct3D11.Texture2DDescription() {
ArraySize = 1,
SampleDescription = new SharpDX.DXGI.SampleDescription( 1, 0 ),
Usage = SharpDX.Direct3D11.ResourceUsage.Default
};
- var texture = new SharpDX.Direct3D11.Texture2D( d3dDevice, textureDesc );
- // 戻り値1:テクスチャ
- テクスチャ = texture;
+ var 出力 = ( srv: (SharpDX.Direct3D11.ShaderResourceView) null, texture: (SharpDX.Direct3D11.Texture2D) null );
+ 出力.texture = new SharpDX.Direct3D11.Texture2D( d3dDevice, textureDesc );
+ 出力.srv = new SharpDX.Direct3D11.ShaderResourceView( d3dDevice, 出力.texture );
- // 戻り値2:シェーダリソースビュー
- return new SharpDX.Direct3D11.ShaderResourceView( d3dDevice, texture );
+ return 出力;
}
}
}
\ No newline at end of file
}
public XmlSchema GetSchema()
- {
- return null;
- }
+ => null;
public void ReadXml( XmlReader reader )
{
while( XmlNodeType.EndElement != reader.NodeType )
{
- var kv = s.Deserialize( reader ) as KeyValue;
- if( null != kv )
+ if( s.Deserialize( reader ) is KeyValue kv )
+ {
this.Add( kv.Key, kv.Value );
+ }
}
reader.Read();
var s = new XmlSerializer( typeof( KeyValue ) );
foreach( var key in Keys )
+ {
s.Serialize( writer, new KeyValue( key, this[ key ] ) );
+ }
}
}
}
{
public static void インスタンスをシリアライズしてファイルに保存する<T>( string strXMLファイル名, T target )
{
- var ファイル名 = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( strXMLファイル名 );
+ var ファイル名 = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( strXMLファイル名 );
using( var sw = new StreamWriter(
new FileStream( ファイル名, FileMode.Create, FileAccess.Write, FileShare.ReadWrite ),
public static T ファイルをデシリアライズしてインスタンスを生成する<T>( string strXMLファイル名 )
{
- var ファイル名 = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( strXMLファイル名 );
+ var ファイル名 = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( strXMLファイル名 );
using( var sr = new StreamReader(
new FileStream( ファイル名, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ),
Encoding.UTF8 ) )
{
- return (T) new XmlSerializer( typeof( T ) ).Deserialize( sr );
+ return ( T ) new XmlSerializer( typeof( T ) ).Deserialize( sr );
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="CSCore" version="1.1.0" targetFramework="net452" />
+ <package id="CSCore" version="1.2.0" targetFramework="net452" />
<package id="SharpDX" version="3.1.1" targetFramework="net452" />
<package id="SharpDX.D3DCompiler" version="3.1.1" targetFramework="net452" />
<package id="SharpDX.Desktop" version="3.1.1" targetFramework="net452" />
<package id="SharpDX.DXGI" version="3.1.1" targetFramework="net452" />
<package id="SharpDX.Mathematics" version="3.1.1" targetFramework="net452" />
<package id="SharpDX.MediaFoundation" version="3.1.1" targetFramework="net452" />
+ <package id="System.ValueTuple" version="4.3.0" targetFramework="net452" />
</packages>
\ No newline at end of file
}
}
}
-
public int 現在のVPS
{
get
this._現在のVPS = 0;
}
+ /// <summary>
+ /// FPSをカウントUPして、「現在のFPS, 現在のVPS」プロパティに現在の値をセットする。
+ /// VPSはカウントUPされない。
+ /// </summary>
public void FPSをカウントしプロパティを更新する()
{
lock( this._スレッド間同期 )
}
}
+ /// <summary>
+ /// VPSをカウントUPする。
+ /// 「現在のFPS, 現在のVPS」プロパティは更新しない。
+ /// FPSはカウントUPされない。
+ /// </summary>
public void VPSをカウントする()
{
lock( this._スレッド間同期 )
}
private int _現在のFPS = 0;
-
private int _現在のVPS = 0;
-
private int _fps用カウンタ = 0;
-
private int _vps用カウンタ = 0;
-
private 定間隔進行 _定間隔進行 = null;
-
private bool _初めてのFPS更新 = true;
-
private readonly object _スレッド間同期 = new object();
}
}
/// </summary>
public const long 未使用 = -1;
- public static long 周波数 => Stopwatch.Frequency;
-
- public static long 生カウント => Stopwatch.GetTimestamp();
-
- public static double 生カウント相対値を秒へ変換して返す( long 生カウント相対値 )
+ public static long 周波数
{
- return (double) 生カウント相対値 / QPCTimer.周波数;
+ get
+ => Stopwatch.Frequency;
}
-
- public static long 秒をカウントに変換して返す( double 秒 )
+ public static long 生カウント
{
- return (long) ( 秒 * QPCTimer.周波数 );
+ get
+ => Stopwatch.GetTimestamp();
}
-
- public QPCTimer()
+ public static double 生カウント相対値を秒へ変換して返す( long 生カウント相対値 )
{
- var now = QPCTimer.生カウント;
-
- this._前回リセットした時点の生カウント = now;
- this._最後にキャプチャされたカウント = now;
- this._稼働中に一時停止した時点のキャプチャカウント = now;
-
- this._一時停止回数 = 0;
+ return ( double ) 生カウント相対値 / QPCTimer.周波数;
}
-
- public long 現在のカウントをキャプチャする()
+ public static long 秒をカウントに変換して返す( double 秒 )
{
- lock( this._スレッド間同期 )
- {
- this._最後にキャプチャされたカウント = QPCTimer.生カウント;
- return this._最後にキャプチャされたカウント;
- }
+ return ( long ) ( 秒 * QPCTimer.周波数 );
}
public long 現在のキャプチャカウント
{
lock( this._スレッド間同期 )
{
- return ( 0 != this._一時停止回数 ) ?
- ( this._稼働中に一時停止した時点のキャプチャカウント - this._前回リセットした時点の生カウント ) : // 停止中
- ( this._最後にキャプチャされたカウント - this._前回リセットした時点の生カウント ); // 稼働中
+ if( 0 != this._一時停止回数 )
+ {
+ // 停止中。
+ return ( this._稼働中に一時停止した時点のキャプチャカウント - this._前回リセットした時点の生カウント );
+ }
+ else
+ {
+ // 稼働中。
+ return ( this._最後にキャプチャされたカウント - this._前回リセットした時点の生カウント );
+ }
}
}
}
-
- public double 現在のキャプチャカウントsec
+ public long 現在のキャプチャカウント100ns
{
- get { return (double) this.現在のキャプチャカウント / QPCTimer.周波数; }
+ get
+ => 10_000_000 * this.現在のキャプチャカウント / QPCTimer.周波数;
}
-
- public long 現在のキャプチャカウント100ns
+ public double 現在のキャプチャカウントsec
{
- get { return ( 1000 * 1000 * 10 * this.現在のキャプチャカウント / QPCTimer.周波数 ); }
+ get
+ => ( double ) this.現在のキャプチャカウント / QPCTimer.周波数;
}
public long 現在のリアルタイムカウント
{
lock( this._スレッド間同期 )
{
- return ( 0 != this._一時停止回数 ) ?
- ( this._稼働中に一時停止した時点のキャプチャカウント - this._前回リセットした時点の生カウント ) : // 停止中
- ( QPCTimer.生カウント - this._前回リセットした時点の生カウント ); // 稼働中
+ if( 0 != this._一時停止回数 )
+ {
+ // 停止中。
+ return ( this._稼働中に一時停止した時点のキャプチャカウント - this._前回リセットした時点の生カウント );
+ }
+ else
+ {
+ // 稼働中。
+ return ( QPCTimer.生カウント - this._前回リセットした時点の生カウント );
+ }
}
}
}
-
- public double 現在のリアルタイムカウントsec
+ public long 現在のリアルタイムカウント100ns
{
- get { return (double) this.現在のリアルタイムカウント / QPCTimer.周波数; }
+ get
+ => 10_000_000 * this.現在のリアルタイムカウント / QPCTimer.周波数;
}
-
- public long 現在のリアルタイムカウント100ns
+ public double 現在のリアルタイムカウントsec
{
- get { return ( 1000 * 1000 * 10 * this.現在のリアルタイムカウント / QPCTimer.周波数 ); }
+ get
+ => ( double ) this.現在のリアルタイムカウント / QPCTimer.周波数;
}
public long 前回リセットした時点の生カウント
{
lock( this._スレッド間同期 )
{
- return ( 0 != this._一時停止回数 ) ? true : false;
+ return ( 0 != this._一時停止回数 );
}
}
}
+ public bool 稼働中である
+ {
+ get
+ => !( this.停止中である );
+ }
- public bool 稼働中である => !this.停止中である;
+ public QPCTimer()
+ {
+ var 現在の生カウント = QPCTimer.生カウント;
+ this._前回リセットした時点の生カウント = 現在の生カウント;
+ this._最後にキャプチャされたカウント = 現在の生カウント;
+ this._稼働中に一時停止した時点のキャプチャカウント = 現在の生カウント;
+
+ this._一時停止回数 = 0;
+ }
+ public long 現在のカウントをキャプチャする()
+ {
+ lock( this._スレッド間同期 )
+ {
+ this._最後にキャプチャされたカウント = QPCTimer.生カウント;
+ return this._最後にキャプチャされたカウント;
+ }
+ }
public void リセットする( long 新しいカウント = 0 )
{
lock( this._スレッド間同期 )
this._前回リセットした時点の生カウント = QPCTimer.生カウント - 新しいカウント;
}
}
-
public void 一時停止する()
{
lock( this._スレッド間同期 )
{
- if( 0 == this._一時停止回数 ) // 稼働中である
+ if( this.稼働中である )
{
this._稼働中に一時停止した時点のキャプチャカウント = this._最後にキャプチャされたカウント;
}
+
this._一時停止回数++;
}
}
-
public void 再開する()
{
lock( this._スレッド間同期 )
{
- if( 0 < this._一時停止回数 ) // 停止中である
+ if( this.停止中である )
{
this._一時停止回数--;
}
private long _前回リセットした時点の生カウント = 0;
-
private long _最後にキャプチャされたカウント = 0;
-
private long _稼働中に一時停止した時点のキャプチャカウント = 0;
-
private int _一時停止回数 = 0;
-
private readonly object _スレッド間同期 = new object();
}
}
public class 単純増加後不変カウンタ
{
/// <summary>
- /// カウンタを進行し、現在の値を取得する。
+ /// カウンタの開始値。
+ /// 常に終了値以下。
/// </summary>
- public int 現在値
+ public int 開始値
{
get
{
lock( this._スレッド間同期 )
{
- this._進行する();
- return this._現在値;
+ return this._開始値;
}
}
}
/// <summary>
- /// カウンタを進行し、現在の値を割合(0.0:開始値 ~ 1.0:終了値)に変換して取得する。
+ /// カウンタの終了値。
+ /// 常に開始値以上。
/// </summary>
- public float 現在値の割合
+ public int 終了値
{
get
{
lock( this._スレッド間同期 )
{
- Debug.Assert( 0 != ( this._終了値 - this._開始値 ) );
- this._進行する();
- return (float) ( this._現在値 - this._開始値 ) / (float) ( this._終了値 - this._開始値 );
+ return this._終了値;
}
}
}
- public int 開始値
+ /// <summary>
+ /// カウンタを進行し、現在の値を返す。
+ /// </summary>
+ public int 現在値
{
get
{
lock( this._スレッド間同期 )
{
- return this._開始値;
+ this._カウンタを進行する();
+ return this._現在値;
}
}
}
- public int 終了値
+ /// <summary>
+ /// カウンタを進行し、現在の値を 0.0~1.0 の割合値に変換して返す。
+ /// </summary>
+ /// <return>
+ /// 0.0 (開始値) ~ 1.0 (終了値)
+ /// </return>
+ public float 現在値の割合
{
get
{
lock( this._スレッド間同期 )
{
- return this._終了値;
+ if( this._開始値 != this._終了値 )
+ {
+ this._カウンタを進行する();
+ return ( float ) ( this._現在値 - this._開始値 ) / ( float ) ( this._終了値 - this._開始値 );
+ }
+ else
+ {
+ return 1.0f; // 開始値 = 終了値 なら常に 1.0 とする。
+ }
}
}
}
{
lock( this._スレッド間同期 )
{
- this._進行する(); // 終了してるかどうか判定する前に、溜まってる進行を全部消化する。
+ this._カウンタを進行する(); // 終了してるかどうか判定する前に、溜まってる進行を全部消化する。
return this._動作中;
}
}
}
-
+
/// <summary>
/// カウンタの進行が一時停止されているなら true を返す。
/// (終了値に達しているかどうかは無関係。)
/// </summary>
- public bool 停止中である => !( this.動作中である );
+ public bool 停止中である
+ {
+ get
+ => !( this.動作中である );
+ }
/// <summary>
/// カウンタを進行し、その結果、現在値が終了値に達していたら true を返す。
{
lock( this._スレッド間同期 )
{
- this._進行する();
+ this._カウンタを進行する();
return ( this._現在値 >= this._終了値 );
}
}
/// <summary>
/// カウンタを進行し、その結果、まだ現在値が終了値に達していないら true を返す。
/// </summary>
- public bool 終了値に達していない => !( this.終了値に達した );
+ public bool 終了値に達していない
+ {
+ get
+ => !( this.終了値に達した );
+ }
/// <summary>
+ /// コンストラクタ。
/// 初期化のみ行い、カウンタは開始しない。
/// </summary>
public 単純増加後不変カウンタ()
{
this._間隔ms = QPCTimer.未使用;
this._定間隔進行 = null;
-
this._開始値 = 0;
this._終了値 = 0;
this._現在値 = 0;
}
/// <summary>
+ /// コンストラクタ。
/// 初期化し、同時にカウンタを開始する。
/// </summary>
- public 単純増加後不変カウンタ( int 最初の値, int 最後の値, long 値をひとつ増加させるのにかける時間ms = 1000 ) : this()
+ public 単純増加後不変カウンタ( int 最初の値, int 最後の値, long 値をひとつ増加させるのにかける時間ms = 1000 )
+ : this()
{
this.開始する( 最初の値, 最後の値, 値をひとつ増加させるのにかける時間ms );
}
+ /// <summary>
+ /// カウンタの現在値を最初の値に設定し、カウンタの進行を開始する。
+ /// </summary>
public void 開始する( int 最初の値, int 最後の値, long 値をひとつ増加させるのにかける時間ms = 1000 )
{
lock( this._スレッド間同期 )
}
}
+ /// <remarks>
+ /// 一時停止中は、カウンタを進行させても、現在値が進まない。
+ /// </remarks>
public void 一時停止する()
{
lock( this._スレッド間同期 )
}
}
+ /// <summary>
+ /// 一時停止しているカウンタを、再び進行できるようにする。
+ /// </summary>
public void 再開する()
{
lock( this._スレッド間同期 )
}
private int _開始値 = 0;
-
private int _終了値 = 0;
-
private int _現在値 = 0;
-
private bool _動作中 = false;
-
private long _間隔ms = QPCTimer.未使用;
-
private 定間隔進行 _定間隔進行 = null;
-
private readonly object _スレッド間同期 = new object();
/// <summary>
/// 前回のこのメソッドの呼び出しからの経過時間をもとに、必要なだけ現在値を増加させる。
/// カウント値が終了値に達している場合は、それ以上増加しない(終了値を維持する)。
/// </summary>
- private void _進行する()
+ private void _カウンタを進行する()
{
if( this._間隔ms == QPCTimer.未使用 )
return; // 開始されていないなら無視。
if( this._動作中 )
{
- if( this._現在値 < this._終了値 ) // 終了値以降、現在値は不変。
+ if( this._現在値 < this._終了値 )
+ {
this._現在値++;
+ }
+ else
+ {
+ // 終了値以降、現在値は不変。
+ }
}
} );
public class 単純増加後反復カウンタ
{
/// <summary>
- /// カウンタを進行し、現在の値を取得する。
+ /// カウンタの開始値。
+ /// 常に終了値以下。
/// </summary>
- public int 現在値
+ public int 開始値
{
get
{
lock( this._スレッド間同期 )
{
- this._進行する();
- return this._現在値;
+ return this._開始値;
}
}
}
/// <summary>
- /// カウンタを進行し、現在の値を割合(0.0:開始値 ~ 1.0:終了値)に変換して取得する。
+ /// カウンタの終了値。
+ /// 常に開始値以上。
/// </summary>
- public float 現在値の割合
+ public int 終了値
{
get
{
lock( this._スレッド間同期 )
{
- Debug.Assert( 0 != ( this._終了値 - this._開始値 ) );
- this._進行する();
- return (float) ( this._現在値 - this._開始値 ) / (float) ( this._終了値 - this._開始値 );
+ return this._終了値;
}
}
}
- public int 開始値
+ /// <summary>
+ /// カウンタを進行し、現在の値を返す。
+ /// </summary>
+ public int 現在値
{
get
{
lock( this._スレッド間同期 )
{
- return this._開始値;
+ this._カウンタを進行する();
+ return this._現在値;
}
}
}
- public int 終了値
+ /// <summary>
+ /// カウンタを進行し、現在の値を 0.0~1.0 の割合値に変換して返す。
+ /// </summary>
+ /// <return>
+ /// 0.0 (開始値) ~ 1.0 (終了値)
+ /// </return>
+ public float 現在値の割合
{
get
{
lock( this._スレッド間同期 )
{
- return this._終了値;
+ if( this._開始値 != this._終了値 )
+ {
+ this._カウンタを進行する();
+ return ( float ) ( this._現在値 - this._開始値 ) / ( float ) ( this._終了値 - this._開始値 );
+ }
+ else
+ {
+ return 1.0f; // 開始値 = 終了値 なら常に 1.0 とする。
+ }
}
}
}
{
lock( this._スレッド間同期 )
{
- this._進行する(); // 終了してるかどうか判定する前に、溜まってる進行を全部消化する。
+ this._カウンタを進行する(); // 終了してるかどうか判定する前に、溜まってる進行を全部消化する。
return this._動作中;
}
}
/// カウンタの進行が一時停止されているなら true を返す。
/// (終了値に達しているかどうかは別問題。)
/// </summary>
- public bool 停止中である => !( this.動作中である );
+ public bool 停止中である
+ {
+ get
+ => !( this.動作中である );
+ }
/// <summary>
+ /// コンストラクタ。
/// 初期化のみ行い、カウンタは開始しない。
/// </summary>
public 単純増加後反復カウンタ()
{
this._間隔ms = QPCTimer.未使用;
this._定間隔進行 = null;
-
this._開始値 = 0;
this._終了値 = 0;
this._現在値 = 0;
}
/// <summary>
+ /// コンストラクタ。
/// 初期化し、同時にカウンタを開始する。
/// </summary>
- public 単純増加後反復カウンタ( int 最初の値, int 最後の値, long 値をひとつ増加させるのにかける時間ms = 1000 ) : this()
+ public 単純増加後反復カウンタ( int 最初の値, int 最後の値, long 値をひとつ増加させるのにかける時間ms = 1000 )
+ : this()
{
this.開始する( 最初の値, 最後の値, 値をひとつ増加させるのにかける時間ms );
}
+ /// <summary>
+ /// カウンタの現在値を最初の値に設定し、カウンタの進行を開始する。
+ /// </summary>
public void 開始する( int 最初の値, int 最後の値, long 値をひとつ増加させるのにかける時間ms = 1000 )
{
lock( this._スレッド間同期 )
}
}
+ /// <remarks>
+ /// 一時停止中は、カウンタを進行させても、現在値が進まない。
+ /// </remarks>
public void 一時停止する()
{
lock( this._スレッド間同期 )
}
}
+ /// <summary>
+ /// 一時停止しているカウンタを、再び進行できるようにする。
+ /// </summary>
public void 再開する()
{
lock( this._スレッド間同期 )
}
private int _開始値 = 0;
-
private int _終了値 = 0;
-
private int _現在値 = 0;
-
private bool _動作中 = false;
-
private long _間隔ms = QPCTimer.未使用;
-
private 定間隔進行 _定間隔進行 = null;
-
private readonly object _スレッド間同期 = new object();
/// <summary>
/// 前回のこのメソッドの呼び出しからの経過時間をもとに、必要なだけ現在値を増加させる。
/// カウント値が終了値に達している場合は、開始値に繰り戻す。
/// </summary>
- private void _進行する()
+ private void _カウンタを進行する()
{
if( this._間隔ms == QPCTimer.未使用 )
return; // 開始されていないなら無視。
{
this._現在値++;
- // çµ\82äº\86å\80¤ã\82\92è¶\85ã\81\88ã\81¦ã\81\84ã\82\8cã\81°é\96\8bå§\8bå\80¤ã\81«æ\88»ã\82\8b。
+ // çµ\82äº\86å\80¤ã\82\92è¶\85ã\81\88ã\81\9fã\82\89é\96\8bå§\8bå\80¤ã\81«æ\88»ã\82\8bï¼\88å\8f\8d復ï¼\89。
if( this._現在値 > this._終了値 )
this._現在値 = this._開始値;
}
public class 定間隔進行
{
/// <summary>
+ /// コンストラクタ。
/// 初期化し、同時に間隔の計測も開始する。
/// </summary>
public 定間隔進行()
this._タイマ.リセットする();
}
}
-
public void 経過時間の計測を一時停止する()
{
lock( this._スレッド間同期 )
this._タイマ.一時停止する();
}
}
-
public void 経過時間の計測を再開する()
{
lock( this._スレッド間同期 )
this._タイマ.再開する();
}
}
-
public void 経過時間の分だけ進行する( long 間隔ms, Action 定間隔処理 )
{
lock( this._スレッド間同期 )
// 初めての進行の場合、前回時刻を初期化する。
if( this._前回の進行時刻ms == QPCTimer.未使用 )
+ {
this._前回の進行時刻ms = 現在時刻ms;
+ }
// (ないと思うが)タイマが一回りしてしまった時のための保護。正常動作を保証するものではない。
if( this._前回の進行時刻ms > 現在時刻ms )
+ {
this._前回の進行時刻ms = 現在時刻ms;
+ }
// 経過時間における回数だけ、処理を実行する。
while( ( 現在時刻ms - this._前回の進行時刻ms ) >= 間隔ms )
}
private long _前回の進行時刻ms = QPCTimer.未使用;
-
private readonly QPCTimer _タイマ = new QPCTimer();
-
private readonly object _スレッド間同期 = new object();
}
}
{
// 引数チェック
if( null == 変換したいフォルダの絶対パス )
- return 変換したいフォルダの絶対パス;
+ return null;
if( false == Path.IsPathRooted( 基点フォルダの絶対パス ) )
throw new FDKException( $"指定された基点フォルダが絶対パスではありません。[{基点フォルダの絶対パス}]" );
// Uri をデコードして返す。'/' は '\' に置換する。
return Uri.UnescapeDataString( 変換後uri.ToString() ).Replace( oldChar: '/', newChar: '\\' );
}
-
public static void フォルダ変数を追加する( string 変数名, string 置換するパス文字列 )
{
フォルダ._フォルダ変数toパス[ 変数名 ] = 置換するパス文字列;
}
-
public static void フォルダ変数を削除する( string 変数名 )
{
if( フォルダ._フォルダ変数toパス.ContainsKey( 変数名 ) )
+ {
フォルダ._フォルダ変数toパス.Remove( 変数名 );
+ }
else
+ {
throw new FDKException( $"指定されたフォルダ変数「{変数名}」は存在しません。" );
+ }
}
-
public static string 絶対パスに含まれるフォルダ変数を展開して返す( string path )
{
- if( null != path )
+ if( null == path )
+ return null;
+
+ foreach( var kvp in フォルダ._フォルダ変数toパス )
{
- foreach( var kvp in フォルダ._フォルダ変数toパス )
- {
- if( kvp.Value.Nullまたは空である() )
- continue;
- path = path.Replace( "$(" + kvp.Key + ")", kvp.Value );
- }
+ if( kvp.Value.Nullまたは空である() )
+ continue;
+
+ path = path.Replace( "$(" + kvp.Key + ")", kvp.Value );
}
+
return path;
}
-
public static string 絶対パスをフォルダ変数付き絶対パスに変換して返す( string path )
{
- if( null != path )
+ if( null == path )
+ return null;
+
+ foreach( var kvp in フォルダ._フォルダ変数toパス )
{
- foreach( var kvp in フォルダ._フォルダ変数toパス )
- {
- if( kvp.Value.Nullまたは空である() )
- continue;
- path = path.Replace( kvp.Value, "$(" + kvp.Key + ")" );
- }
+ if( kvp.Value.Nullまたは空である() )
+ continue;
+
+ path = path.Replace( kvp.Value, "$(" + kvp.Key + ")" );
}
+
return path;
}
public class DecodedWaveSource : IWaveSource
{
/// <summary>
- /// シークは常にサポートする。
+ /// シーク能力があるなら true 。
/// </summary>
- public bool CanSeek => ( true );
+ public bool CanSeek
+ => true; // 常にサポートする。
/// <summary>
/// デコード後のオーディオデータの長さ[byte]。
/// </summary>
- public long Length => this._EncodedWaveData.Length;
+ public long Length
+ {
+ get
+ => this._EncodedWaveData.Length;
+ }
/// <summary>
/// 現在の位置。
/// </summary>
public long Position
{
- get { return this._Position; }
+ get
+ => this._Position;
+
set
{
- if( ( 0 > value ) || ( ( this.Length > 0 ) && ( this.Length <= value ) ) ) // Length == 0 なら例外は出さない
+ if( ( 0 > value ) || ( ( this.Length > 0 ) && ( this.Length <= value ) ) ) // ※ Length == 0 なら例外は出さない
throw new ArgumentOutOfRangeException();
this._Position = value;
/// <summary>
/// デコード後のオーディオデータのフォーマット。
/// </summary>
- public WaveFormat WaveFormat { get; protected set; }
+ public WaveFormat WaveFormat
+ {
+ get;
+ protected set;
+ }
- public string Path { get; } = null;
+ /// <summary>
+ /// コンストラクタで指定されたファイルパス。
+ /// </summary>
+ public string Path
+ {
+ get;
+ } = null;
/// <summary>
- /// メディアファイル(動画、音楽)をデコードする。
+ /// コンストラクタ。
+ /// 指定したメディアファイル(動画、音楽)をデコードする。
/// </summary>
public DecodedWaveSource( string path, WaveFormat targetFormat )
{
// ISampleSource は IWaveSource を 32bit-float に変換して出力する仕様なので、
// 最初からその形式でデコードして ISampleSource.Read() の変換負荷を下げる。
+
this.WaveFormat = new WaveFormat(
targetFormat.SampleRate, // サンプルレートと
32,
this._初期化する( path );
}
+ /// <summary>
+ /// 解放する。
+ /// </summary>
public void Dispose()
{
this._解放する();
/// </returns>
public int Read( byte[] buffer, int offset, int count )
{
- // 音がめちゃくちゃになるとうざいので、このメソッド内では例外を出さないこと。
+ // ※ 音がめちゃくちゃになるとうざいので、このメソッド内では例外を出さないこと。
if( ( null == this._EncodedWaveData ) && ( null == buffer ) )
return 0;
- // offset ã\81¯ã\80\810~buffer.Length-1 に収める。
+ // offset ã\82\92 0~buffer.Length-1 に収める。
offset = Math.Max( 0, Math.Min( buffer.Length - 1, offset ) );
// count は、_EncodeWaveData.Length-Position, buffer.Length-offset, count のうちの最小値とする。
}
private MFMediaType _MediaType = null;
-
private byte[] _EncodedWaveData = null;
-
private long _Position = 0;
+ /// <summary>
+ /// 指定されたファイルをデコードする。
+ /// </summary>
+ /// <param name="path">
+ /// サウンドファイル。
+ /// </param>
private void _初期化する( string path )
{
try
{
- // SourceReader は、SharpDX ではなく CSCore のものを使う。(MediaType から WaveFormat に一発で変換できるので。)
- using( var sourceReader = new MFSourceReader( path ) )
+ using( var sourceReader = new MFSourceReader( path ) ) // SourceReader は、SharpDX ではなく CSCore のものを使う。(MediaType から WaveFormat に一発で変換できるので。)
using( var waveStream = new MemoryStream() )
{
- #region " 最初のオーディオストリームを選択し、その他のすべてのストリームを非選択にする。"
- //----------------
- sourceReader.SetStreamSelection( (int) SourceReaderIndex.AllStreams, false );
- sourceReader.SetStreamSelection( (int) SourceReaderIndex.FirstAudioStream, true );
- //----------------
- #endregion
- #region " デコード後フォーマットを持つメディアタイプを作成し、SourceReader に登録する。"
- //----------------
+ // 最初のオーディオストリームを選択し、その他のすべてのストリームを非選択にする。
+
+ sourceReader.SetStreamSelection( ( int ) SourceReaderIndex.AllStreams, false );
+ sourceReader.SetStreamSelection( ( int ) SourceReaderIndex.FirstAudioStream, true );
+
+ // デコード後フォーマットを持つメディアタイプを作成し、SourceReader に登録する。
using( var partialMediaType = MFMediaType.FromWaveFormat( this.WaveFormat ) ) // WaveFormatEx にも対応。
{
// 作成したメディアタイプを sourceReader にセットする。必要なデコーダが見つからなかったら、ここで例外が発生する。
// 最初のオーディオストリームが選択されていることを保証する。
sourceReader.SetStreamSelection( (int) SourceReaderIndex.FirstAudioStream, true );
}
- //----------------
- #endregion
- #region " sourceReader からサンプルを取得してデコードし、waveStream へ書き込んだのち、byte[] _EncodedWaveData へ出力する。"
- //-----------------
+
+ // sourceReader からサンプルを取得してデコードし、waveStream へ書き込んだのち、byte[] _EncodedWaveData へ出力する。
while( true )
{
// 次のサンプルを読み込む。
// ストリームの内容を byte 配列に出力。
this._EncodedWaveData = waveStream.ToArray();
- //-----------------
- #endregion
}
}
catch( Exception e )
this._Position = 0;
}
-
private void _解放する()
{
Utilities.解放する( ref this._MediaType );
{
public class Device : IDisposable
{
- public PlaybackState レンダリング状態 => this._レンダリング状態;
+ public PlaybackState レンダリング状態
+ {
+ get
+ => this._レンダリング状態;
+ }
public double 遅延sec
{
get
- {
- return FDK.Utilities.変換_100ns単位からsec単位へ( this._遅延100ns );
- }
+ => Utilities.変換_100ns単位からsec単位へ( this._遅延100ns );
+
protected set
- {
- this._遅延100ns = FDK.Utilities.変換_sec単位から100ns単位へ( value );
- }
+ => this._遅延100ns = Utilities.変換_sec単位から100ns単位へ( value );
}
public long 遅延100ns
{
- get { return this._遅延100ns; }
- protected set { this._遅延100ns = value; }
+ get
+ => this._遅延100ns;
+
+ protected set
+ => this._遅延100ns = value;
}
- public WaveFormat WaveFormat => this._WaveFormat;
+ public WaveFormat WaveFormat
+ {
+ get
+ => this._WaveFormat;
+ }
/// <summary>
/// レンダリングボリューム。
public float 音量
{
get
- {
- return ( null != this._Mixer ) ? this._Mixer.Volume : 1.0f;
- }
+ => this._Mixer?.Volume ?? 1.0f;
+
set
{
if( ( 0.0f > value ) || ( 1.0f < value ) )
/// <summary>
/// コンストラクタ。
/// </summary>
- /// <param name="共有モード"></param>
- /// <param name="バッファサイズsec"></param>
- /// <param name="希望フォーマット"></param>
+ /// <param name="共有モード">
+ /// true なら共有モード、false なら排他モード。
+ /// </param>
public Device( AudioClientShareMode 共有モード, double バッファサイズsec = 0.010, WaveFormat 希望フォーマット = null )
{
this._共有モード = 共有モード;
this._レンダリング状態 = PlaybackState.Stopped;
this._初期化する( 希望フォーマット );
+ this.レンダリングを開始する();
- this.PlayRendering();
-
+ // ログ表示
Log.Info( $"WASAPIデバイスを初期化しました。" );
Log.Info( $" Mode: {this._共有モード}" );
Log.Info( $" Laytency: {this.遅延sec * 1000.0} ms" );
- var wfx = this.WaveFormat as WaveFormatExtensible;
- if( null == wfx )
- Log.Info( $" Format: {this.WaveFormat.WaveFormatTag}, {this.WaveFormat.SampleRate}Hz, {this.WaveFormat.Channels}ch, {this.WaveFormat.BitsPerSample}bits" );
- else
+ if( this.WaveFormat is WaveFormatExtensible wfx )
Log.Info( $" Format: {wfx.WaveFormatTag}[{AudioSubTypes.EncodingFromSubType( wfx.SubFormat )}], {wfx.SampleRate}Hz, {wfx.Channels}ch, {wfx.BitsPerSample}bits" );
+ else
+ Log.Info( $" Format: {this.WaveFormat.WaveFormatTag}, {this.WaveFormat.SampleRate}Hz, {this.WaveFormat.Channels}ch, {this.WaveFormat.BitsPerSample}bits" );
}
/// <summary>
/// メディアファイル(動画、音声)からサウンドインスタンスを生成して返す。
/// </summary>
- public Sound CreateSound( string path )
+ public Sound サウンドを生成する( string path )
{
return new Sound( path, this._Mixer );
}
/// <summary>
/// IWaveSource からサウンドインスタンスを生成して返す。
/// </summary>
- public Sound CreateSound( IWaveSource source )
+ public Sound サウンドを生成する( IWaveSource source )
{
return new Sound( source, this._Mixer );
}
/// ミキサーの出力を開始する。
/// 以降、ミキサーに Sound を追加すれば、自動的に再生される。
/// </summary>
- public void PlayRendering()
+ public void レンダリングを開始する()
{
lock( this._スレッド間同期 )
{
- if( this._レンダリング状態 == PlaybackState.Paused )
- {
- // Pause 中なら Resume する。
- this.ResumeRendering();
- }
- else if( this._レンダリング状態 == PlaybackState.Stopped )
+ switch( this._レンダリング状態 )
{
- using( var 起動完了通知 = new AutoResetEvent( false ) )
- {
- // スレッドがすでに終了していることを確認する。
- this._レンダリングスレッド?.Join();
-
- // レンダリングスレッドを起動する。
- this._レンダリングスレッド = new Thread( this._レンダリングスレッドエントリ ) {
- Name = "WASAPI Playback",
- Priority = ThreadPriority.AboveNormal, // 標準よりやや上
- };
- this._レンダリングスレッド.Start( 起動完了通知 );
-
- // スレッドからの起動完了通知を待つ。
- 起動完了通知.WaitOne();
- }
+ case PlaybackState.Paused:
+ this.レンダリングを再開する(); // Resume する。
+ break;
+
+ case PlaybackState.Stopped:
+ using( var 起動完了通知 = new AutoResetEvent( false ) )
+ {
+ // スレッドがすでに終了していることを確認する。
+ this._レンダリングスレッド?.Join();
+
+ // レンダリングスレッドを起動する。
+ this._レンダリングスレッド = new Thread( this._レンダリングスレッドエントリ ) {
+ Name = "WASAPI Playback",
+ Priority = ThreadPriority.AboveNormal, // 標準よりやや上
+ };
+ this._レンダリングスレッド.Start( 起動完了通知 );
+
+ // スレッドからの起動完了通知を待つ。
+ 起動完了通知.WaitOne();
+ }
+ break;
}
}
}
/// ミキサーの出力を停止する。
/// ミキサーに登録されているすべての Sound の再生が停止する。
/// </summary>
- public void StopRendering()
+ public void レンダリングを停止する()
{
lock( this._スレッド間同期 )
{
/// <summary>
/// ミキサーの出力を一時停止する。
/// ミキサーに登録されているすべての Sound の再生が一時停止する。
- /// ResumeRendering()で出力を再開できる。
/// </summary>
- public void PauseRendering()
+ public void レンダリングを一時停止する()
{
lock( this._スレッド間同期 )
{
- if( this.レンダリング状態 == PlaybackState.Playing )
- {
- this._レンダリング状態 = PlaybackState.Paused;
- Log.Info( "WASAPIのレンダリングを一時停止しました。" );
- }
- else
+ switch( this._レンダリング状態 )
{
- Log.WARNING( "WASAPIのレンダリングを一時停止しようとしましたが、すでに一時停止しています。" );
+ case PlaybackState.Playing:
+ this._レンダリング状態 = PlaybackState.Paused;
+ Log.Info( "WASAPIのレンダリングを一時停止しました。" );
+ break;
+
+ default:
+ Log.WARNING( "WASAPIのレンダリングを一時停止しようとしましたが、すでに一時停止しています。" );
+ break;
}
}
}
/// <summary>
/// ミキサーの出力を再開する。
- /// PauseRendering() で一時停止状態にあるときのみ有効。
+ /// 一時停止状態にあるときのみ有効。
/// </summary>
- public void ResumeRendering()
+ public void レンダリングを再開する()
{
lock( this._スレッド間同期 )
{
- if( this._レンダリング状態 == PlaybackState.Paused )
+ switch( this._レンダリング状態 )
{
- this._レンダリング状態 = PlaybackState.Playing;
- Log.Info( "WASAPIのレンダリングを再開しました。" );
- }
- else
- {
- Log.WARNING( "WASAPIのレンダリングを再開しようとしましたが、すでに再開されています。" );
+ case PlaybackState.Paused:
+ this._レンダリング状態 = PlaybackState.Playing;
+ Log.Info( "WASAPIのレンダリングを再開しました。" );
+ break;
+
+ default:
+ Log.WARNING( "WASAPIのレンダリングを再開しようとしましたが、すでに再開されています。" );
+ break;
}
}
}
{
this.GetClock( out long position, out long qpcPosition, out long frequency );
- if( 0.0 >= frequency )
- return double.NaN;
-
- return (double) position / frequency;
+ return ( 0 != frequency ) ?
+ ( ( double ) position / frequency ) : double.NaN;
}
}
- #region " Dispose-Finalize パターン "
- //----------------
~Device()
{
this.Dispose( false );
}
-
public void Dispose()
{
this.Dispose( true );
GC.SuppressFinalize( this );
}
-
protected virtual void Dispose( bool bDisposeManaged )
{
if( !this._dispose済み )
if( bDisposeManaged )
{
// (A) ここでマネージリソースを解放する。
- this.StopRendering();
+ this.レンダリングを停止する();
this._解放する();
}
this._dispose済み = true;
}
}
- //----------------
- #endregion
private volatile PlaybackState _レンダリング状態 = PlaybackState.Stopped;
-
private AudioClientShareMode _共有モード;
-
private long _遅延100ns = 0;
-
private WaveFormat _WaveFormat = null;
-
private AudioClock _AudioClock = null;
-
private AudioRenderClient _AudioRenderClient = null;
-
private AudioClient _AudioClient = null;
-
private MMDevice _MMDevice = null;
-
private Thread _レンダリングスレッド = null;
-
private EventWaitHandle _レンダリングイベント = null;
-
private Mixer _Mixer = null;
-
- private readonly object _スレッド間同期 = new object();
-
private bool _dispose済み = false;
+ private readonly object _スレッド間同期 = new object();
+ /// <summary>
+ /// サウンドデバイスリソースとミキサーを生成する。
+ /// </summary>
private void _初期化する( WaveFormat 希望フォーマット )
{
lock( this._スレッド間同期 )
if( this._レンダリング状態 != PlaybackState.Stopped )
throw new InvalidOperationException( "WASAPI のレンダリングを停止しないまま初期化することはできません。" );
+ // レンダリングスレッドが存在しているなら、終了するのを待つ。
this._レンダリングスレッド?.Join();
this._解放する();
// MMDevice を取得する。
this._MMDevice = MMDeviceEnumerator.DefaultAudioEndpoint(
- DataFlow.Render, // 方向:書き込み
- Role.Console ); // 用途:ゲーム、システム通知音、音声命令
+ DataFlow.Render, // 方向 ... 書き込み
+ Role.Console ); // 用途 ... ゲーム、システム通知音、音声命令
// AudioClient を取得する。
this._AudioClient = AudioClient.FromMMDevice( this._MMDevice );
// フォーマットを決定する。
- if( null == ( this._WaveFormat = this._適切なフォーマットを調べて返す( 希望フォーマット ) ) )
+ this._WaveFormat = this._適切なフォーマットを調べて返す( 希望フォーマット ) ??
throw new NotSupportedException( "サポート可能な WaveFormat が見つかりませんでした。" );
- // 遅延を既定値にする(共有モードの場合のみ)。
+ // 共有モードの場合、遅延を既定値に設定する。
if( this._共有モード == AudioClientShareMode.Shared )
+ {
this._遅延100ns = this._AudioClient.DefaultDevicePeriod;
+ }
// AudioClient を初期化する。
- void AudioClientを初期化するマクロ()
- {
- this._AudioClient.Initialize(
- this._共有モード,
- AudioClientStreamFlags.StreamFlagsEventCallback, // イベント駆動で固定。
- this._遅延100ns,
- this._遅延100ns, // イベント駆動の場合、Periodicity は BufferDuration と同じ値でなければならない。
- this._WaveFormat,
- Guid.Empty );
- };
try
{
- AudioClientを初期化するマクロ();
+ _AudioClientを初期化する();
}
catch( CoreAudioAPIException e )
{
if( e.ErrorCode == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED )
{
int サイズframe = this._AudioClient.GetBufferSize(); // アライメント済みサイズが取得できる。
- this._遅延100ns = (long) ( 10.0 * 1000.0 * 1000.0 * サイズframe / this._WaveFormat.SampleRate + 0.5 ); // +0.5 は四捨五入
+ this._遅延100ns = ( long ) ( 10.0 * 1000.0 * 1000.0 * サイズframe / this._WaveFormat.SampleRate + 0.5 ); // +0.5 は四捨五入
- AudioClientを初期化するマクロ(); // それでも例外なら知らん。
+ _AudioClientを初期化する(); // 再度初期化。それでも例外なら知らん。
}
else
{
// ミキサーを生成する。
this._Mixer = new Mixer( this._WaveFormat );
}
+
+ // 以下、ローカル関数。
+
+ void _AudioClientを初期化する()
+ {
+ this._AudioClient.Initialize(
+ this._共有モード,
+ AudioClientStreamFlags.StreamFlagsEventCallback, // イベント駆動で固定。
+ this._遅延100ns,
+ this._遅延100ns, // イベント駆動の場合、Periodicity は BufferDuration と同じ値でなければならない。
+ this._WaveFormat,
+ Guid.Empty );
+ }
}
+ /// <summary>
+ /// サウンドデバイスリソースとミキサーを解放する。
+ /// </summary>
private void _解放する()
{
Utilities.解放する( ref this._Mixer );
// (D) 共有モードのフォーマットも NG である場合は、以下から探す。
bool found = this._AudioClient.IsFormatSupported( AudioClientShareMode.Exclusive,
- new WaveFormat( 48000, 24, 2, AudioEncoding.Pcm ) {
- },
+ new WaveFormat( 48000, 24, 2, AudioEncoding.Pcm ),
out WaveFormat closest );
最終的に決定されたフォーマット = new[] {
private void _レンダリングスレッドエントリ( object 起動完了通知 )
{
var 例外 = (Exception) null;
- var avrtHandle = IntPtr.Zero;
+ var 元のMMCSS特性 = IntPtr.Zero;
try
{
#region " 初期化。"
//----------------
int バッファサイズframe = this._AudioClient.BufferSize;
- var バッファ = new float[ バッファサイズframe * this.WaveFormat.Channels ]; // 前提1・this._レンダリング先(ミキサー)の出力は 32bit-float で固定。
+ var バッファ = new float[ バッファサイズframe * this.WaveFormat.Channels ]; // 前提1・this._レンダリング先(ミキサー)の出力は 32bit-float で固定。
// このスレッドの MMCSS 型を登録する。
- string mmcssType = new[] {
- new { 最大遅延 = 0.0105, 型名 = "Pro Audio" }, // 優先度の高いものから。
- new { 最大遅延 = 0.0150, 型名 = "Games" },
+ string mmcssType;
+ switch( this.遅延sec )
+ {
+ case double 遅延 when( 0.0105 > 遅延 ):
+ mmcssType = "Pro Audio";
+ break;
+
+ case double 遅延 when( 0.0150 > 遅延 ):
+ mmcssType = "Games";
+ break;
+
+ default:
+ mmcssType = "Audio";
+ break;
}
- .FirstOrDefault( ( i ) => ( i.最大遅延 > this.遅延sec ) )?.型名 ?? "Audio";
- avrtHandle = Device.AvSetMmThreadCharacteristics( mmcssType, out int taskIndex );
+ 元のMMCSS特性 = Device.AvSetMmThreadCharacteristics( mmcssType, out int taskIndex );
// AudioClient を開始する。
this._AudioClient.Start();
起動完了通知 = null;
//----------------
#endregion
+
#region " メインループ。"
//----------------
var イベントs = new WaitHandle[] { this._レンダリングイベント };
+
while( this.レンダリング状態 != PlaybackState.Stopped )
{
+ // イベントs[] のいずれかのイベントが発火する(かタイムアウトする)まで待つ。
int イベント番号 = WaitHandle.WaitAny(
waitHandles: イベントs,
millisecondsTimeout: (int) ( 3000.0 * this.遅延sec ), // 適正値は レイテンシ×3 [ms] (MSDNより)
exitContext: false );
+ // タイムアウトした=まだどのイベントもきてない。
if( イベント番号 == WaitHandle.WaitTimeout )
continue;
int 未再生数frame = ( this._共有モード == AudioClientShareMode.Exclusive ) ? 0 : this._AudioClient.GetCurrentPadding();
int 空きframe = バッファサイズframe - 未再生数frame;
- if( 空きframe > 5 ) // あまりに空きが小さいならスキップする。
- {
- // レンダリング先からデータを取得して AudioRenderClient へ出力する。
+ if( 5 >= 空きframe )
+ continue; // あまりに空きが小さいなら、何もせずスキップする。
- int 読み込むサイズsample = 空きframe * this.WaveFormat.Channels; // 前提2・レンダリング先.WaveFormat と this.WaveFormat は同一。
- 読み込むサイズsample -= ( 読み込むサイズsample % ( this.WaveFormat.BlockAlign / this.WaveFormat.BytesPerSample ) ); // BlockAlign 境界にそろえる。
+ // レンダリング先からデータを取得して AudioRenderClient へ出力する。
- if( 0 < 読み込むサイズsample )
- {
- // ミキサーからの出力(32bit-float)をバッファに取得する。
- int 読み込んだサイズsample = this._Mixer.Read( バッファ, 0, 読み込むサイズsample );
+ int 読み込むサイズsample = 空きframe * this.WaveFormat.Channels; // 前提・レンダリング先.WaveFormat と this.WaveFormat は同一。
+ 読み込むサイズsample -= ( 読み込むサイズsample % ( this.WaveFormat.BlockAlign / this.WaveFormat.BytesPerSample ) ); // BlockAlign 境界にそろえる。
+ if( 0 >= 読み込むサイズsample )
+ continue; // サンプルなし。スキップ。
+
+ // ミキサーからの出力(32bit-float)をバッファに取得する。
+ int 読み込んだサイズsample = this._Mixer.Read( バッファ, 0, 読み込むサイズsample );
- // バッファのデータを変換しつつ、AudioRenderClient へ出力する。
- IntPtr bufferPtr = this._AudioRenderClient.GetBuffer( 空きframe );
- try
- {
- var encoding = AudioSubTypes.EncodingFromSubType( WaveFormatExtensible.SubTypeFromWaveFormat( this.WaveFormat ) );
+ // バッファのデータを変換しつつ、AudioRenderClient へ出力する。
+ IntPtr bufferPtr = this._AudioRenderClient.GetBuffer( 空きframe );
+ try
+ {
+ var encoding = AudioSubTypes.EncodingFromSubType( WaveFormatExtensible.SubTypeFromWaveFormat( this.WaveFormat ) );
- if( encoding == AudioEncoding.Pcm )
+ switch( encoding )
+ {
+ case AudioEncoding.IeeeFloat:
+ #region " FLOAT32 → FLOAT32 "
+ //----------------
+ Marshal.Copy( バッファ, 0, bufferPtr, 読み込んだサイズsample );
+ //----------------
+ #endregion
+ break;
+
+ case AudioEncoding.Pcm:
+ switch( this.WaveFormat.BitsPerSample )
{
- if( 24 == this.WaveFormat.BitsPerSample )
- {
- #region " (A) (Mixer)32bit-float → (AudioRenderClient)24bit-PCM の場合 "
+ case 24:
+ #region " FLOAT32 → PCM24 "
//----------------
- // 以下のコードでは、まだ、まともに再生できない。おそらくザーッという大きいノイズだらけの音になる。
- unsafe
{
- byte* ptr = (byte*) bufferPtr.ToPointer(); // AudioRenderClient のバッファは GC 対象外なのでピン止め不要。
-
- for( int i = 0; i < 読み込んだサイズsample; i++ )
+ // ※ 以下のコードでは、まだ、まともに再生できない。おそらくザーッという大きいノイズだらけの音になる。
+ unsafe
{
- float data = バッファ[ i ];
- if( -1.0f > data ) data = -1.0f;
- if( +1.0f < data ) data = +1.0f;
-
- uint sample32 = (uint) ( data * 8388608f - 1f ); // 24bit PCM の値域は -8388608~+8388607
- byte* psample32 = (byte*) &sample32;
- *ptr++ = *psample32++;
- *ptr++ = *psample32++;
- *ptr++ = *psample32++;
+ byte* ptr = (byte*) bufferPtr.ToPointer(); // AudioRenderClient のバッファは GC 対象外なのでピン止め不要。
+
+ for( int i = 0; i < 読み込んだサイズsample; i++ )
+ {
+ float data = バッファ[ i ];
+ if( -1.0f > data ) data = -1.0f;
+ if( +1.0f < data ) data = +1.0f;
+
+ uint sample32 = (uint) ( data * 8388608f - 1f ); // 24bit PCM の値域は -8388608~+8388607
+ byte* psample32 = (byte*) &sample32;
+ *ptr++ = *psample32++;
+ *ptr++ = *psample32++;
+ *ptr++ = *psample32++;
+ }
}
}
//----------------
#endregion
- }
- else if( 16 == this.WaveFormat.BitsPerSample )
- {
- #region " (B) (Mixer)32bit-float → (AudioRenderClient)16bit-PCM の場合 "
+ break;
+
+ case 16:
+ #region " FLOAT32 → PCM16 "
//----------------
unsafe
{
}
//----------------
#endregion
- }
- else if( 8 == this.WaveFormat.BitsPerSample )
- {
- #region " (C) (Mixer)32bit-float → (AudioRenderClient)8bit-PCM の場合 "
+ break;
+
+ case 8:
+ #region " FLOAT32 → PCM8 "
//----------------
unsafe
{
}
//----------------
#endregion
- }
+ break;
}
- else if( encoding == AudioEncoding.IeeeFloat )
- {
- #region " (D) (Mixer)32bit-float → (AudioRenderClient)32bit-float の場合 "
- //----------------
- Marshal.Copy( バッファ, 0, bufferPtr, 読み込んだサイズsample );
- //----------------
- #endregion
- }
- }
- finally
- {
- int 出力したフレーム数 = 読み込んだサイズsample / this.WaveFormat.Channels;
- this._AudioRenderClient.ReleaseBuffer(
- 出力したフレーム数,
- ( 0 < 出力したフレーム数 ) ? AudioClientBufferFlags.None : AudioClientBufferFlags.Silent );
- }
-
- // レンダリング先からの出力がなくなったらおしまい。
- if( 0 == 読み込んだサイズsample )
- this._レンダリング状態 = PlaybackState.Stopped;
+ break;
}
}
+ finally
+ {
+ int 出力したフレーム数 = 読み込んだサイズsample / this.WaveFormat.Channels;
+
+ this._AudioRenderClient.ReleaseBuffer(
+ 出力したフレーム数,
+ ( 0 < 出力したフレーム数 ) ? AudioClientBufferFlags.None : AudioClientBufferFlags.Silent );
+ }
+
+ // レンダリング先からの出力がなくなったらおしまい。
+ if( 0 == 読み込んだサイズsample )
+ this._レンダリング状態 = PlaybackState.Stopped;
}
}
//----------------
#endregion
+
#region " 終了。"
//----------------
// このスレッドの MMCSS 特性を元に戻す。
- Device.AvRevertMmThreadCharacteristics( avrtHandle );
- avrtHandle = IntPtr.Zero;
+ Device.AvRevertMmThreadCharacteristics( 元のMMCSS特性 );
+ 元のMMCSS特性 = IntPtr.Zero;
// ハードウェアの再生が終わるくらいまで、少し待つ。
Thread.Sleep( (int) ( this.遅延sec * 1000 / 2 ) );
}
finally
{
- if( avrtHandle != IntPtr.Zero )
- Device.AvRevertMmThreadCharacteristics( avrtHandle );
+ #region " 完了。"
+ //----------------
+ if( 元のMMCSS特性 != IntPtr.Zero )
+ Device.AvRevertMmThreadCharacteristics( 元のMMCSS特性 );
- ( 起動完了通知 as EventWaitHandle )?.Set(); // 失敗時を想定して。
+ // 失敗時を想定して。
+ ( 起動完了通知 as EventWaitHandle )?.Set();
+ //----------------
+ #endregion
}
}
int hr = 0;
long pos = 0;
long qpcPos = 0;
+
for( int リトライ回数 = 0; リトライ回数 < 10; リトライ回数++ ) // 最大10回までリトライ。
{
hr = this._AudioClock.GetPositionNative( out pos, out qpcPos );
/// </summary>
public float Volume
{
- get { return this._Volume; }
+ get
+ => this._Volume;
+
set
{
- if( ( 0.0f > value ) || ( 1.0f < value ) )
+ if( ( 0.0f > value ) && ( 1.0f < value ) )
throw new ArgumentOutOfRangeException();
this._Volume = value;
/// <summary>
/// ミキサーのフォーマット。
/// </summary>
- public WaveFormat WaveFormat => this._WaveFormat;
+ public WaveFormat WaveFormat
+ {
+ get
+ => this._WaveFormat;
+ }
/// <summary>
/// ミキサーはループするので、Position には 非対応。
/// </summary>
public long Position
{
- get { return 0; }
- set { throw new NotSupportedException(); }
+ get
+ => 0;
+
+ set
+ => throw new NotSupportedException();
}
/// <summary>
/// ミキサーはシークできない。
/// </summary>
- public bool CanSeek => ( false );
+ public bool CanSeek
+ => false;
/// <summary>
/// ミキサーはループするので、長さの概念はない。
/// </summary>
- public long Length => ( 0 );
+ public long Length
+ => 0;
/// <summary>
+ /// コンストラクタ。
/// 指定したフォーマットを持つミキサーを生成する。
/// </summary>
public Mixer( WaveFormat deviceWaveFormat )
{
// すべての Sound を解放する。
foreach( var sampleSource in this._Sounds )
+ {
sampleSource.Dispose();
+ }
// Sound リストをクリアする。
this._Sounds.Clear();
lock( this._スレッド間同期 )
{
- if( !this.Contains( sound ) )
+ if( false == this.Contains( sound ) )
+ {
this._Sounds.Add( sound );
+ }
}
}
lock( this._スレッド間同期 )
{
if( this.Contains( sound ) )
+ {
this._Sounds.Remove( sound );
+ }
}
}
}
private float _Volume = 1.0f;
-
private WaveFormat _WaveFormat = null;
-
private readonly List<Sound> _Sounds = new List<Sound>();
-
private float[] _中間バッファ = null;
-
private readonly object _スレッド間同期 = new object();
}
}
{
public class Sound : IDisposable
{
- public long 長さsample => this._SampleSource.Length;
-
- public double 長さsec => this._SampleToSec( this.長さsample );
+ public long 長さsample
+ {
+ get
+ => this._SampleSource.Length;
+ }
+ public double 長さsec
+ {
+ get
+ => this._SampleToSec( this.長さsample );
+ }
public long 位置sample
{
- get { return this._SampleSource.Position; }
- set { this._SampleSource.Position = value; }
- }
+ get
+ => this._SampleSource.Position;
+ set
+ => this._SampleSource.Position = value;
+ }
public double 位置sec
{
get
- {
- return this._SampleToSec( this.位置sample );
- }
+ => this._SampleToSec( this.位置sample );
+
set
{
long position = this._SecToSample( value );
get;
protected set;
} = false;
-
public bool 再生停止中である
{
- get { return !this.再生中である; }
- protected set { this.再生中である = !value; }
- }
+ get
+ => !( this.再生中である );
- public ISampleSource SampleSource => this._SampleSource;
+ protected set
+ => this.再生中である = !( value );
+ }
- public IWaveSource WaveSource => this._WaveSource;
+ public ISampleSource SampleSource
+ {
+ get
+ => this._SampleSource;
+ }
+ public IWaveSource WaveSource
+ {
+ get
+ => this._WaveSource;
+ }
/// <summary>
/// 音量。0.0(無音)~1.0(原音)。
/// </summary>
public float Volume
{
- get { return this._Volume; }
+ get
+ => this._Volume;
+
set
{
if( ( 0.0f > value ) || ( 1.0f < value ) )
Utilities.解放する( ref this._SampleSource );
Utilities.解放する( ref this._WaveSource );
+
this._MixerRef = null;
}
/// 再生開始位置。秒単位。
/// </param>
public void Play( double 再生開始位置sec )
- {
- this.Play( this._SecToSample( 再生開始位置sec ) );
- }
+ => this.Play( this._SecToSample( 再生開始位置sec ) );
/// <summary>
/// 再生を停止する。
public void Stop()
{
if( this._MixerRef.TryGetTarget( out Mixer mixer ) )
+ {
mixer.RemoveSound( this );
+ }
this.再生停止中である = true;
}
private IWaveSource _WaveSource = null;
-
private ISampleSource _SampleSource = null;
-
private WeakReference<Mixer> _MixerRef = null;
-
private float _Volume = 1.0f;
private long _SecToSample( double 時間sec )
return 時間sample;
}
-
private double _SampleToSec( long 時間sample )
{
var wf = this.SampleSource.WaveFormat;
/// <summary>
/// 0:透明~1:不透明
/// </summary>
- public float 不透明度 { get; set; } = 1f;
-
- public bool 加算合成する { get; set; } = false;
-
- public Size2F サイズdpx => ( this._ShaderResourceViewSize );
+ public float 不透明度
+ {
+ get;
+ set;
+ } = 1f;
+ public bool 加算合成する
+ {
+ get;
+ set;
+ } = false;
+ public Size2F サイズdpx
+ {
+ get
+ => this._ShaderResourceViewSize;
+ }
public テクスチャ( string 画像ファイルパス, BindFlags bindFlags = BindFlags.ShaderResource )
{
this._画像ファイルパス = 画像ファイルパス;
this.ユーザ指定サイズdpx = Size2.Empty;
}
-
public テクスチャ( Size2 サイズdpx, BindFlags bindFlags = BindFlags.ShaderResource )
{
this._bindFlags = bindFlags;
this._画像ファイルパス = null;
this.ユーザ指定サイズdpx = サイズdpx;
}
-
protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
{
using( var d3dLock = new AutoD3DDeviceLock( dr.DXGIDeviceManager, out Device d3dDevice ) )
using( d3dDevice )
{
- #region " 定数バッファ "
- //----------------
- var cBufferDesc = new BufferDescription() {
- Usage = ResourceUsage.Dynamic, // 動的使用法
- BindFlags = BindFlags.ConstantBuffer, // 定数バッファ
- CpuAccessFlags = CpuAccessFlags.Write, // CPUから書き込む
- OptionFlags = ResourceOptionFlags.None,
- SizeInBytes = SharpDX.Utilities.SizeOf<ST定数バッファの転送元データ>(), // バッファサイズ
- StructureByteStride = 0,
- };
- this._ConstantBuffer = new Buffer( d3dDevice, cBufferDesc );
- //----------------
- #endregion
- #region " テクスチャとシェーダーリソースビュー "
- //----------------
- if( this._画像ファイルパス.Nullでも空でもない() )
- {
- // (A) 画像ファイルから生成する場合。
- this._ShaderResourceView = Utilities.CreateShaderResourceViewFromFile(
- d3dDevice,
- this._bindFlags,
- フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this._画像ファイルパス ),
- out this._ShaderResourceViewSize,
- out this.Texture );
- }
- else if( this.ユーザ指定サイズdpx != Size2.Empty )
+ _定数バッファを生成する();
+
+ _テクスチャとシェーダーリソースビューを生成する();
+
+ // 以下、ローカル関数。
+
+ void _定数バッファを生成する()
{
- // (B) サイズを指定して生成する場合。
- this._ShaderResourceView = Utilities.CreateShaderResourceView(
- d3dDevice,
- this._bindFlags,
- this.ユーザ指定サイズdpx,
- out this.Texture );
-
- this._ShaderResourceViewSize = new Size2F( this.ユーザ指定サイズdpx.Width, this.ユーザ指定サイズdpx.Height );
+ var cBufferDesc = new BufferDescription() {
+ Usage = ResourceUsage.Dynamic, // 動的使用法
+ BindFlags = BindFlags.ConstantBuffer, // 定数バッファ
+ CpuAccessFlags = CpuAccessFlags.Write, // CPUから書き込む
+ OptionFlags = ResourceOptionFlags.None,
+ SizeInBytes = SharpDX.Utilities.SizeOf<ST定数バッファの転送元データ>(), // バッファサイズ
+ StructureByteStride = 0,
+ };
+ this._ConstantBuffer = new Buffer( d3dDevice, cBufferDesc );
}
- else
+ void _テクスチャとシェーダーリソースビューを生成する()
{
- throw new InvalidOperationException();
+ if( this._画像ファイルパス.Nullでも空でもない() )
+ {
+ // (A) 画像ファイルから生成する場合。
+ var 戻り値 = Utilities.CreateShaderResourceViewFromFile(
+ d3dDevice,
+ this._bindFlags,
+ フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this._画像ファイルパス ) );
+ this._ShaderResourceView = 戻り値.srv;
+ this._ShaderResourceViewSize = 戻り値.viewSize;
+ this.Texture = 戻り値.texture;
+ }
+ else if( this.ユーザ指定サイズdpx != Size2.Empty )
+ {
+ // (B) サイズを指定して生成する場合。
+ var 戻り値 = Utilities.CreateShaderResourceView(
+ d3dDevice,
+ this._bindFlags,
+ this.ユーザ指定サイズdpx );
+ this._ShaderResourceView = 戻り値.srv;
+ this.Texture = 戻り値.texture;
+
+ this._ShaderResourceViewSize = new Size2F( this.ユーザ指定サイズdpx.Width, this.ユーザ指定サイズdpx.Height );
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
}
- //----------------
- #endregion
}
}
-
protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
{
Utilities.解放する( ref this._ShaderResourceView );
this._定数バッファの転送元データ.World = ワールド行列変換;
// ビュー変換行列
- this._定数バッファの転送元データ.View = dr.ビュー変換行列; // 転置済み
+ this._定数バッファの転送元データ.View = dr.ビュー変換行列; // 転置済み
// 射影変換行列
- this._定数バッファの転送元データ.Projection = dr.射影変換行列; // 転置済み
+ this._定数バッファの転送元データ.Projection = dr.射影変換行列; // 転置済み
// 描画元矩形
if( null == 転送元矩形 )
}
//----------------
#endregion
+
#region " 3Dパイプラインを設定する。"
//----------------
- // 入力アセンブラ
- using( var IA = d3dContext.InputAssembler )
- {
- IA.InputLayout = null;
- IA.PrimitiveTopology = PrimitiveTopology.TriangleStrip;
- }
- // 頂点シェーダ
- using( var VS = d3dContext.VertexShader )
- {
- VS.Set( テクスチャ._VertexShader );
- VS.SetConstantBuffers( 0, this._ConstantBuffer );
- }
- // ジオメトリシェーダ
- using( var GS = d3dContext.GeometryShader )
- {
- d3dContext.GeometryShader.Set( null );
- }
- // ラスタライザ
- using( var RS = d3dContext.Rasterizer )
- {
- RS.SetViewports( dr.D3DViewPort );
- RS.State = テクスチャ._RasterizerState;
- }
- // ピクセルシェーダ
- using( var PS = d3dContext.PixelShader )
- {
- PS.Set( テクスチャ._PixelShader );
- PS.SetConstantBuffers( 0, this._ConstantBuffer );
- PS.SetShaderResources( 0, 1, this._ShaderResourceView );
- PS.SetSamplers( 0, 1, テクスチャ._SamplerState );
- }
- // 出力マージャ
- using( var OM = d3dContext.OutputMerger )
{
- OM.SetTargets( dr.D3DDepthStencilView, dr.D3DRenderTargetView );
- OM.SetBlendState(
- ( this.加算合成する ) ? テクスチャ._BlendState加算合成 : テクスチャ._BlendState通常合成,
- new SharpDX.Mathematics.Interop.RawColor4( 0f, 0f, 0f, 0f ),
- -1 );
- OM.SetDepthStencilState( dr.D3DDepthStencilState, 0 );
+ // 入力アセンブラ
+ using( var IA = d3dContext.InputAssembler )
+ {
+ IA.InputLayout = null;
+ IA.PrimitiveTopology = PrimitiveTopology.TriangleStrip;
+ }
+ // 頂点シェーダ
+ using( var VS = d3dContext.VertexShader )
+ {
+ VS.Set( テクスチャ._VertexShader );
+ VS.SetConstantBuffers( 0, this._ConstantBuffer );
+ }
+ // ジオメトリシェーダ
+ using( var GS = d3dContext.GeometryShader )
+ {
+ d3dContext.GeometryShader.Set( null );
+ }
+ // ラスタライザ
+ using( var RS = d3dContext.Rasterizer )
+ {
+ RS.SetViewports( dr.D3DViewPort );
+ RS.State = テクスチャ._RasterizerState;
+ }
+ // ピクセルシェーダ
+ using( var PS = d3dContext.PixelShader )
+ {
+ PS.Set( テクスチャ._PixelShader );
+ PS.SetConstantBuffers( 0, this._ConstantBuffer );
+ PS.SetShaderResources( 0, 1, this._ShaderResourceView );
+ PS.SetSamplers( 0, 1, テクスチャ._SamplerState );
+ }
+ // 出力マージャ
+ using( var OM = d3dContext.OutputMerger )
+ {
+ OM.SetTargets( dr.D3DDepthStencilView, dr.D3DRenderTargetView );
+ OM.SetBlendState(
+ ( this.加算合成する ) ? テクスチャ._BlendState加算合成 : テクスチャ._BlendState通常合成,
+ new SharpDX.Mathematics.Interop.RawColor4( 0f, 0f, 0f, 0f ),
+ -1 );
+ OM.SetDepthStencilState( dr.D3DDepthStencilState, 0 );
+ }
}
//----------------
#endregion
}
protected Size2 ユーザ指定サイズdpx;
-
protected Texture2D Texture = null;
-
private string _画像ファイルパス = null;
-
private Buffer _ConstantBuffer = null;
-
private ShaderResourceView _ShaderResourceView = null;
-
private Size2F _ShaderResourceViewSize;
-
private BindFlags _bindFlags;
-
private struct ST定数バッファの転送元データ
{
public Matrix World; // ワールド変換行列
public float dummy2; // float4境界に合わせるためのダミー
public float dummy3; // float4境界に合わせるためのダミー
};
-
private ST定数バッファの転送元データ _定数バッファの転送元データ;
-
// (2) 全インスタンス共通項目(static)
public static void 全インスタンスで共有するリソースを作成する( デバイスリソース dr )
using( var d3dLock = new AutoD3DDeviceLock( dr.DXGIDeviceManager, out Device d3dDevice ) )
using( d3dDevice )
{
- #region " 頂点シェーダ "
- //----------------
var シェーダコンパイルのオプション =
ShaderFlags.Debug |
ShaderFlags.SkipOptimization |
ShaderFlags.EnableStrictness |
ShaderFlags.PackMatrixColumnMajor;
- // シェーダコードをコンパイルする。
- using( var code = ShaderBytecode.Compile(
- Properties.Resources.テクスチャ用シェーダコード,
- "VS", "vs_4_0", シェーダコンパイルのオプション ) )
+ #region " 頂点シェーダを生成する。"
+ //----------------
{
- // 頂点シェーダを生成する。
- テクスチャ._VertexShader = new VertexShader( d3dDevice, code );
+ // シェーダコードをコンパイルする。
+ using( var code = ShaderBytecode.Compile(
+ Properties.Resources.テクスチャ用シェーダコード,
+ "VS", "vs_4_0", シェーダコンパイルのオプション ) )
+ {
+ // 頂点シェーダを生成する。
+ テクスチャ._VertexShader = new VertexShader( d3dDevice, code );
+ }
}
//----------------
#endregion
- #region " ピクセルシェーダ "
+
+ #region " ピクセルシェーダを生成する。"
//----------------
- // シェーダコードをコンパイルする。
- using( var code = ShaderBytecode.Compile(
- Properties.Resources.テクスチャ用シェーダコード,
- "PS", "ps_4_0", シェーダコンパイルのオプション ) )
{
- // ピクセルシェーダを作成する。
- テクスチャ._PixelShader = new PixelShader( d3dDevice, code );
+ // シェーダコードをコンパイルする。
+ using( var code = ShaderBytecode.Compile(
+ Properties.Resources.テクスチャ用シェーダコード,
+ "PS", "ps_4_0", シェーダコンパイルのオプション ) )
+ {
+ // ピクセルシェーダを作成する。
+ テクスチャ._PixelShader = new PixelShader( d3dDevice, code );
+ }
}
//----------------
#endregion
- #region " ブレンドステート(通常)"
+
+ #region " ブレンドステート通常版を生成する。"
//----------------
- var BlendStateNorm = new BlendStateDescription() {
- AlphaToCoverageEnable = false, // アルファマスクで透過する(するならZバッファ必須)
- IndependentBlendEnable = false, // 個別設定。false なら BendStateDescription.RenderTarget[0] だけが有効で、[1~7] は無視される。
- };
- BlendStateNorm.RenderTarget[ 0 ].IsBlendEnabled = true; // true ならブレンディングが有効。
- BlendStateNorm.RenderTarget[ 0 ].RenderTargetWriteMask = ColorWriteMaskFlags.All; // RGBA の書き込みマスク。
-
- // アルファ値のブレンディング設定 ... 特になし
- BlendStateNorm.RenderTarget[ 0 ].SourceAlphaBlend = BlendOption.One;
- BlendStateNorm.RenderTarget[ 0 ].DestinationAlphaBlend = BlendOption.Zero;
- BlendStateNorm.RenderTarget[ 0 ].AlphaBlendOperation = BlendOperation.Add;
-
- // 色値のブレンディング設定 ... アルファ強度に応じた透明合成(テクスチャのアルファ値は、テクスチャのアルファ×ピクセルシェーダでの全体アルファとする(HLSL参照))
- BlendStateNorm.RenderTarget[ 0 ].SourceBlend = BlendOption.SourceAlpha;
- BlendStateNorm.RenderTarget[ 0 ].DestinationBlend = BlendOption.InverseSourceAlpha;
- BlendStateNorm.RenderTarget[ 0 ].BlendOperation = BlendOperation.Add;
-
- // ブレンドステートを作成する。
- テクスチャ._BlendState通常合成 = new BlendState( d3dDevice, BlendStateNorm );
+ {
+ var BlendStateNorm = new BlendStateDescription() {
+ AlphaToCoverageEnable = false, // アルファマスクで透過する(するならZバッファ必須)
+ IndependentBlendEnable = false, // 個別設定。false なら BendStateDescription.RenderTarget[0] だけが有効で、[1~7] は無視される。
+ };
+ BlendStateNorm.RenderTarget[ 0 ].IsBlendEnabled = true; // true ならブレンディングが有効。
+ BlendStateNorm.RenderTarget[ 0 ].RenderTargetWriteMask = ColorWriteMaskFlags.All; // RGBA の書き込みマスク。
+
+ // アルファ値のブレンディング設定 ... 特になし
+ BlendStateNorm.RenderTarget[ 0 ].SourceAlphaBlend = BlendOption.One;
+ BlendStateNorm.RenderTarget[ 0 ].DestinationAlphaBlend = BlendOption.Zero;
+ BlendStateNorm.RenderTarget[ 0 ].AlphaBlendOperation = BlendOperation.Add;
+
+ // 色値のブレンディング設定 ... アルファ強度に応じた透明合成(テクスチャのアルファ値は、テクスチャのアルファ×ピクセルシェーダでの全体アルファとする(HLSL参照))
+ BlendStateNorm.RenderTarget[ 0 ].SourceBlend = BlendOption.SourceAlpha;
+ BlendStateNorm.RenderTarget[ 0 ].DestinationBlend = BlendOption.InverseSourceAlpha;
+ BlendStateNorm.RenderTarget[ 0 ].BlendOperation = BlendOperation.Add;
+
+ // ブレンドステートを作成する。
+ テクスチャ._BlendState通常合成 = new BlendState( d3dDevice, BlendStateNorm );
+ }
//----------------
#endregion
- #region " ブレンドステート(加算合成)"
+
+ #region " ブレンドステート加算合成版を生成する。"
//----------------
- var BlendStateAdd = new BlendStateDescription() {
- AlphaToCoverageEnable = false, // アルファマスクで透過する(するならZバッファ必須)
- IndependentBlendEnable = false, // 個別設定。false なら BendStateDescription.RenderTarget[0] だけが有効で、[1~7] は無視される。
- };
- BlendStateAdd.RenderTarget[ 0 ].IsBlendEnabled = true; // true ならブレンディングが有効。
- BlendStateAdd.RenderTarget[ 0 ].RenderTargetWriteMask = ColorWriteMaskFlags.All; // RGBA の書き込みマスク。
-
- // アルファ値のブレンディング設定 ... 特になし
- BlendStateAdd.RenderTarget[ 0 ].SourceAlphaBlend = BlendOption.One;
- BlendStateAdd.RenderTarget[ 0 ].DestinationAlphaBlend = BlendOption.Zero;
- BlendStateAdd.RenderTarget[ 0 ].AlphaBlendOperation = BlendOperation.Add;
-
- // 色値のブレンディング設定 ... 加算合成
- BlendStateAdd.RenderTarget[ 0 ].SourceBlend = BlendOption.SourceAlpha;
- BlendStateAdd.RenderTarget[ 0 ].DestinationBlend = BlendOption.One;
- BlendStateAdd.RenderTarget[ 0 ].BlendOperation = BlendOperation.Add;
-
- // ブレンドステートを作成する。
- テクスチャ._BlendState加算合成 = new BlendState( d3dDevice, BlendStateNorm );
+ {
+ var BlendStateAdd = new BlendStateDescription() {
+ AlphaToCoverageEnable = false, // アルファマスクで透過する(するならZバッファ必須)
+ IndependentBlendEnable = false, // 個別設定。false なら BendStateDescription.RenderTarget[0] だけが有効で、[1~7] は無視される。
+ };
+ BlendStateAdd.RenderTarget[ 0 ].IsBlendEnabled = true; // true ならブレンディングが有効。
+ BlendStateAdd.RenderTarget[ 0 ].RenderTargetWriteMask = ColorWriteMaskFlags.All; // RGBA の書き込みマスク。
+
+ // アルファ値のブレンディング設定 ... 特になし
+ BlendStateAdd.RenderTarget[ 0 ].SourceAlphaBlend = BlendOption.One;
+ BlendStateAdd.RenderTarget[ 0 ].DestinationAlphaBlend = BlendOption.Zero;
+ BlendStateAdd.RenderTarget[ 0 ].AlphaBlendOperation = BlendOperation.Add;
+
+ // 色値のブレンディング設定 ... 加算合成
+ BlendStateAdd.RenderTarget[ 0 ].SourceBlend = BlendOption.SourceAlpha;
+ BlendStateAdd.RenderTarget[ 0 ].DestinationBlend = BlendOption.One;
+ BlendStateAdd.RenderTarget[ 0 ].BlendOperation = BlendOperation.Add;
+
+ // ブレンドステートを作成する。
+ テクスチャ._BlendState加算合成 = new BlendState( d3dDevice, BlendStateAdd );
+ }
//----------------
#endregion
- #region " ラスタライザステート "
+
+ #region " ラスタライザステートを生成する。"
//----------------
- var RSDesc = new RasterizerStateDescription() {
- FillMode = FillMode.Solid, // 普通に描画する
- CullMode = CullMode.None, // 両面を描画する
- IsFrontCounterClockwise = false, // 時計回りが表面
- DepthBias = 0,
- DepthBiasClamp = 0,
- SlopeScaledDepthBias = 0,
- IsDepthClipEnabled = true,
- IsScissorEnabled = false,
- IsMultisampleEnabled = false,
- IsAntialiasedLineEnabled = false,
- };
- テクスチャ._RasterizerState = new RasterizerState( d3dDevice, RSDesc );
+ {
+ var RSDesc = new RasterizerStateDescription() {
+ FillMode = FillMode.Solid, // 普通に描画する
+ CullMode = CullMode.None, // 両面を描画する
+ IsFrontCounterClockwise = false, // 時計回りが表面
+ DepthBias = 0,
+ DepthBiasClamp = 0,
+ SlopeScaledDepthBias = 0,
+ IsDepthClipEnabled = true,
+ IsScissorEnabled = false,
+ IsMultisampleEnabled = false,
+ IsAntialiasedLineEnabled = false,
+ };
+
+ テクスチャ._RasterizerState = new RasterizerState( d3dDevice, RSDesc );
+ }
//----------------
#endregion
- #region " サンプラーステート "
+
+ #region " サンプラーステートを生成する。"
//----------------
- var descSampler = new SamplerStateDescription() {
- Filter = Filter.Anisotropic,
- AddressU = TextureAddressMode.Wrap,
- AddressV = TextureAddressMode.Wrap,
- AddressW = TextureAddressMode.Wrap,
- MipLodBias = 0.0f,
- MaximumAnisotropy = 2,
- ComparisonFunction = Comparison.Never,
- BorderColor = new RawColor4( 0f, 0f, 0f, 0f ),
- MinimumLod = float.MinValue,
- MaximumLod = float.MaxValue,
- };
- テクスチャ._SamplerState = new SamplerState( d3dDevice, descSampler );
+ {
+ var descSampler = new SamplerStateDescription() {
+ Filter = Filter.Anisotropic,
+ AddressU = TextureAddressMode.Wrap,
+ AddressV = TextureAddressMode.Wrap,
+ AddressW = TextureAddressMode.Wrap,
+ MipLodBias = 0.0f,
+ MaximumAnisotropy = 2,
+ ComparisonFunction = Comparison.Never,
+ BorderColor = new RawColor4( 0f, 0f, 0f, 0f ),
+ MinimumLod = float.MinValue,
+ MaximumLod = float.MaxValue,
+ };
+
+ テクスチャ._SamplerState = new SamplerState( d3dDevice, descSampler );
+ }
//----------------
#endregion
}
}
-
public static void 全インスタンスで共有するリソースを解放する()
{
Utilities.解放する( ref テクスチャ._SamplerState );
}
private static VertexShader _VertexShader = null;
-
private static PixelShader _PixelShader = null;
-
private static BlendState _BlendState通常合成 = null;
-
private static BlendState _BlendState加算合成 = null;
-
private static RasterizerState _RasterizerState = null;
-
private static SamplerState _SamplerState = null;
}
}
using System.Diagnostics;
using System.Linq;
using SharpDX;
-using FDK;
namespace FDK.メディア
{
/// <summary>
- /// 任意個の文字を格納した一枚のテクスチャ画像と、それぞれの文字領域の矩形リストから、文字列を連続するテクスチャ画像で表示する。
+ /// 任意個の文字を格納した一枚のテクスチャ画像と、それぞれの文字領域の矩形リストから、
+ /// 文字列を連続するテクスチャ画像で表示する。
/// </summary>
public class テクスチャフォント : Activity
{
+ /// <summary>
+ /// コンストラクタ。
+ /// 指定された画像ファイルと矩形リストファイルを使って、テクスチャフォントを生成する。
+ /// </summary>
public テクスチャフォント( string 文字盤の画像ファイルパス, string 文字矩形リストファイルパス )
: this( 文字盤の画像ファイルパス, new 矩形リスト( 文字矩形リストファイルパス ) )
{
}
+ /// <summary>
+ /// コンストラクタ。
+ /// 指定された画像ファイルと矩形リストを使って、テクスチャフォントを生成する。
+ /// </summary>
+ /// <param name="文字盤の画像ファイルパス"></param>
+ /// <param name="文字矩形リスト"></param>
public テクスチャフォント( string 文字盤の画像ファイルパス, 矩形リスト 文字矩形リスト )
{
this.子リスト.Add( this._文字盤 = new テクスチャ( 文字盤の画像ファイルパス ) );
foreach( var 文字矩形 in 有効文字矩形s )
{
文字列全体のサイズdpx.Width += 文字矩形.Width;
+
+ // 文字列全体の高さは、最大の文字高に一致。
if( 文字列全体のサイズdpx.Height < 文字矩形.Height )
- 文字列全体のサイズdpx.Height = 文字矩形.Height; // 文字列全体の高さは、最大の文字高に一致。
+ {
+ 文字列全体のサイズdpx.Height = 文字矩形.Height;
+ }
}
// 描画する。
}
private テクスチャ _文字盤 = null;
-
private 矩形リスト _文字矩形リスト = null;
}
}
/// 設計時(コーディング時や画像作成時)における画面サイズ。
/// 実際の物理画面サイズ(ウィンドウサイズやモニタサイズ)には依存しない。
/// </summary>
- public Size2F 設計画面サイズdpx = new Size2F( 1920f, 1080f );
+ public Size2F 設計画面サイズdpx
+ {
+ get;
+ set;
+ } = new Size2F( 1920f, 1080f );
/// <summary>
/// 実際の画面(ウィンドウやモニタ)のサイズ。
/// 設計画面サイズには依存しない。
/// </summary>
- public Size2F 物理画面サイズpx = new Size2F( 0f, 0f ); // (0, 0) は、サイズ依存リソース無効の印。
-
- public IntPtr ウィンドウハンドル = IntPtr.Zero;
-
- public float 視野角deg { get; set; } = 45f;
+ public Size2F 物理画面サイズpx
+ {
+ get;
+ set;
+ } = new Size2F( 0f, 0f ); // (0, 0) は、サイズ依存リソース無効の印。
+ public IntPtr ウィンドウハンドル
+ {
+ get;
+ set;
+ } = IntPtr.Zero;
+ public float 視野角deg
+ {
+ get;
+ set;
+ } = 45f;
public Matrix ビュー変換行列
{
get
var カメラの位置 = new Vector3( 0f, 0f, ( -2f * this._dz( this.設計画面サイズdpx.Height, this.視野角deg ) ) );
var カメラの注視点 = new Vector3( 0f, 0f, 0f );
var カメラの上方向 = new Vector3( 0f, 1f, 0f );
+
var mat = Matrix.LookAtLH( カメラの位置, カメラの注視点, カメラの上方向 );
+
mat.Transpose(); // 転置
+
return mat;
}
}
-
public Matrix 射影変換行列
{
get
{
float dz = this._dz( this.設計画面サイズdpx.Height, this.視野角deg );
+
var mat = Matrix.PerspectiveFovLH(
MathUtil.DegreesToRadians( 視野角deg ),
設計画面サイズdpx.Width / 設計画面サイズdpx.Height, // アスペクト比
-dz, // 前方投影面までの距離
dz ); // 後方投影面までの距離
+
mat.Transpose(); // 転置
+
return mat;
}
}
-
- public DXGIDeviceManager DXGIDeviceManager => ( this._DXGIDeviceManager );
-
- public SwapChain1 SwapChain1 => ( this._SwapChain1 );
-
- public RenderTargetView D3DRenderTargetView => ( this._D3DRenderTargetView );
-
- public RawViewportF[] D3DViewPort => ( this._D3DViewPort );
-
- public Texture2D D3DDepthStencil => ( this._D3DDepthStencil );
-
- public DepthStencilView D3DDepthStencilView => ( this._D3DDepthStencilView );
-
- public DepthStencilState D3DDepthStencilState => ( this._D3DDepthStencilState );
-
- public DeviceDebug D3DDeviceDebug => ( this._D3DDeviceDebug );
-
- public D2DFactory2 D2DFactory2 => ( this._D2DFactory2 );
-
- public DWFactory DWriteFactory => ( this._DWriteFactory );
-
- public ImagingFactory2 WicImagingFactory2 => ( this._WicImagingFactory2 );
-
- public D2DDevice1 D2DDevice1 => ( this._D2DDevice1 );
-
- public D2DDeviceContext1 D2DContext1 => ( this._D2DContext1 );
-
- public Bitmap1 D2DRenderTargetBitmap => ( this._D2DRenderTargetBitmap );
+ public DXGIDeviceManager DXGIDeviceManager
+ {
+ get
+ => this._DXGIDeviceManager;
+ }
+ public SwapChain1 SwapChain1
+ {
+ get
+ => this._SwapChain1;
+ }
+ public RenderTargetView D3DRenderTargetView
+ {
+ get
+ => this._D3DRenderTargetView;
+ }
+ public RawViewportF[] D3DViewPort
+ {
+ get
+ => this._D3DViewPort;
+ }
+ public Texture2D D3DDepthStencil
+ {
+ get
+ => this._D3DDepthStencil;
+ }
+ public DepthStencilView D3DDepthStencilView
+ {
+ get
+ => this._D3DDepthStencilView;
+ }
+ public DepthStencilState D3DDepthStencilState
+ {
+ get
+ => this._D3DDepthStencilState;
+ }
+ public DeviceDebug D3DDeviceDebug
+ {
+ get
+ => this._D3DDeviceDebug;
+ }
+ public D2DFactory2 D2DFactory2
+ {
+ get
+ => this._D2DFactory2;
+ }
+ public DWFactory DWriteFactory
+ {
+ get
+ => this._DWriteFactory;
+ }
+ public ImagingFactory2 WicImagingFactory2
+ {
+ get
+ => this._WicImagingFactory2;
+ }
+ public D2DDevice1 D2DDevice1
+ {
+ get
+ => this._D2DDevice1;
+ }
+ public D2DDeviceContext1 D2DContext1
+ {
+ get
+ => this._D2DContext1;
+ }
+ public Bitmap1 D2DRenderTargetBitmap
+ {
+ get
+ => this._D2DRenderTargetBitmap;
+ }
public float 拡大率DPXtoPX横方向
{
- get { return ( this.物理画面サイズpx.Width / this.設計画面サイズdpx.Width ); }
+ get
+ => this.物理画面サイズpx.Width / this.設計画面サイズdpx.Width;
}
-
public float 拡大率DPXtoPX縦方向
{
- get { return ( this.物理画面サイズpx.Height / this.設計画面サイズdpx.Height ); }
+ get
+ => this.物理画面サイズpx.Height / this.設計画面サイズdpx.Height;
}
-
- public float 拡大率PXtoDPX横方向
+ public Matrix3x2 拡大行列DPXtoPX
{
- get { return ( 1f / this.拡大率DPXtoPX横方向 ); }
+ get
+ => Matrix3x2.Scaling( this.拡大率DPXtoPX横方向, this.拡大率DPXtoPX縦方向 );
}
- public float 拡大率PXtoDPX縦方向
+ public float 拡大率PXtoDPX横方向
{
- get { return ( 1f / this.拡大率DPXtoPX縦方向 ); }
+ get
+ => 1f / this.拡大率DPXtoPX横方向;
}
-
- public Matrix3x2 拡大行列DPXtoPX
+ public float 拡大率PXtoDPX縦方向
{
- get { return Matrix3x2.Scaling( this.拡大率DPXtoPX横方向, this.拡大率DPXtoPX縦方向 ); }
+ get
+ => 1f / this.拡大率DPXtoPX縦方向;
}
-
public Matrix3x2 拡大行列PXtoDPX
{
- get { return Matrix3x2.Scaling( this.拡大率PXtoDPX横方向, this.拡大率PXtoDPX縦方向 ); }
+ get
+ => Matrix3x2.Scaling( this.拡大率PXtoDPX横方向, this.拡大率PXtoDPX縦方向 );
}
public デバイスリソース( Size2F 設計画面サイズdpx )
{
this.設計画面サイズdpx = 設計画面サイズdpx;
}
-
+ public void Dispose()
+ {
+ this.すべてのリソースを解放する();
+ }
public void すべてのリソースを作成する( System.Drawing.Size バックバッファサイズpx, IntPtr ウィンドウハンドル )
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
- try
- {
- this.物理画面サイズpx = new Size2F( バックバッファサイズpx.Width, バックバッファサイズpx.Height );
- this.ウィンドウハンドル = ウィンドウハンドル;
- this._すべてのリソースを作成する();
- }
- finally
- {
- Log.EndInfo( $"{Utilities.現在のメソッド名}" );
- }
- }
+ this.物理画面サイズpx = new Size2F( バックバッファサイズpx.Width, バックバッファサイズpx.Height );
+ this.ウィンドウハンドル = ウィンドウハンドル;
+ this._すべてのリソースを作成する();
+
+ Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ }
public void すべてのリソースを解放する()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
- try
- {
- // D3D 関連
- this._SwapChain1?.SetFullscreenState( fullscreen: false, targetRef: null ); // スワップチェインをウインドウモードにする。
- this.サイズに依存するリソースを解放する();
- Utilities.解放する( ref this._SwapChain1 );
- Utilities.解放する( ref this._DXGIDeviceManager );
+ // D3D 関連
+ this._SwapChain1?.SetFullscreenState( fullscreen: false, targetRef: null ); // スワップチェインをウインドウモードにする。
+ this.サイズに依存するリソースを解放する();
- // その他
- Utilities.解放する( ref this._WicImagingFactory2 );
- Utilities.解放する( ref this._DWriteFactory );
- Utilities.解放する( ref this._D2DFactory2 );
+ Utilities.解放する( ref this._SwapChain1 );
+ Utilities.解放する( ref this._DXGIDeviceManager );
- this._D3DDeviceDebug?.ReportLiveDeviceObjects( ReportingLevel.Detail );
- Utilities.解放する( ref this._D3DDeviceDebug );
- }
- finally
- {
- Log.EndInfo( $"{Utilities.現在のメソッド名}" );
- }
- }
+ // その他
+ Utilities.解放する( ref this._WicImagingFactory2 );
+ Utilities.解放する( ref this._DWriteFactory );
+ Utilities.解放する( ref this._D2DFactory2 );
- public void Dispose()
- {
- this.すべてのリソースを解放する();
- }
+ this._D3DDeviceDebug?.ReportLiveDeviceObjects( ReportingLevel.Detail );
+ Utilities.解放する( ref this._D3DDeviceDebug );
+ Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ }
public void サイズに依存するリソースを作成する()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
- try
+
+ #region " スワップチェーンのサイズを変更する。"
+ //----------------
{
- #region " スワップチェーンのサイズを変更する。"
- //----------------
Debug.Assert( null != this.SwapChain1 ); // スワップチェーンは(デバイスとともに)すでに生成されていること。
// ResizeTarget は、全画面モードにしたとき、モニタ画面の解像度も変更する。
// ただし、swapChainFlags に AllowModeSwitch を指定すると変更される(ResizeTargetと同じ挙動になる)。
this.SwapChain1.ResizeBuffers(
bufferCount: 2,
- width: (int) this.物理画面サイズpx.Width,
- height: (int) this.物理画面サイズpx.Height,
+ width: ( int ) this.物理画面サイズpx.Width,
+ height: ( int ) this.物理画面サイズpx.Height,
newFormat: Format.B8G8R8A8_UNorm,
swapChainFlags: SwapChainFlags.None );
- //swapChainFlags: SwapChainFlags.AllowModeSwitch );
- //----------------
- #endregion
+ //swapChainFlags: SwapChainFlags.AllowModeSwitch );
+ }
+ //----------------
+ #endregion
- // バックバッファを使って、D3D / D2D 関連のリソースを作成する。
- using( var backBuffer = Texture2D.FromSwapChain<Texture2D>( this._SwapChain1, 0 ) )
+ // バックバッファを使って、D3D / D2D 関連のリソースを作成する。
+ using( var backBuffer = Texture2D.FromSwapChain<Texture2D>( this._SwapChain1, 0 ) )
+ {
+ using( var d3dLock = new AutoD3DDeviceLock( this.DXGIDeviceManager, out SharpDX.Direct3D11.Device d3dDevice ) )
+ using( d3dDevice )
{
- using( var d3dLock = new AutoD3DDeviceLock( this.DXGIDeviceManager, out SharpDX.Direct3D11.Device d3dDevice ) )
- using( d3dDevice )
+ // D3D 関連
+
+ #region " レンダーターゲットビューを作成する。"
+ //----------------
+ this._D3DRenderTargetView = new RenderTargetView( d3dDevice, backBuffer );
+ //----------------
+ #endregion
+
+ #region " 深度ステンシルテクスチャを作成する。"
+ //----------------
+ var descDepth = backBuffer.Description;
+ //descDepth.Width = backBuffer.Description.Width; → backBuffer に同じ
+ //descDepth.Height = backBuffer.Description.Height; → 同上
+ descDepth.MipLevels = 1; // ミップマップレベル数
+ descDepth.ArraySize = 1; // 配列サイズ
+ descDepth.Format = Format.D32_Float; // フォーマット(深度のみ)
+ descDepth.Usage = ResourceUsage.Default; // デフォルト使用法
+ descDepth.BindFlags = BindFlags.DepthStencil; // 深度ステンシル
+ descDepth.CpuAccessFlags = CpuAccessFlags.None; // CPUからはアクセスしない
+ descDepth.OptionFlags = ResourceOptionFlags.None; // その他の設定なし
+ this._D3DDepthStencil = new Texture2D( d3dDevice, descDepth );
+ //----------------
+ #endregion
+
+ #region " 深度ステンシルビューを作成する。"
+ //----------------
+ var descDSV = new DepthStencilViewDescription() {
+ Format = descDepth.Format,
+ Dimension = DepthStencilViewDimension.Texture2D,
+ Flags = DepthStencilViewFlags.None,
+ Texture2D = new DepthStencilViewDescription.Texture2DResource() {
+ MipSlice = 0,
+ },
+ };
+ this._D3DDepthStencilView = new DepthStencilView( d3dDevice, this._D3DDepthStencil, descDSV );
+ //----------------
+ #endregion
+
+ #region " 深度ステンシルステートを作成する。"
+ //----------------
+ var DepthSencil = new DepthStencilStateDescription() {
+ IsDepthEnabled = true, // 深度テストあり
+ DepthWriteMask = DepthWriteMask.All, // 書き込む
+ DepthComparison = Comparison.Less, // 手前の物体を描画
+ IsStencilEnabled = false, // ステンシルテストなし。
+ StencilReadMask = 0, // ステンシル読み込みマスク。
+ StencilWriteMask = 0, // ステンシル書き込みマスク。
+
+ // 面が表を向いている場合のステンシル・テストの設定
+ FrontFace = new DepthStencilOperationDescription() {
+ FailOperation = StencilOperation.Keep, // 維持
+ DepthFailOperation = StencilOperation.Keep, // 維持
+ PassOperation = StencilOperation.Keep, // 維持
+ Comparison = Comparison.Never, // 常に失敗
+ },
+
+ // 面が裏を向いている場合のステンシル・テストの設定
+ BackFace = new DepthStencilOperationDescription() {
+ FailOperation = StencilOperation.Keep, // 維持
+ DepthFailOperation = StencilOperation.Keep, // 維持
+ PassOperation = StencilOperation.Keep, // 維持
+ Comparison = Comparison.Always, // 常に成功
+ },
+ };
+ this._D3DDepthStencilState = new DepthStencilState( d3dDevice, DepthSencil );
+ //----------------
+ #endregion
+
+ #region " ビューポートを作成する。"
+ //----------------
+ this._D3DViewPort[ 0 ] = new RawViewportF() {
+ X = 0.0f,
+ Y = 0.0f,
+ Width = ( float ) backBuffer.Description.Width, // 物理画面単位[px]
+ Height = ( float ) backBuffer.Description.Height, // 物理画面単位[px]
+ MinDepth = 0.0f,
+ MaxDepth = 1.0f,
+ };
+ //----------------
+ #endregion
+
+ テクスチャ.全インスタンスで共有するリソースを作成する( this );
+
+ // D2D 関連
+
+ using( var backsurface = Surface.FromSwapChain( this._SwapChain1, 0 ) )
{
- // D3D 関連
- #region " レンダーターゲットビューを作成する。"
- //----------------
- this._D3DRenderTargetView = new RenderTargetView( d3dDevice, backBuffer );
+ #region " D2DDevice を作成する。"
//----------------
- #endregion
- #region " 深度ステンシルテクスチャを作成する。"
- //----------------
- var descDepth = backBuffer.Description;
- //descDepth.Width = backBuffer.Description.Width; → backBuffer に同じ
- //descDepth.Height = backBuffer.Description.Height; → 同上
- descDepth.MipLevels = 1; // ミップマップレベル数
- descDepth.ArraySize = 1; // 配列サイズ
- descDepth.Format = Format.D32_Float; // フォーマット(深度のみ)
- descDepth.Usage = ResourceUsage.Default; // デフォルト使用法
- descDepth.BindFlags = BindFlags.DepthStencil; // 深度ステンシル
- descDepth.CpuAccessFlags = CpuAccessFlags.None; // CPUからはアクセスしない
- descDepth.OptionFlags = ResourceOptionFlags.None; // その他の設定なし
- this._D3DDepthStencil = new Texture2D( d3dDevice, descDepth );
+ using( var dxgiDevice2 = d3dDevice.QueryInterfaceOrNull<SharpDX.DXGI.Device2>() )
+ {
+ if( null == dxgiDevice2 )
+ throw new FDKException( "Direct3D11デバイスが、IDXGIDevice2 をサポートしていません。" );
+
+ this._D2DDevice1 = new D2DDevice1( this.D2DFactory2, dxgiDevice2 );
+ }
//----------------
#endregion
- #region " 深度ステンシルビューを作成する。"
+
+ #region " D2DDeviceを作成する。"
//----------------
- var descDSV = new DepthStencilViewDescription() {
- Format = descDepth.Format,
- Dimension = DepthStencilViewDimension.Texture2D,
- Flags = DepthStencilViewFlags.None,
- Texture2D = new DepthStencilViewDescription.Texture2DResource() {
- MipSlice = 0,
- },
- };
- this._D3DDepthStencilView = new DepthStencilView( d3dDevice, this._D3DDepthStencil, descDSV );
+ using( var dxgiDevice2 = d3dDevice.QueryInterfaceOrNull<SharpDX.DXGI.Device2>() )
+ {
+ if( null == dxgiDevice2 )
+ throw new FDKException( "Direct3D11デバイスが、IDXGIDevice2 をサポートしていません。" );
+
+ this._D2DDevice1 = new D2DDevice1( this.D2DFactory2, dxgiDevice2 );
+ }
//----------------
#endregion
- #region " 深度ステンシルステートを作成する。"
+
+ #region " D2Dの既定のデバイスコンテキストを作成する。"
//----------------
- var DepthSencil = new DepthStencilStateDescription() {
- IsDepthEnabled = true, // 深度テストあり
- DepthWriteMask = DepthWriteMask.All, // 書き込む
- DepthComparison = Comparison.Less, // 手前の物体を描画
- IsStencilEnabled = false, // ステンシルテストなし。
- StencilReadMask = 0, // ステンシル読み込みマスク。
- StencilWriteMask = 0, // ステンシル書き込みマスク。
-
- // 面が表を向いている場合のステンシル・テストの設定
- FrontFace = new DepthStencilOperationDescription() {
- FailOperation = StencilOperation.Keep, // 維持
- DepthFailOperation = StencilOperation.Keep, // 維持
- PassOperation = StencilOperation.Keep, // 維持
- Comparison = Comparison.Never, // 常に失敗
- },
-
- // 面が裏を向いている場合のステンシル・テストの設定
- BackFace = new DepthStencilOperationDescription() {
- FailOperation = StencilOperation.Keep, // 維持
- DepthFailOperation = StencilOperation.Keep, // 維持
- PassOperation = StencilOperation.Keep, // 維持
- Comparison = Comparison.Always, // 常に成功
- },
- };
- this._D3DDepthStencilState = new DepthStencilState( d3dDevice, DepthSencil );
+ this._D2DContext1 = new D2DDeviceContext1( this.D2DDevice1, DeviceContextOptions.EnableMultithreadedOptimizations );
+
+ // 現在のディスプレイDPI を取得し、D2DContext に設定する。
+ //this.D2DContext1.DotsPerInch = this.D2DFactory2.DesktopDpi;
+ this.D2DContext1.DotsPerInch = new Size2F( 96f, 96f );
//----------------
#endregion
- #region " ビューポートを作成する。"
+
+ #region " D2Dの既定のレンダーターゲットビットマップを作成する。"
//----------------
- this._D3DViewPort[ 0 ] = new RawViewportF() {
- X = 0.0f,
- Y = 0.0f,
- Width = (float) backBuffer.Description.Width, // 物理画面単位[px]
- Height = (float) backBuffer.Description.Height, // 物理画面単位[px]
- MinDepth = 0.0f,
- MaxDepth = 1.0f,
- };
+ var dpi = this.D2DContext1.DotsPerInch;
+
+ // DXGIスワップチェーンのバックバッファとデータを共有するD2Dターゲットビットマップを作成する。ビューではなく共有リソース。
+ this._D2DRenderTargetBitmap = new Bitmap1(
+ this.D2DContext1, // このコンテキストを通じて、
+ backsurface, // バックバッファとデータを共有する。
+ new BitmapProperties1(
+ new SharpDX.Direct2D1.PixelFormat( backsurface.Description.Format, SharpDX.Direct2D1.AlphaMode.Ignore ),
+ dpi.Width,
+ dpi.Height,
+ BitmapOptions.Target | BitmapOptions.CannotDraw ) );
+
+ // ここでもうD2Dのレンダーターゲットとして登録しておく。
+ this.D2DContext1.Target = this._D2DRenderTargetBitmap;
//----------------
#endregion
- #region " 全テクスチャで共有するリソースを作成する。"
+
+ #region " テキストのアンチエイリアシングを設定する。"
//----------------
- テクスチャ.全インスタンスで共有するリソースを作成する( this );
+ // Grayscale が、すべての Windows ストアアプリで推奨される。らしい。
+ this.D2DContext1.TextAntialiasMode = SharpDX.Direct2D1.TextAntialiasMode.Grayscale;
//----------------
#endregion
-
- // D2D 関連
- using( var backsurface = Surface.FromSwapChain( this._SwapChain1, 0 ) )
- {
- #region " D2DDevice を作成する。"
- //-----------------
- using( var dxgiDevice2 = d3dDevice.QueryInterfaceOrNull<SharpDX.DXGI.Device2>() )
- {
- if( null == dxgiDevice2 )
- throw new FDKException( "Direct3D11デバイスが、IDXGIDevice2 をサポートしていません。" );
-
- this._D2DDevice1 = new D2DDevice1( this.D2DFactory2, dxgiDevice2 );
- }
- //-----------------
- #endregion
- #region " D2Dの既定のデバイスコンテキストを作成する。"
- //-----------------
- this._D2DContext1 = new D2DDeviceContext1( this.D2DDevice1, DeviceContextOptions.EnableMultithreadedOptimizations );
-
- // 現在のディスプレイDPI を取得し、D2DContext に設定する。
- //this.D2DContext1.DotsPerInch = this.D2DFactory2.DesktopDpi;
- this.D2DContext1.DotsPerInch = new Size2F( 96f, 96f );
- //-----------------
- #endregion
- #region " D2Dの既定のレンダーターゲットビットマップを作成する。"
- //-----------------
- var dpi = this.D2DContext1.DotsPerInch;
-
- // DXGIスワップチェーンのバックバッファとデータを共有するD2Dターゲットビットマップを作成する。ビューではなく共有リソース。
- this._D2DRenderTargetBitmap = new Bitmap1(
- this.D2DContext1, // このコンテキストを通じて、
- backsurface, // バックバッファとデータを共有する。
- new BitmapProperties1(
- new SharpDX.Direct2D1.PixelFormat( backsurface.Description.Format, SharpDX.Direct2D1.AlphaMode.Ignore ),
- dpi.Width,
- dpi.Height,
- BitmapOptions.Target | BitmapOptions.CannotDraw ) );
-
- // ここでもうD2Dのレンダーターゲットとして登録しておく。
- this.D2DContext1.Target = this._D2DRenderTargetBitmap;
- //-----------------
- #endregion
- #region " テキストのアンチエイリアシングを設定する。"
- //-----------------
- // Grayscale が、すべての Windows ストアアプリで推奨される。らしい。
- this.D2DContext1.TextAntialiasMode = SharpDX.Direct2D1.TextAntialiasMode.Grayscale;
- //-----------------
- #endregion
- }
}
}
}
- finally
- {
- FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
- }
- }
+ Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
+ }
public void サイズに依存するリソースを解放する()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
- try
- {
- // D2D 関連
- if( null != this._D2DContext1 )
- this._D2DContext1.Target = null;
- Utilities.解放する( ref this._D2DRenderTargetBitmap );
- Utilities.解放する( ref this._D2DContext1 );
- Utilities.解放する( ref this._D2DDevice1 );
- // D3D 関連
- using( var d3dLock = new AutoD3DDeviceLock( this.DXGIDeviceManager, out SharpDX.Direct3D11.Device d3dDevice ) )
- using( d3dDevice )
- using( var d3dContext = d3dDevice.ImmediateContext )
- {
- d3dContext.ClearState();
- d3dContext.OutputMerger.ResetTargets();
+ // D2D 関連
- テクスチャ.全インスタンスで共有するリソースを解放する();
+ if( null != this._D2DContext1 )
+ this._D2DContext1.Target = null;
- Utilities.解放する( ref this._D3DDepthStencilState );
- Utilities.解放する( ref this._D3DDepthStencilView );
- Utilities.解放する( ref this._D3DDepthStencil );
- Utilities.解放する( ref this._D3DRenderTargetView );
- //Utilities.解放する( ref this.bs_SwapChain ); → スワップチェーンは解放しない(これの生成・解放はデバイスとセットで行う)。
+ Utilities.解放する( ref this._D2DRenderTargetBitmap );
+ Utilities.解放する( ref this._D2DContext1 );
+ Utilities.解放する( ref this._D2DDevice1 );
- // (0,0)は、サイズ依存リソース無効の印。
- this.物理画面サイズpx = new Size2F( 0f, 0f );
- }
- }
- finally
+ // D3D 関連
+
+ using( var d3dLock = new AutoD3DDeviceLock( this.DXGIDeviceManager, out SharpDX.Direct3D11.Device d3dDevice ) )
+ using( d3dDevice )
+ using( var d3dContext = d3dDevice.ImmediateContext )
{
- Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ d3dContext.ClearState();
+ d3dContext.OutputMerger.ResetTargets();
+
+ テクスチャ.全インスタンスで共有するリソースを解放する();
+
+ Utilities.解放する( ref this._D3DDepthStencilState );
+ Utilities.解放する( ref this._D3DDepthStencilView );
+ Utilities.解放する( ref this._D3DDepthStencil );
+ Utilities.解放する( ref this._D3DRenderTargetView );
+ //Utilities.解放する( ref this.bs_SwapChain ); → スワップチェーンは解放しない(これの生成・解放はデバイスとセットで行う)。
+
+ // (0,0)は、サイズ依存リソース無効の印。
+ this.物理画面サイズpx = new Size2F( 0f, 0f );
}
- }
+ Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ }
public void D3Dデバイスが消失していれば再構築する( out bool 異常状態なのでアプリを終了せよ )
{
異常状態なのでアプリを終了せよ = false;
}
if( 削除理由.Success )
- return; // デバイスは消失していない。
+ return; // デバイスは消失していない。
var エラー詳細 = new[] {
new { Code = SharpDX.DXGI.ResultCode.DeviceHung.Code, Info = SharpDX.DXGI.ResultCode.DeviceHung.ApiCode, Rebuild = true },
異常状態なのでアプリを終了せよ = true;
}
}
-
public void D2DContextの設定をリセットする( D2DDeviceContext1 context1 )
{
context1.Transform = Matrix3x2.Identity;
context1.PrimitiveBlend = PrimitiveBlend.SourceOver;
}
-
protected void _すべてのリソースを作成する()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
- try
- {
- // これらが呼び出し前に設定されていること。
- Debug.Assert( ( 0f < this.物理画面サイズpx.Width ) && ( 0f < this.物理画面サイズpx.Height ) );
- Debug.Assert( IntPtr.Zero != this.ウィンドウハンドル );
- #region " D2DFactory2 を作成する。"
- //-----------------
- {
- var デバッグレベル = DebugLevel.None;
+ // これらが呼び出し前に設定されていること。
+ Debug.Assert( ( 0f < this.物理画面サイズpx.Width ) && ( 0f < this.物理画面サイズpx.Height ) );
+ Debug.Assert( IntPtr.Zero != this.ウィンドウハンドル );
+
+ #region " D2DFactory2を作成する。"
+ //----------------
+ var デバッグレベル = DebugLevel.None;
#if DEBUG
- // プロジェクトがデバッグビルドに含まれている場合は、Direct2D デバッグレイヤーを SDK レイヤーを介して有効にする。
- デバッグレベル = DebugLevel.Information;
+ // プロジェクトがデバッグビルドに含まれている場合は、Direct2D デバッグレイヤーを SDK レイヤーを介して有効にする。
+ デバッグレベル = DebugLevel.Information;
#endif
- this._D2DFactory2 = new D2DFactory2( SharpDX.Direct2D1.FactoryType.MultiThreaded, デバッグレベル );
- }
- //-----------------
- #endregion
- #region " DWriteFactory を作成する。"
- //-----------------
- this._DWriteFactory = new DWFactory( SharpDX.DirectWrite.FactoryType.Shared );
- //-----------------
- #endregion
- #region " WicImagingFactory2 を作成する。"
- //-----------------
- this._WicImagingFactory2 = new ImagingFactory2();
- //-----------------
- #endregion
+ this._D2DFactory2 = new D2DFactory2( SharpDX.Direct2D1.FactoryType.MultiThreaded, デバッグレベル );
+ //----------------
+ #endregion
- var d3dDevice = (SharpDX.Direct3D11.Device) null;
+ this._DWriteFactory = new DWFactory( SharpDX.DirectWrite.FactoryType.Shared );
+ this._WicImagingFactory2 = new ImagingFactory2();
+ this._DXGIDeviceManager = new DXGIDeviceManager();
- #region " DXGIDeviceManager を作成する。"
- //-----------------
- this._DXGIDeviceManager = new DXGIDeviceManager();
- //-----------------
- #endregion
- #region " D3Dデバイスを作成する。"
- //----------------
+ var d3dDevice = (SharpDX.Direct3D11.Device) null;
+
+ #region " D3Dデバイスを作成する。"
+ //----------------
+ {
// 作成フラグ
var creationFlags = DeviceCreationFlags.BgraSupport; // D2Dをサポートするなら BgraSupport フラグが必須。
#if DEBUG
Log.Info( "D3Dデバイスを生成しました。" );
Log.Info( $"機能レベル: {d3dDevice.FeatureLevel.ToString()}" );
+ }
+ //----------------
+ #endregion
+
+ using( d3dDevice )
+ {
+ // D3D 関連
+
+ #region " スワップチェーンを作成する。"
+ //----------------
+ using( var dxgiDevice1 = d3dDevice.QueryInterface<SharpDX.DXGI.Device1>() )
+ using( var dxgiAdapter = dxgiDevice1.Adapter )
+ using( var dxgiFactory2 = dxgiAdapter.GetParent<SharpDX.DXGI.Factory2>() )
+ {
+ var swapChainDesc1 = new SwapChainDescription1() {
+ BufferCount = 2,
+ Width = (int) this.物理画面サイズpx.Width,
+ Height = (int) this.物理画面サイズpx.Height,
+ Format = Format.B8G8R8A8_UNorm, // D2D をサポートするなら B8G8R8A8 を使う必要がある。
+ Stereo = false,
+ SampleDescription = new SampleDescription( 1, 0 ), // マルチサンプリングは使わない。
+ SwapEffect = SwapEffect.FlipSequential, // ストアアプリの認定要件。
+ Scaling = Scaling.None, // SwapEffect に Flip なんちゃらを使う場合は None にする必要がある。
+ Usage = Usage.RenderTargetOutput,
+ Flags = SwapChainFlags.None,
+ //Flags = SwapChainFlags.AllowModeSwitch,
+ };
+ this._SwapChain1 = new SwapChain1(
+ dxgiFactory2,
+ d3dDevice,
+ this.ウィンドウハンドル,
+ ref swapChainDesc1 );
+
+ dxgiDevice1.MaximumFrameLatency = 1; // ストアアプリの認定要件
+ }
//----------------
#endregion
- using( d3dDevice )
+ // デバイスからデバッグオブジェクトを取得する。
+ this._D3DDeviceDebug = d3dDevice.QueryInterfaceOrNull<DeviceDebug>();
+
+ // D3DDevice が ID3D11VideoDevice を実装してないならエラー。(Win8以降のPCでは実装されているはず。)
+ using( var videoDevice = d3dDevice.QueryInterfaceOrNull<VideoDevice>() )
{
- // D3D 関連
+ if( null == videoDevice )
+ throw new FDKException( "Direct3D11デバイスが、ID3D11VideoDevice をサポートしていません。" );
+ }
- #region " スワップチェーンを作成する。"
- //----------------
- using( var dxgiDevice1 = d3dDevice.QueryInterface<SharpDX.DXGI.Device1>() )
- using( var dxgiAdapter = dxgiDevice1.Adapter )
- using( var dxgiFactory2 = dxgiAdapter.GetParent<SharpDX.DXGI.Factory2>() )
- {
- var swapChainDesc1 = new SwapChainDescription1() {
- BufferCount = 2,
- Width = (int) this.物理画面サイズpx.Width,
- Height = (int) this.物理画面サイズpx.Height,
- Format = Format.B8G8R8A8_UNorm, // D2D をサポートするなら B8G8R8A8 を使う必要がある。
- Stereo = false,
- SampleDescription = new SampleDescription( 1, 0 ), // マルチサンプリングは使わない。
- SwapEffect = SwapEffect.FlipSequential, // ストアアプリの認定要件。
- Scaling = Scaling.None, // SwapEffect に Flip なんちゃらを使う場合は None にする必要がある。
- Usage = Usage.RenderTargetOutput,
- Flags = SwapChainFlags.None,
- //Flags = SwapChainFlags.AllowModeSwitch,
- };
- this._SwapChain1 = new SwapChain1(
- dxgiFactory2,
- d3dDevice,
- this.ウィンドウハンドル,
- ref swapChainDesc1 );
-
- dxgiDevice1.MaximumFrameLatency = 1; // ストアアプリの認定要件
- }
- //----------------
- #endregion
- #region " デバイスからデバッグオブジェクトを取得する。"
- //----------------
- this._D3DDeviceDebug = d3dDevice.QueryInterfaceOrNull<DeviceDebug>();
- //----------------
- #endregion
- #region " D3DDevice が ID3D11VideoDevice を実装してないならエラー。(Win8以降のPCでは実装されているはず。) "
- //-----------------
- using( var videoDevice = d3dDevice.QueryInterfaceOrNull<VideoDevice>() )
- {
- if( null == videoDevice )
- throw new FDKException( "Direct3D11デバイスが、ID3D11VideoDevice をサポートしていません。" );
- }
- //-----------------
- #endregion
- #region " マルチスレッドモードを ON に設定する。DXVAを使う場合は必須。"
- //-----------------
- using( var multithread = d3dDevice.QueryInterfaceOrNull<SharpDX.Direct3D.DeviceMultithread>() )
- {
- if( null == multithread )
- throw new FDKException( "Direct3D11デバイスが、ID3D10Multithread をサポートしていません。" );
+ // マルチスレッドモードを ON に設定する。DXVAを使う場合は必須。
+ using( var multithread = d3dDevice.QueryInterfaceOrNull<SharpDX.Direct3D.DeviceMultithread>() )
+ {
+ if( null == multithread )
+ throw new FDKException( "Direct3D11デバイスが、ID3D10Multithread をサポートしていません。" );
- multithread.SetMultithreadProtected( true );
- }
- //-----------------
- #endregion
- #region " DXGIデバイスマネージャに D3Dデバイスを登録する。"
- //-----------------
- this.DXGIDeviceManager.ResetDevice( d3dDevice );
- //-----------------
- #endregion
- #region " すべての Windows イベントを無視する。具体的には PrintScreen と Alt+Enter 。"
- //----------------
- using( var factory = this._SwapChain1.GetParent<SharpDX.DXGI.Factory>() )
- {
- factory.MakeWindowAssociation( ウィンドウハンドル, WindowAssociationFlags.IgnoreAll );
- }
- //----------------
- #endregion
+ multithread.SetMultithreadProtected( true );
}
- this.サイズに依存するリソースを作成する();
- }
- finally
- {
- Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ // DXGIデバイスマネージャに D3Dデバイスを登録する。
+ this.DXGIDeviceManager.ResetDevice( d3dDevice );
+
+ // すべての Windows イベントを無視する。具体的には PrintScreen と Alt+Enter 。
+ using( var factory = this._SwapChain1.GetParent<SharpDX.DXGI.Factory>() )
+ {
+ factory.MakeWindowAssociation( ウィンドウハンドル, WindowAssociationFlags.IgnoreAll );
+ }
}
+
+ this.サイズに依存するリソースを作成する();
+
+ Log.EndInfo( $"{Utilities.現在のメソッド名}" );
}
private D2DFactory2 _D2DFactory2 = null;
-
private DWFactory _DWriteFactory = null;
-
private ImagingFactory2 _WicImagingFactory2 = null;
-
private DXGIDeviceManager _DXGIDeviceManager = null;
-
private SwapChain1 _SwapChain1 = null;
-
- private RawViewportF[] _D3DViewPort = new SharpDX.Mathematics.Interop.RawViewportF[ 1 ];
-
+ private RawViewportF[] _D3DViewPort = new RawViewportF[ 1 ];
private DepthStencilState _D3DDepthStencilState = null;
-
private RenderTargetView _D3DRenderTargetView = null;
-
private Texture2D _D3DDepthStencil = null;
-
private DepthStencilView _D3DDepthStencilView = null;
-
private DeviceDebug _D3DDeviceDebug = null;
-
private D2DDevice1 _D2DDevice1 = null;
-
private D2DDeviceContext1 _D2DContext1 = null;
-
private Bitmap1 _D2DRenderTargetBitmap = null;
private float _dz( float 高さdpx, float 視野角deg )
{
- return (float) ( 高さdpx / ( 4.0 * Math.Tan( MathUtil.DegreesToRadians( 視野角deg / 2.0f ) ) ) );
+ return ( float ) ( 高さdpx / ( 4.0 * Math.Tan( MathUtil.DegreesToRadians( 視野角deg / 2.0f ) ) ) );
}
}
}
: base( 画像ファイルパス, BindFlags.RenderTarget | BindFlags.ShaderResource )
{
}
-
public ビットマップ付きテクスチャ( Size2 サイズdpx )
: base( サイズdpx, BindFlags.RenderTarget | BindFlags.ShaderResource )
{
}
-
protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
{
// テクスチャを作成する。
this._BitmapTarget = new Bitmap1( dr.D2DContext1, dxgiSurface, bmpProp );
}
}
-
protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
{
// ビットマップターゲットを解放する。
// テクスチャを解放する。
base.Onデバイス依存リソースの解放( dr );
}
-
public void ビットマップへ描画する( デバイスリソース dr, Action<SharpDX.Direct2D1.DeviceContext1, Bitmap1> 描画アクション )
{
using( var 旧ターゲット = dr.D2DContext1.Target )
get;
protected set;
} = null;
-
public Size2F サイズdpx
{
get;
protected set;
}
-
public double 長さsec
{
get;
protected set;
} = 0.0;
-
public bool 加算合成
{
get;
public 動画( string 動画ファイルパス, int キューのサイズ = 16 )
{
- this.動画ファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス );
+ this.動画ファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス );
this._キューのサイズ = キューのサイズ;
}
-
public void 再生を開始する( double 開始位置sec = 0.0, bool ループ再生する = false )
{
this._ループ再生する = ループ再生する;
this._デコードタスク = Task.Factory.StartNew( this._デコードタスクエントリ, (object) 開始位置sec );
this._デコードタスク起動完了.WaitOne();
}
-
public void 進行描画する( デバイスリソース dr, RectangleF 描画先矩形dpx, float 不透明度0to1 = 1.0f )
{
// Direct2D の行列は、設計単位じゃなく物理単位で計算するので注意。
this.進行描画する( dr, 変換行列2Dpx, 不透明度0to1 );
}
-
public void 進行描画する( デバイスリソース dr, Matrix3x2 変換行列, float 不透明度0to1 = 1.0f )
{
#region " 条件チェック。"
//----------------
#endregion
- Action<FrameQueueItem> 次のフレームを表示する = ( frame ) => {
- this._次のフレームを取り出す( out frame );
- this._最後に表示したフレーム?.Dispose();
- this._最後に表示したフレーム = frame;
- this._D2DBitmapを描画する( dr, 変換行列, frame.D2DBitmap, 不透明度0to1 );
- };
- Action 前のフレームを表示する = () => {
- this._D2DBitmapを描画する( dr, 変換行列, this._最後に表示したフレーム?.D2DBitmap, 不透明度0to1 );
- };
-
this._次のフレームを確認する( out FrameQueueItem フレーム );
if( null != フレーム ) // 次のフレームがある。
( フレーム.表示時刻sec < this._最後に表示したフレーム.表示時刻sec ) )
{
this._再生タイマ.Value.リセットする( QPCTimer.秒をカウントに変換して返す( フレーム.表示時刻sec ) );
- 次のフレームを表示する( フレーム );
+ _次のフレームを表示する( フレーム );
}
// (B) 次のフレームの表示時刻に達した。
else if( フレーム.表示時刻sec <= this._再生タイマ.Value.現在のリアルタイムカウントsec )
{
- 次のフレームを表示する( フレーム );
+ _次のフレームを表示する( フレーム );
}
// (C) 次のフレームの表示時刻にはまだ達していない。
else
{
- 前のフレームを表示する();
+ _前のフレームを表示する();
}
+
+ #region " ローカル関数 "
+ //----------------
+ void _次のフレームを表示する( FrameQueueItem frame )
+ {
+ this._次のフレームを取り出す( out frame );
+ this._最後に表示したフレーム?.Dispose();
+ this._最後に表示したフレーム = frame;
+ this._D2DBitmapを描画する( dr, 変換行列, frame.D2DBitmap, 不透明度0to1 );
+ };
+ void _前のフレームを表示する()
+ {
+ this._D2DBitmapを描画する( dr, 変換行列, this._最後に表示したフレーム?.D2DBitmap, 不透明度0to1 );
+ };
+ //----------------
+ #endregion
}
// (D) デコードが追い付いてない、またはループせず再生が終わっている。
// 何も表示しない → 真っ黒画像。デコードが追い付いてないなら点滅するだろう。
}
}
-
protected override void On活性化( デバイスリソース dr )
{
this._デコードタスク = null; // タスクが起動していないときは null であることを保証する。
this._デコードタスクを終了せよ = new AutoResetEvent( false );
this._再生タイマ.Value = new QPCTimer();
}
-
protected override void On非活性化( デバイスリソース dr )
{
this._キューが空いた.Close();
this._デコードタスクを終了せよ.Close();
}
-
protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
- try
- {
- this._デコードタスク用D2DDeviceContext参照 = dr.D2DContext1;
- this._フレームキュー = new ConcurrentQueue<FrameQueueItem>();
- this._最後に表示したフレーム = null;
+ this._デコードタスク用D2DDeviceContext参照 = dr.D2DContext1;
+ this._フレームキュー = new ConcurrentQueue<FrameQueueItem>();
+ this._最後に表示したフレーム = null;
- // 動画ファイルから、SourceReaderEx, MediaType, WicBitmap を生成する。
+ // 動画ファイルから、SourceReaderEx, MediaType, WicBitmap を生成する。
- string 変数付きファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス ); // Log出力用。
+ string 変数付きファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス ); // Log出力用。
- #region " 動画ファイルパスの有効性を確認する。"
- //-----------------
- if( 動画ファイルパス.Nullまたは空である() )
- {
- Log.ERROR( $"動画ファイルパスが null または空文字列です。[{変数付きファイルパス}]" );
- return;
- }
- if( false == File.Exists( 動画ファイルパス ) )
- {
- Log.ERROR( $"動画ファイルが存在しません。[{変数付きファイルパス}]" );
- return;
- }
- //-----------------
- #endregion
- #region " SourceReaderEx を生成する。"
- //-----------------
- try
+ #region " 動画ファイルパスの有効性を確認する。"
+ //-----------------
+ if( 動画ファイルパス.Nullまたは空である() )
+ {
+ Log.ERROR( $"動画ファイルパスが null または空文字列です。[{変数付きファイルパス}]" );
+ return;
+ }
+ if( false == File.Exists( 動画ファイルパス ) )
+ {
+ Log.ERROR( $"動画ファイルが存在しません。[{変数付きファイルパス}]" );
+ return;
+ }
+ //-----------------
+ #endregion
+
+ #region " SourceReaderEx を生成する。"
+ //-----------------
+ try
+ {
+ using( var 属性 = new MediaAttributes() )
{
- using( var 属性 = new MediaAttributes() )
- {
- // DXVAに対応しているGPUの場合には、それをデコードに利用するよう指定する。
- 属性.Set( SourceReaderAttributeKeys.D3DManager, dr.DXGIDeviceManager );
+ // DXVAに対応しているGPUの場合には、それをデコードに利用するよう指定する。
+ 属性.Set( SourceReaderAttributeKeys.D3DManager, dr.DXGIDeviceManager );
- // 追加のビデオプロセッシングを有効にする。
- 属性.Set( SourceReaderAttributeKeys.EnableAdvancedVideoProcessing, true ); // 真偽値が bool だったり
+ // 追加のビデオプロセッシングを有効にする。
+ 属性.Set( SourceReaderAttributeKeys.EnableAdvancedVideoProcessing, true ); // 真偽値が bool だったり
- // 追加のビデオプロセッシングを有効にしたら、こちらは無効に。
- 属性.Set( SinkWriterAttributeKeys.ReadwriteDisableConverters, 0 ); // int だったり
+ // 追加のビデオプロセッシングを有効にしたら、こちらは無効に。
+ 属性.Set( SinkWriterAttributeKeys.ReadwriteDisableConverters, 0 ); // int だったり
- // 属性を使って、SourceReaderEx を生成。
- using( var sourceReader = new SourceReader( 動画ファイルパス, 属性 ) ) // パスは URI 扱い
- {
- this._SourceReaderEx = sourceReader.QueryInterface<SourceReaderEx>();
- }
- }
- }
- catch( SharpDXException e )
- {
- Log.ERROR( $"SourceReaderEx の作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
- throw;
- }
- //-----------------
- #endregion
- #region " 最初のビデオストリームを選択し、その他のすべてのストリームを非選択にする。"
- //-----------------
- try
- {
- this._SourceReaderEx.SetStreamSelection( SourceReaderIndex.AllStreams, false );
- this._SourceReaderEx.SetStreamSelection( SourceReaderIndex.FirstVideoStream, true );
- }
- catch( SharpDXException e )
- {
- Log.ERROR( $"ストリームの選択に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
- throw;
- }
- //-----------------
- #endregion
- #region " 部分 MediaType を作成し、SourceReaderEx に登録する。"
- //-----------------
- try
- {
- using( var mediaType = new MediaType() )
+ // 属性を使って、SourceReaderEx を生成。
+ using( var sourceReader = new SourceReader( 動画ファイルパス, 属性 ) ) // パスは URI 扱い
{
- // フォーマットは ARGB32 で固定とする。(SourceReaderEx を使わない場合、H264 では ARGB32 が選べないので注意。)
- mediaType.Set( MediaTypeAttributeKeys.MajorType, MediaTypeGuids.Video );
- mediaType.Set( MediaTypeAttributeKeys.Subtype, VideoFormatGuids.Argb32 );
-
- // 部分メディアタイプを SourceReaderEx にセットする。SourceReaderEx は、必要なデコーダをロードするだろう。
- this._SourceReaderEx.SetCurrentMediaType( SourceReaderIndex.FirstVideoStream, mediaType );
+ this._SourceReaderEx = sourceReader.QueryInterface<SourceReaderEx>();
}
}
- catch( SharpDXException e )
- {
- Log.ERROR( $"MediaType (Video, ARGB32) の設定または必要なデコーダの読み込みに失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
- throw;
- }
- //-----------------
- #endregion
- #region " ビデオストリームが選択されていることを再度保証する。"
- //-----------------
- try
- {
- this._SourceReaderEx.SetStreamSelection( SourceReaderIndex.FirstVideoStream, true );
- }
- catch( SharpDXException e )
- {
- Log.ERROR( $"最初のビデオストリームの選択に失敗しました(MediaType 設定後)。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
- throw;
- }
- //-----------------
- #endregion
- #region " 完全 MediaType と動画の情報を取得する。"
- //-----------------
- try
- {
- this._MediaType = this._SourceReaderEx.GetCurrentMediaType( SourceReaderIndex.FirstVideoStream );
- }
- catch( SharpDXException e )
- {
- Log.ERROR( $"完全メディアタイプの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
- throw;
- }
+ }
+ catch( SharpDXException e )
+ {
+ Log.ERROR( $"SourceReaderEx の作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+ throw;
+ }
+ //-----------------
+ #endregion
- // フレームサイズを取得する。
- try
- {
- // 動画の途中でのサイズ変更には対応しない。
- long packedFrameSize = this._MediaType.Get( MediaTypeAttributeKeys.FrameSize );
- this.サイズdpx = new Size2F( ( packedFrameSize >> 32 ) & 0xFFFFFFFF, ( packedFrameSize ) & 0xFFFFFFFF );
- }
- catch( SharpDXException e )
- {
- Log.ERROR( $"フレームサイズの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
- throw;
- }
+ #region " 最初のビデオストリームを選択し、その他のすべてのストリームを非選択にする。"
+ //-----------------
+ try
+ {
+ this._SourceReaderEx.SetStreamSelection( SourceReaderIndex.AllStreams, false );
+ this._SourceReaderEx.SetStreamSelection( SourceReaderIndex.FirstVideoStream, true );
+ }
+ catch( SharpDXException e )
+ {
+ Log.ERROR( $"ストリームの選択に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+ throw;
+ }
+ //-----------------
+ #endregion
- // 動画の長さを取得する。
- try
- {
- this.長さsec = this._SourceReaderEx.GetPresentationAttribute(
- SourceReaderIndex.MediaSource,
- PresentationDescriptionAttributeKeys.Duration
- ) / ( 1000.0 * 1000.0 * 10.0 );
- }
- catch( SharpDXException e )
- {
- Log.ERROR( $"動画の長さの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
- throw;
- }
- //-----------------
- #endregion
- #region " 描画先となる WicBitmap を作成する。"
- //-----------------
- try
- {
- this._WicBitmap = new SharpDX.WIC.Bitmap(
- dr.WicImagingFactory2,
- (int) this.サイズdpx.Width,
- (int) this.サイズdpx.Height,
- SharpDX.WIC.PixelFormat.Format32bppBGR,
- SharpDX.WIC.BitmapCreateCacheOption.CacheOnLoad );
- }
- catch( SharpDXException e )
+ #region " 部分 MediaType を作成し、SourceReaderEx に登録する。"
+ //-----------------
+ try
+ {
+ using( var mediaType = new MediaType() )
{
- Log.ERROR( $"描画先となる WICビットマップの作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
- throw;
+ // フォーマットは ARGB32 で固定とする。(SourceReaderEx を使わない場合、H264 では ARGB32 が選べないので注意。)
+ mediaType.Set( MediaTypeAttributeKeys.MajorType, MediaTypeGuids.Video );
+ mediaType.Set( MediaTypeAttributeKeys.Subtype, VideoFormatGuids.Argb32 );
+
+ // 部分メディアタイプを SourceReaderEx にセットする。SourceReaderEx は、必要なデコーダをロードするだろう。
+ this._SourceReaderEx.SetCurrentMediaType( SourceReaderIndex.FirstVideoStream, mediaType );
}
- //-----------------
- #endregion
+ }
+ catch( SharpDXException e )
+ {
+ Log.ERROR( $"MediaType (Video, ARGB32) の設定または必要なデコーダの読み込みに失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+ throw;
+ }
+ //-----------------
+ #endregion
- this._ストックする();
+ #region " ビデオストリームが選択されていることを再度保証する。"
+ //-----------------
+ try
+ {
+ this._SourceReaderEx.SetStreamSelection( SourceReaderIndex.FirstVideoStream, true );
}
- finally
+ catch( SharpDXException e )
{
- Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ Log.ERROR( $"最初のビデオストリームの選択に失敗しました(MediaType 設定後)。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+ throw;
}
- }
+ //-----------------
+ #endregion
+
+ #region " 完全 MediaType と動画の情報を取得する。"
+ //-----------------
+ try
+ {
+ this._MediaType = this._SourceReaderEx.GetCurrentMediaType( SourceReaderIndex.FirstVideoStream );
+ }
+ catch( SharpDXException e )
+ {
+ Log.ERROR( $"完全メディアタイプの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+ throw;
+ }
+
+ // フレームサイズを取得する。
+ try
+ {
+ // 動画の途中でのサイズ変更には対応しない。
+ long packedFrameSize = this._MediaType.Get( MediaTypeAttributeKeys.FrameSize );
+ this.サイズdpx = new Size2F( ( packedFrameSize >> 32 ) & 0xFFFFFFFF, ( packedFrameSize ) & 0xFFFFFFFF );
+ }
+ catch( SharpDXException e )
+ {
+ Log.ERROR( $"フレームサイズの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+ throw;
+ }
+
+ // 動画の長さを取得する。
+ try
+ {
+ this.長さsec = this._SourceReaderEx.GetPresentationAttribute(
+ SourceReaderIndex.MediaSource,
+ PresentationDescriptionAttributeKeys.Duration
+ ) / ( 1000.0 * 1000.0 * 10.0 );
+ }
+ catch( SharpDXException e )
+ {
+ Log.ERROR( $"動画の長さの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+ throw;
+ }
+ //-----------------
+ #endregion
+
+ #region " 描画先となる WicBitmap を作成する。"
+ //-----------------
+ try
+ {
+ this._WicBitmap = new SharpDX.WIC.Bitmap(
+ dr.WicImagingFactory2,
+ ( int ) this.サイズdpx.Width,
+ ( int ) this.サイズdpx.Height,
+ SharpDX.WIC.PixelFormat.Format32bppBGR,
+ SharpDX.WIC.BitmapCreateCacheOption.CacheOnLoad );
+ }
+ catch( SharpDXException e )
+ {
+ Log.ERROR( $"描画先となる WICビットマップの作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+ throw;
+ }
+ //-----------------
+ #endregion
+ this._ストックする();
+
+ Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ }
protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
{
this._デコードタスクを終了せよ.Set();
- if( !this._デコードタスク.Wait( 2000 ) )
+ if( false == this._デコードタスク.Wait( 2000 ) )
Log.WARNING( "デコードタスクの終了待ちがタイムアウトしました。" );
this._デコードタスク = null;
}
private int _キューのサイズ = 0;
-
private class FrameQueueItem : IDisposable
{
public double 表示時刻sec = 0;
- public SharpDX.Direct2D1.Bitmap D2DBitmap = null;
+ public Bitmap D2DBitmap = null;
public void Dispose()
{
Utilities.解放する( ref this.D2DBitmap );
}
}
-
private ConcurrentQueue<FrameQueueItem> _フレームキュー = null;
-
private FrameQueueItem _最後に表示したフレーム = null;
-
private bool _ループ再生する = false;
-
private Task _デコードタスク = null;
-
private AutoResetEvent _デコードタスク起動完了 = null;
-
private ManualResetEvent _キューが空いた = null;
-
private AutoResetEvent _デコードタスクを終了せよ = null;
-
private SourceReaderEx _SourceReaderEx = null;
-
private MediaType _MediaType = null;
-
private SharpDX.WIC.Bitmap _WicBitmap = null; // MediaFoundation は WICBitmap に出力する。
-
private DeviceContext1 _デコードタスク用D2DDeviceContext参照 = null; // D2Dはスレッドセーフであること。
private void _キューをクリアする()
this._キューが空いた?.Set();
}
-
private void _次のフレームを確認する( out FrameQueueItem フレーム )
{
if( ( 0 == this._フレームキュー.Count ) ||
フレーム = null; // キューが空だったか、Peek が一歩遅かった?(ないはずだが
}
}
-
private void _次のフレームを取り出す( out FrameQueueItem フレーム )
{
if( ( 0 == this._フレームキュー.Count ) ||
this._キューが空いた.Set();
}
}
-
private void _D2DBitmapを描画する( デバイスリソース dr, Matrix3x2 変換行列2Dpx, Bitmap D2DBitmap, float 不透明度 )
{
if( null == D2DBitmap )
} );
}
-
// 以下、デコードタスク用。
private RWLock<QPCTimer> _再生タイマ = new RWLock<QPCTimer>();
var 再生開始位置sec = (double) obj再生開始位置sec;
if( 0.0 < 再生開始位置sec )
- this._再生位置までストリームを進める( 再生開始位置sec );
+ {
+ this._再生位置までストリームを進める( 再生開始位置sec );
+ }
const int EVID_キューが空いた = 0;
const int EVID_デコードタスクを終了せよ = 1;
Log.Info( "デコードタスクを終了しました。" );
}
-
/// <returns>
/// 格納できたかスキップした場合は true、エラーあるいはストリーム終了なら false。
/// </returns>
//if( サンプルの表示時刻100ns < this.再生タイマ.Value.現在のリアルタイムカウント100ns単位 )
// return true; // もう表示時刻は通り過ぎてるのでスキップする。---> この実装だとループのし始めには常に true になってしまうので却下。
- this._サンプルをビットマップに転送する( sample, out bitmap );
+ bitmap = this._サンプルからビットマップを取得する( sample );
this._フレームキュー.Enqueue( new FrameQueueItem() {
D2DBitmap = bitmap,
- 表示時刻sec = サンプルの表示時刻100ns / ( 10.0 * 1000.0 * 1000.0 ),
+ 表示時刻sec = サンプルの表示時刻100ns / 10_000_000.0,
} );
bitmap = null; // キューに格納したので、ここでは Dispose しない。
return true;
}
-
private void _再生位置までストリームを進める( double 再生位置sec )
{
#region " ストリームがシーク不可なら何もしない。"
var flags = this._SourceReaderEx.GetPresentationAttribute(
SourceReaderIndex.MediaSource,
SourceReaderAttributeKeys.MediaSourceCharacteristics );
+
if( ( flags & (int) MediaSourceCharacteristics.CanSeek ) == 0 )
{
Log.WARNING( "この動画はシークできないようです。" );
this._キューをクリアする();
- long 再生位置100ns = (long) ( 再生位置sec * 1000.0 * 1000.0 * 10.0 );
+ long 再生位置100ns = (long) ( 再生位置sec * 10_000_000 );
this._SourceReaderEx.SetCurrentPosition( 再生位置100ns );
// キーフレームから再生位置100nsまで ReadSample する。
Log.Info( $"動画の再生位置を {再生位置sec}sec へ移動しました。" );
}
-
private void _ストックする()
{
for( int i = 0; i < this._キューのサイズ; i++ )
+ {
this._サンプルをひとつデコードしてフレームをキューへ格納する();
+ }
this._キューが空いた.Reset(); // 埋まった
}
-
- private unsafe void _サンプルをビットマップに転送する( Sample Sample, out Bitmap D2DBitmap )
+ private unsafe Bitmap _サンプルからビットマップを取得する( Sample Sample )
{
+ var d2dBitmap = (Bitmap) null;
var buffer = (MediaBuffer) null;
var buffer2d2 = (Buffer2D2) null;
try
}
//-----------------
#endregion
+
#region " サンプルバッファを Buffer2D2 にキャストする。"
//-----------------
try
}
//-----------------
#endregion
+
#region " サンプルバッファをロックする。"
//-----------------
byte[] scanLine0_bp = new byte[ 8 ]; // 「生ポインタ」が格納される。32bitなら[0~3]、64bitなら[0~7]が有効。(CPUではなく.NETに依存)
}
//-----------------
#endregion
+
try
{
#region " サンプルバッファのネイティブ先頭アドレスを取得する。"
}
//-----------------
#endregion
+
#region " サンプルから WicBitmap へ画像をコピーする。"
//-----------------
try
}
//----------------
#endregion
+
#region " WicBitmap から D2DBitmap を生成する。"
//----------------
try
{
- D2DBitmap = Bitmap.FromWicBitmap( this._デコードタスク用D2DDeviceContext参照, this._WicBitmap );
+ d2dBitmap = Bitmap.FromWicBitmap( this._デコードタスク用D2DDeviceContext参照, this._WicBitmap );
}
catch( SharpDXException e )
{
Utilities.解放する( ref buffer2d2 );
Utilities.解放する( ref buffer );
}
- }
+ return d2dBitmap;
+ }
private unsafe void* _生ポインタを格納したbyte配列からポインタを取得して返す( byte[] 生ポインタ )
{
if( ( 4 == IntPtr.Size ) && BitConverter.IsLittleEndian )
: base( 画像ファイルパス )
{
}
-
protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
{
this.画像を生成する(
/// <summary>
/// このメンバを set すれば、次回の進行描画時に画像が更新される。
/// </summary>
- public string 表示文字列 { get; set; } = null;
-
- public string フォント名 { get; set; } = "メイリオ";
-
- public float フォントサイズpt { get; set; } = 20.0f;
-
- public InterpolationMode 補正モード { get; set; } = InterpolationMode.Linear;
-
- public RectangleF? 転送元矩形dpx { get; set; } = null;
-
- public bool 加算合成 { get; set; } = false;
-
- public Size2F レイアウトサイズdpx { get; set; } = new Size2F( -1f, -1f );
+ public string 表示文字列
+ {
+ get;
+ set;
+ } = null;
- public bool 下詰め { get; set; } = false;
+ public string フォント名
+ {
+ get;
+ set;
+ } = "メイリオ";
+ public float フォントサイズpt
+ {
+ get;
+ set;
+ } = 20.0f;
+ public InterpolationMode 補正モード
+ {
+ get;
+ set;
+ } = InterpolationMode.Linear;
+ public RectangleF? 転送元矩形dpx
+ {
+ get;
+ set;
+ } = null;
+ public bool 加算合成
+ {
+ get;
+ set;
+ } = false;
+ public Size2F レイアウトサイズdpx
+ {
+ get;
+ set;
+ } = new Size2F( -1f, -1f );
+ public bool 下詰め
+ {
+ get;
+ set;
+ } = false;
public 文字列画像()
{
}
-
public 文字列画像( string 文字列, float フォントサイズpt = 20.0f, string フォント名 = "メイリオ" )
: this()
{
this.フォント名 = フォント名;
this.フォントサイズpt = フォントサイズpt;
}
-
protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
{
this._前回の表示文字列 = null;
if( this.表示文字列.Nullでも空でもない() )
{
- this.ビットマップを生成する( dr );
+ this.ã\83\93ã\83\83ã\83\88ã\83\9eã\83\83ã\83\97ã\83¬ã\83³ã\83\80ã\83¼ã\82¿ã\83¼ã\82²ã\83\83ã\83\88ã\82\92ç\94\9fæ\88\90ã\81\99ã\82\8b( dr );
this._前回の表示文字列 = this.表示文字列; // 最初の構築完了。
}
}
-
protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
{
Utilities.解放する( ref this._黒ブラシ );
if( this.表示文字列.Nullまたは空である() )
return;
- // 表示文字列が変更されているなら、ここで表示ビットマップの再構築を行う。
+ // 表示æ\96\87å\97å\88\97ã\81\8cå¤\89æ\9b´ã\81\95ã\82\8cã\81¦ã\81\84ã\82\8bã\81ªã\82\89ã\80\81ã\81\93ã\81\93ã\81§è¡¨ç¤ºã\83\93ã\83\83ã\83\88ã\83\9eã\83\83ã\83\97ã\83¬ã\83³ã\83\80ã\83¼ã\82¿ã\83¼ã\82²ã\83\83ã\83\88ã\81®å\86\8dæ§\8bç¯\89ã\82\92è¡\8cã\81\86ã\80\82
if( false == string.Equals( this.表示文字列, this._前回の表示文字列 ) )
- this.ビットマップを生成する( dr );
+ {
+ this.ビットマップレンダーターゲットを生成する( dr );
+ }
if( null == this.ビットマップレンダーターゲット )
return;
protected BitmapRenderTarget ビットマップレンダーターゲット = null;
- protected void ビットマップを生成する( デバイスリソース dr )
+ protected void ã\83\93ã\83\83ã\83\88ã\83\9eã\83\83ã\83\97ã\83¬ã\83³ã\83\80ã\83¼ã\82¿ã\83¼ã\82²ã\83\83ã\83\88ã\82\92ç\94\9fæ\88\90ã\81\99ã\82\8b( ã\83\87ã\83\90ã\82¤ã\82¹ã\83ªã\82½ã\83¼ã\82¹ dr )
{
this._前回の表示文字列 = this.表示文字列;
}
if( ( 0.0f >= this.レイアウトサイズdpx.Width ) || ( 0.0f >= this.レイアウトサイズdpx.Height ) )
+ {
this.レイアウトサイズdpx = new Size2F( dr.設計画面サイズdpx.Width, dr.設計画面サイズdpx.Height );
+ }
this._テキストレイアウト?.Dispose();
this._テキストレイアウト = new SharpDX.DirectWrite.TextLayout(
var 表示ビットマップのサイズdpx = new Size2F();
var 上マージン = 0.0f;
+
if( this.下詰め )
{
表示ビットマップのサイズdpx = new Size2F(
// ブラシの作成がまだなら行う。
if( null == this._白ブラシ )
+ {
this._白ブラシ = new SolidColorBrush( this.ビットマップレンダーターゲット, Color.LightGray );
+ }
if( null == this._黒ブラシ )
+ {
this._黒ブラシ = new SolidColorBrush( this.ビットマップレンダーターゲット, Color.Black );
+ }
// ビットマップレンダーターゲットにテキストを描画する。
Utilities.D2DBatchDraw( this.ビットマップレンダーターゲット, () => {
}
private string _前回の表示文字列 = null;
-
private SharpDX.DirectWrite.TextFormat _テキストフォーマット = null;
-
private SharpDX.DirectWrite.TextLayout _テキストレイアウト = null;
-
private SolidColorBrush _白ブラシ = null;
-
private SolidColorBrush _黒ブラシ = null;
}
}
/// </summary>
public class 画像 : Activity
{
- public bool 生成成功 => ( null != this.Bitmap );
+ /// <summary>
+ /// 画像の生成に成功していれば true 。
+ /// </summary>
+ public bool 生成成功
+ {
+ get
+ => ( null != this.Bitmap );
+ }
- public bool 生成失敗 => ( null == this.Bitmap );
+ /// <summary>
+ /// 画像の生成に失敗していれば true 。
+ /// </summary>
+ public bool 生成失敗
+ {
+ get
+ => !( this.生成成功 );
+ }
/// <summary>
/// 画像は、設計単位で作成される。
{
get
{
- return ( this.生成成功 ) ?
- new SharpDX.Size2F( (float) this.Bitmap.PixelSize.Width, (float) this.Bitmap.PixelSize.Height ) :
- SharpDX.Size2F.Zero;
+ if( this.生成成功 )
+ {
+ return new Size2F( this.Bitmap.PixelSize.Width, this.Bitmap.PixelSize.Height );
+ }
+ else
+ {
+ return Size2F.Zero;
+ }
}
}
- public InterpolationMode 補正モード { get; set; } = InterpolationMode.Linear;
-
- public bool 加算合成 { get; set; } = false;
+ public InterpolationMode 補正モード
+ {
+ get;
+ set;
+ } = InterpolationMode.Linear;
+ public bool 加算合成
+ {
+ get;
+ set;
+ } = false;
public 画像( string 画像ファイルパス )
{
this.画像ファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 画像ファイルパス );
}
-
protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
{
this.画像を生成する( dr );
}
-
protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
{
Utilities.解放する( ref this.Bitmap );
if( null == this.Bitmap )
return;
- if( null == 転送元矩形dpx || (false == 転送元矩形dpx.HasValue ) )
+ if( ( null == 転送元矩形dpx ) || ( false == 転送元矩形dpx.HasValue ) )
{
// Bitmap.PixelSize は設計単位(dpx)なので注意。
転送元矩形dpx = new RectangleF( 0f, 0f, this.Bitmap.PixelSize.Width, this.Bitmap.PixelSize.Height );
}
protected string 画像ファイルパス = null;
-
protected Bitmap1 Bitmap = null;
protected void 画像を生成する( デバイスリソース dr, BitmapProperties1 bitmapProperties1 = null )
var sourceFrame = (BitmapFrameDecode) null;
var converter = (FormatConverter) null;
- string 変数付きファイルパス = フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( this.画像ファイルパス ); // Log出力用
+ string 変数付きファイルパス = フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( this.画像ファイルパス ); // Log出力用
try
{
#region " 画像ファイルパスの有効性を確認する。"
//-----------------
- if( string.IsNullOrEmpty( this.画像ファイルパス ) )
+ if( this.画像ファイルパス.Nullまたは空である() )
{
Log.ERROR( $"画像ファイルパスが null または空文字列です。[{変数付きファイルパス}]" );
return;
}
//-----------------
#endregion
+
#region " 画像ファイルに対応できるデコーダを見つける。"
//-----------------
try
}
//-----------------
#endregion
+
#region " 最初のフレームをデコードし、取得する。"
//-----------------
try
}
//-----------------
#endregion
+
#region " 32bitPBGRA へのフォーマットコンバータを生成する。"
//-----------------
try
}
//-----------------
#endregion
+
#region " コンバータを使って、フレームを WICビットマップ経由で D2D ビットマップに変換する。"
//-----------------
try
: this( 文字盤の画像ファイルパス, new 矩形リスト( 文字矩形リストファイルパス ) )
{
}
-
public 画像フォント( string 文字盤の画像ファイルパス, 矩形リスト 文字矩形リスト )
{
this.子リスト.Add( this._文字盤 = new 画像( 文字盤の画像ファイルパス ) );
this._文字矩形リスト = 文字矩形リスト;
}
-
public void 描画する( デバイスリソース dr, float 左位置dpx, float 上位置dpx, string 表示文字列 )
{
if( 表示文字列.Nullまたは空である() )
foreach( var 文字矩形 in 有効文字矩形s )
{
文字列全体のサイズdpx.Width += 文字矩形.Width;
+
if( 文字列全体のサイズdpx.Height < 文字矩形.Height )
文字列全体のサイズdpx.Height = 文字矩形.Height; // 文字列全体の高さは、最大の文字高に一致。
}
for( int i = 0; i < 有効文字数; i++ )
{
var 文字矩形 = 有効文字矩形s.ElementAt( i );
+
this._文字盤.描画する( dr, 左位置dpx, 上位置dpx, 転送元矩形dpx: 文字矩形 );
+
左位置dpx += 文字矩形.Width;
}
}
private 画像 _文字盤 = null;
-
private 矩形リスト _文字矩形リスト = null;
}
}
/// </summary>
public class 矩形リスト
{
- public Dictionary<string, RectangleF> 文字列to矩形 { get; } = new Dictionary<string, RectangleF>();
+ public Dictionary<string, RectangleF> 文字列to矩形
+ {
+ get;
+ } = new Dictionary<string, RectangleF>();
/// <summary>
/// キー(文字列)に対応する矩形を返す。
public RectangleF? this[ string 文字列 ]
{
get
- {
- return this.文字列to矩形.ContainsKey( 文字列 ) ?
- this.文字列to矩形[ 文字列 ] :
- (RectangleF?) null;
- }
+ => this.文字列to矩形.ContainsKey( 文字列 ) ? this.文字列to矩形[ 文字列 ] : ( RectangleF? ) null;
}
public 矩形リスト()
{
}
-
public 矩形リスト( string ファイルパス )
: this()
{
this.矩形リストXmlファイルを読み込む( ファイルパス );
}
-
public void 矩形リストXmlファイルを読み込む( string ファイルパス )
{
this.文字列to矩形.Clear();
foreach( var SubImage要素 in Root要素.Elements( nameof( XML.SubImage ) ) )
{
// Name 属性を取得。なかったら例外発出。
- var Name属性 = SubImage要素.Attribute( nameof( XML.Name ) );
- if( null == Name属性 )
+ var Name属性 = SubImage要素.Attribute( nameof( XML.Name ) ) ??
throw new FDKException( $"{nameof( XML.Name )} 属性が存在しません。[{ファイルパス}]" );
// 同じ名前の Name 属性が指定されたらこの要素を無視。
continue;
}
- // Rectangle 属性を取得。なかったら例外発出。
+ // Rectangle 属性を取得。なかったらスキップ。
var Rectangle属性 = SubImage要素.Attribute( nameof( XML.Rectangle ) );
if( null == Rectangle属性 )
{
{
public class Keyboard : IInputDevice, IDisposable
{
- public InputDeviceType 入力デバイス種別 => ( InputDeviceType.Keyboard );
+ public InputDeviceType 入力デバイス種別
+ => InputDeviceType.Keyboard;
/// <summary>
/// ポーリング時に、前回のポーリング以降の状態と比べて生成された入力イベントのリスト。
/// </summary>
- public List<InputEvent> 入力イベントリスト { get; protected set; }
+ public List<InputEvent> 入力イベントリスト
+ {
+ get;
+ protected set;
+ }
public Keyboard( IntPtr hWindow )
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
- try
- {
- this._Window = hWindow;
- this.入力イベントリスト = new List<InputEvent>();
-
- #region " キーの押下状態配列を初期化する。"
- //-----------------
- for( int i = 0; i < 256; i++ )
- this._現在のキーの押下状態[ i ] = false;
- //-----------------
- #endregion
-
- // DirectInput を生成する。
- var di = new DirectInput();
-
- #region " キーボードが接続されていないなら、this.Device = null のままとする。"
- //-----------------
- if( 0 == di.GetDevices( DeviceType.Keyboard, DeviceEnumerationFlags.AttachedOnly ).Count )
- {
- this._Device = null; // これは、エラーではない。
- return;
- }
- //-----------------
- #endregion
- // デバイスを生成する。
- this._Device = new SharpDX.DirectInput.Keyboard( di );
+ this._Window = hWindow;
+ this.入力イベントリスト = new List<InputEvent>();
- // デバイスの協調モードを設定する。
- this._Device.SetCooperativeLevel(
- this._Window,
- CooperativeLevel.NoWinKey |
- CooperativeLevel.Foreground |
- CooperativeLevel.NonExclusive );
+ // キーの押下状態配列を初期化する。
+ for( int i = 0; i < 256; i++ )
+ this._現在のキーの押下状態[ i ] = false;
- // デバイスの入力バッファサイズを設定する。
- this._Device.Properties.BufferSize = Keyboard._デバイスの入力バッファサイズ;
- }
- finally
+ // DirectInput を生成する。
+ var di = new DirectInput();
+
+ // キーボードが接続されていないなら、this.Device = null のままとする。
+ if( 0 == di.GetDevices( DeviceType.Keyboard, DeviceEnumerationFlags.AttachedOnly ).Count )
{
- Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ this._Device = null; // これは、エラーではない。
+ return;
}
- }
+ // デバイスを生成する。
+ this._Device = new SharpDX.DirectInput.Keyboard( di );
+
+ // デバイスの協調モードを設定する。
+ this._Device.SetCooperativeLevel(
+ this._Window,
+ CooperativeLevel.NoWinKey |
+ CooperativeLevel.Foreground |
+ CooperativeLevel.NonExclusive );
+
+ // デバイスの入力バッファサイズを設定する。
+ this._Device.Properties.BufferSize = Keyboard._デバイスの入力バッファサイズ;
+
+ Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ }
public void Dispose()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
- try
- {
- Utilities.解放する( ref this._Device );
- }
- finally
- {
- Log.EndInfo( $"{Utilities.現在のメソッド名}" );
- }
- }
+ Utilities.解放する( ref this._Device );
+
+ Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ }
public void ポーリングする()
{
if( null == this._Device )
this.入力イベントリスト.Clear(); // Acquire 前にクリアしておく(Acquire の失敗時にリストが空であるように)。
- #region " Acquire する。失敗(非アクティブ、ウィンドウ終了時など)したら、何もしない。 "
- //-----------------
+ // Acquire する。失敗(非アクティブ、ウィンドウ終了時など)したら、何もしない。
try
{
this._Device.Acquire();
//Log.WARNING( "キーボードデバイスの Acquire に失敗しました。" );
return;
}
- //-----------------
- #endregion
try
{
{
return this.キーが押された( key, out _ );
}
-
public bool キーが押された( int key, out InputEvent ev )
{
ev = null;
// 非 null なら見つかった。
return ( null != ev ) ? true : false;
}
-
public bool キーが押された( Key key )
{
return this.キーが押された( (int) key, out _ );
}
-
public bool キーが押された( Key key, out InputEvent ev )
{
return this.キーが押された( (int) key, out ev );
return this._現在のキーの押下状態[ key ];
}
-
public bool キーが押されている( Key key )
{
return this.キーが押されている( (int) key );
{
return this.キーが離された( key, out _ );
}
-
public bool キーが離された( int key, out InputEvent ev )
{
ev = null;
// 非 null なら見つかった。
return ( null != ev ) ? true : false;
}
-
public bool キーが離された( Key key )
{
return this.キーが離された( (int) key, out _ );
}
-
public bool キーが離された( Key key, out InputEvent ev )
{
return this.キーが離された( (int) key, out ev );
return !( this._現在のキーの押下状態[ key ] );
}
-
public bool キーが離されている( Key key )
{
return this.キーが離されている( (int) key );
}
private const int _デバイスの入力バッファサイズ = 32;
-
private SharpDX.DirectInput.Keyboard _Device = null; // キーボードがアタッチされていない場合は null 。
-
private IntPtr _Window = IntPtr.Zero;
/// <summary>
public class MidiIn : IInputDevice, IDisposable
{
- public InputDeviceType 入力デバイス種別 => ( InputDeviceType.MidiIn );
+ public InputDeviceType 入力デバイス種別
+ => InputDeviceType.MidiIn;
/// <summary>
/// FootPedal の MIDIコード。
/// FootPedal 同時 HiHat キャンセル処理に使用される。
/// コードが判明次第、セットすること。
/// </remarks>
- public List<int> FootPedalNotes = new List<int>();
+ public List<int> FootPedalNotes
+ {
+ get;
+ } = new List<int>();
/// <summary>
/// HiHat (Open, Close, etc,.) のMIDIコード。
/// FootPedal 同時 HiHat キャンセル処理に使用される。
/// コードが判明次第、セットすること。
/// </remarks>
- public List<int> HiHatNotes = new List<int>();
+ public List<int> HiHatNotes
+ {
+ get;
+ } = new List<int>();
/// <summary>
/// 入力イベントのリスト。
/// ポーリング時に、前回のポーリング(またはコンストラクタ)以降に発生した入力イベントが格納される。
/// </summary>
- public List<InputEvent> 入力イベントリスト { get; protected set; }
+ public List<InputEvent> 入力イベントリスト
+ {
+ get;
+ protected set;
+ }
public MidiIn()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
- try
- {
- // 初期化する。
- this._MIDI入力デバイスハンドルリスト = new List<MIDIINHANDLE>( 5 ); // 5個もあれば十分?
- this._蓄積用入力イベントリスト = new List<InputEvent>( 32 ); // 適当
- this.入力イベントリスト = new List<InputEvent>();
- // コールバックをデリゲートとして生成し、そのデリゲートをGCの対象から外す。
- this._midiInProc = new MidiIn.MidiInProc( this.MIDI入力コールバック );
- this._midiInProcGCh = GCHandle.Alloc( this._midiInProc );
+ // 初期化する。
+ this._MIDI入力デバイスハンドルリスト = new List<MIDIINHANDLE>( 5 ); // 5個もあれば十分?
+ this._蓄積用入力イベントリスト = new List<InputEvent>( 32 ); // 適当
+ this.入力イベントリスト = new List<InputEvent>();
+
+ // コールバックをデリゲートとして生成し、そのデリゲートをGCの対象から外す。
+ this._midiInProc = new MidiIn.MidiInProc( this.MIDI入力コールバック );
+ this._midiInProcGCh = GCHandle.Alloc( this._midiInProc );
- // デバイス数を取得。
- uint MIDI入力デバイス数 = MidiIn.midiInGetNumDevs();
- Log.Info( $"MIDI入力デバイス数={MIDI入力デバイス数}" );
+ // デバイス数を取得。
+ uint MIDI入力デバイス数 = MidiIn.midiInGetNumDevs();
+ Log.Info( $"MIDI入力デバイス数={MIDI入力デバイス数}" );
- // すべてのMIDI入力デバイスについて...
- for( uint id = 0; id < MIDI入力デバイス数; id++ )
+ // すべてのMIDI入力デバイスについて...
+ for( uint id = 0; id < MIDI入力デバイス数; id++ )
+ {
+ // MIDI入力デバイスを開く。コールバックは全デバイスで共通。
+ MIDIINHANDLE hMidiIn = 0;
+ if( ( ( uint ) CSCore.MmResult.NoError == MidiIn.midiInOpen( ref hMidiIn, id, this._midiInProc, 0, MidiIn.CALLBACK_FUNCTION ) ) && ( 0 != hMidiIn ) )
{
- // MIDI入力デバイスを開く。コールバックは全デバイスで共通。
- MIDIINHANDLE hMidiIn = 0;
- if( ( (uint) CSCore.MmResult.NoError == MidiIn.midiInOpen( ref hMidiIn, id, this._midiInProc, 0, MidiIn.CALLBACK_FUNCTION ) ) && ( 0 != hMidiIn ) )
- {
- this._MIDI入力デバイスハンドルリスト.Add( hMidiIn );
- MidiIn.midiInStart( hMidiIn );
- }
+ this._MIDI入力デバイスハンドルリスト.Add( hMidiIn );
+ MidiIn.midiInStart( hMidiIn );
}
}
- finally
- {
- Log.EndInfo( $"{Utilities.現在のメソッド名}" );
- }
- }
+ Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ }
public void Dispose()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
- try
- {
- // すべてのMIDI入力デバイスの受信を停止し、デバイスを閉じる。
- foreach( var hMidiIn in this._MIDI入力デバイスハンドルリスト )
- {
- MidiIn.midiInStop( hMidiIn );
- MidiIn.midiInReset( hMidiIn );
- MidiIn.midiInClose( hMidiIn );
- }
- this._MIDI入力デバイスハンドルリスト.Clear();
- // コールバックデリゲートをGCの対象に戻し、デリゲートへの参照を破棄する。
- lock( this._コールバック同期 ) // コールバックが実行中でないことを保証する。(不十分だが)
- {
- this._midiInProcGCh.Free();
- this._midiInProc = null;
- }
+ // すべてのMIDI入力デバイスの受信を停止し、デバイスを閉じる。
+ foreach( var hMidiIn in this._MIDI入力デバイスハンドルリスト )
+ {
+ MidiIn.midiInStop( hMidiIn );
+ MidiIn.midiInReset( hMidiIn );
+ MidiIn.midiInClose( hMidiIn );
}
- finally
+ this._MIDI入力デバイスハンドルリスト.Clear();
+
+ // コールバックデリゲートをGCの対象に戻し、デリゲートへの参照を破棄する。
+ lock( this._コールバック同期 ) // コールバックが実行中でないことを保証する。(不十分だが)
{
- Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ this._midiInProcGCh.Free();
+ this._midiInProc = null;
}
- }
+ Log.EndInfo( $"{Utilities.現在のメソッド名}" );
+ }
public void ポーリングする()
{
lock( this._コールバック同期 )
// FootPedal同時HHのキャンセル処理。
if( ( 0 < this.FootPedalNotes.Count ) && ( 0 < this.HiHatNotes.Count ) )
{
- #region " FootPedalとほぼ同時にHiHatが鳴っていたら、そのHiHatに無効印(Key=-1)を付与する。"
+ #region " (1) FootPedalとほぼ同時にHiHatが鳴っていたら、そのHiHatに無効印(Key=-1)を付与する。"
//-----------------
for( int i = 0; i < this.入力イベントリスト.Count; i++ )
{
}
//-----------------
#endregion
- #region " 無効印のあるイベントをすべて取り除く。"
+
+ #region " (2) 無効印のあるイベントをすべて取り除く。"
//-----------------
this.入力イベントリスト.RemoveAll( ( ev ) => { return ( -1 == ev.Key ); } );
//-----------------
}
}
- public bool キーが押された( int key )
- {
- return this.キーが押された( key, out _ );
- }
-
public bool キーが押された( int key, out InputEvent ev )
{
ev = null;
return ( null != ev ) ? true : false;
}
-
- public bool キーが押されている( int key ) => ( false );
-
- public bool キーが離された( int key )
+ public bool キーが押された( int key )
{
- return this.キーが離された( key, out _ );
+ return this.キーが押された( key, out _ );
}
+ public bool キーが押されている( int key )
+ => false; // 常に false
+
public bool キーが離された( int key, out InputEvent ev )
{
// MIDI入力では扱わない。
ev = null;
return false;
}
+ public bool キーが離された( int key )
+ {
+ return this.キーが離された( key, out _ );
+ }
- public bool キーが離されている( int key ) => ( false );
+ public bool キーが離されている( int key )
+ => false; // 常に false
protected virtual void MIDI入力コールバック( MIDIINHANDLE hMidiIn, uint wMsg, int dwInstance, int dwParam1, int dwParam2 )
{
}
private List<MIDIINHANDLE> _MIDI入力デバイスハンドルリスト = null;
-
private List<InputEvent> _蓄積用入力イベントリスト = null; // コールバック関数で蓄積され、ポーリング時にキャッシュへコピー&クリアされる。
-
private MidiInProc _midiInProc = null; // 全MIDI入力デバイスで共通のコールバックのデリゲートとGCHandleと本体メソッド。
-
private GCHandle _midiInProcGCh;
-
private readonly object _コールバック同期 = new object();
- #region " Win32 API "
+ #region " Win32 "
//-----------------
private const int CALLBACK_FUNCTION = 0x00030000;
private const uint MIM_DATA = 0x000003C3;
}
private DXGIDeviceManager _DeviceManager = null;
-
private IntPtr _DeviceHandle = IntPtr.Zero;
/// <return>
d3dDevice = Device.FromPointer<Device>( dev );
}
-
private void _デバイスを解放しロックを解除する()
{
if( IntPtr.Zero == this._DeviceHandle )
: this( default( T ) )
{
}
-
public RWLock( T 初期値 )
{
this._スレッド間同期 = new ReaderWriterLockSlim( LockRecursionPolicy.SupportsRecursion ); // 再入可能(実は推奨されない)
this._Value = 初期値;
}
-
public RWLock( ReaderWriterLockSlim rwLocker, T 初期値 )
{
Debug.Assert( null != rwLocker );
this._スレッド間同期 = rwLocker;
this._Value = 初期値;
}
-
public RWLock( ReaderWriterLockSlim rwLocker )
: this( rwLocker, default( T ) )
{
}
private T _Value = default( T );
-
private readonly ReaderWriterLockSlim _スレッド間同期;
}
}
{
this._スレッド間同期 = new ReaderWriterLockSlim( LockRecursionPolicy.SupportsRecursion ); // 再入可能(実は推奨されない)
}
-
public RWLockAction( ReaderWriterLockSlim rwLocker )
{
Debug.Assert( null != rwLocker );
this._スレッド間同期 = rwLocker;
}
-
public void ReadLock( Action Lock中に行う処理 )
{
- this._スレッド間同期.EnterReadLock();
try
{
+ this._スレッド間同期.EnterReadLock();
Lock中に行う処理();
}
finally
this._スレッド間同期.ExitReadLock();
}
}
-
public void WriteLock( Action Lock中に行う処理 )
{
- this._スレッド間同期.EnterWriteLock();
try
{
+ this._スレッド間同期.EnterWriteLock();
Lock中に行う処理();
}
finally
this._スレッド間同期.ExitWriteLock();
}
}
-
public T ReadLock<T>( Func<T> Lock中に行う処理 )
{
- this._スレッド間同期.EnterReadLock();
try
{
+ this._スレッド間同期.EnterReadLock();
return Lock中に行う処理();
}
finally
this._スレッド間同期.ExitReadLock();
}
}
-
public T WriteLock<T>( Func<T> Lock中に行う処理 )
{
- this._スレッド間同期.EnterWriteLock();
try
{
+ this._スレッド間同期.EnterWriteLock();
return Lock中に行う処理();
}
finally
public class TriStateEvent
{
public enum 状態種別 { ON, OFF, 無効 }
-
public 状態種別 現在の状態
{
get
this.リセットする( 初期状態 );
}
- /// <summary>
- /// 状態が ON または 無効 になるまでブロックする。
- /// </summary>
/// <returns>
/// 解除後の状態(ON または 無効)。
/// </returns>
return ( h == 0 ) ? 状態種別.ON : 状態種別.無効;
}
- /// <summary>
- /// 状態が OFF または 無効 になるまでブロックする。
- /// </summary>
/// <returns>
/// 解除後の状態(OFF または 無効)。
/// </returns>
return ( h == 0 ) ? 状態種別.OFF : 状態種別.無効;
}
- /// <summary>
- /// 状態が 無効 になるまでブロックする。
- /// </summary>
public void 無効になるまでブロックする()
{
this._無効イベント.WaitOne();
}
private 状態種別 _状態 = 状態種別.OFF;
-
private ManualResetEvent _無効イベント = null;
-
private ManualResetEvent _ONイベント = null;
-
private ManualResetEvent _OFFイベント = null;
-
private readonly object _スレッド間同期 = new object();
}
}
// プロパティ(保存される)
public bool AutoFocus = true;
+
public bool ShowRecentUsedFiles = true;
+
public int MaxOfUsedRecentFiles = 10;
+
public List<string> RecentUsedFiles = new List<string>();
+
public string ViewerPath = "";
// メソッド(保存されない)
return config;
}
+
public void 保存する( string ファイル名 )
{
try
MessageBox.Show( $"ファイルの保存に失敗しました。[{ファイル名}]\n--------\n{e.ToString()}" );
}
}
+
public void ファイルを最近使ったファイルの一覧に追加する( string ファイル名 )
{
// 絶対パスを取得する。
}
protected string メッセージ;
+
protected Font フォント;
protected void Popupメッセージ_FormClosing( object sender, FormClosingEventArgs e )
{
FDK.Utilities.解放する( ref this.フォント );
}
+
protected void Popupメッセージ_Load( object sender, EventArgs e )
{
base.Location = new Point(
<Reference Include="System.Core" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.ServiceModel.Channels" />
+ <Reference Include="System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+ <HintPath>..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
+ </Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
+ <None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
public T 変更対象;
public T 変更前の値;
public T 変更後の値;
- public object 任意1; // 任意に使えるパラメータ領域
- public object 任意2; //
+ public object 任意1; // 任意に使えるパラメータ領域
+ public object 任意2; //
public セル( object 所有者ID, DGUndoを実行する<T> Undoアクション, DGRedoを実行する<T> Redoアクション, T 変更対象, T 変更前の値, T 変更後の値, object 任意1 = null, object 任意2 = null )
{
this.任意1 = 任意1;
this.任意2 = 任意2;
}
+
public override void Redoを実行する()
{
base.所有者ID = null;
this.redoアクション( this.変更対象, this.変更前の値, this.変更後の値, this.任意1, this.任意2 );
}
+
public override void Undoを実行する()
{
base.所有者ID = null;
}
protected DGRedoを実行する<T> redoアクション;
+
protected DGUndoを実行する<T> undoアクション;
}
-}
+}
\ No newline at end of file
class セルBase
{
public bool 所有権がある( object 所有者候補 ) => ( this.所有者ID == 所有者候補 );
+
public void 所有権を放棄する( object 現所有者 )
{
if( this.所有者ID == 現所有者 )
this.所有者ID = null;
}
+
public virtual void Redoを実行する()
{
}
+
public virtual void Undoを実行する()
{
}
class Cセルリスト : セルBase
{
public List<セルBase> セルs { get; set; } = null;
+
public int 次にセルが追加される位置0to { get; set; } = 0;
+
public Cセルリスト 親リスト { get; set; } = null;
+
public int Redo可能な回数 => ( this.現在の総セル数 - this.Undo可能な回数 );
+
public int Undo可能な回数 => ( this.次にセルが追加される位置0to );
+
public int 現在の総セル数 => ( this.セルs.Count );
public Cセルリスト( Cセルリスト 親リスト )
this.セルs = new List<セルBase>();
this.次にセルが追加される位置0to = 0;
}
+
public override void Redoを実行する()
{
// 前から順に実行する。
foreach( var cell in this.セルs )
cell.Redoを実行する();
}
+
public override void Undoを実行する()
{
// 後ろから順に実行する。
public static bool UndoRedoした直後である { get; set; } = false;
public int Redo可能な回数 => ( this.現在の総ノード数 - this.Undo可能な回数 );
+
public int Undo可能な回数 => ( this.ルート.Undo可能な回数 );
+
public int 現在の総ノード数 => ( this.ルート.現在の総セル数 );
public 管理()
{
this.現在のセル = this.ルート;
}
+
public セルBase Redoするセルを取得して返す()
{
this.現在のセル = this.ルート;
this.ルート.次にセルが追加される位置0to++;
return this.ルート.セルs[ this.ルート.次にセルが追加される位置0to - 1 ];
}
+
public セルBase Undoするセルを取得して返す()
{
this.現在のセル = this.ルート;
this.ルート.次にセルが追加される位置0to--;
return this.ルート.セルs[ this.ルート.次にセルが追加される位置0to ];
}
+
public セルBase Undoするセルを取得して返す_見るだけ()
{
this.現在のセル = this.ルート;
return this.ルート.セルs[ this.ルート.次にセルが追加される位置0to - 1 ];
}
+
public void セルを追加する( セルBase 単独セル )
{
// 追加するセルの後方にあるセルをすべて削除する。
this.現在のセル.セルs.Add( 単独セル );
this.現在のセル.次にセルが追加される位置0to++;
}
+
public void トランザクション記録を開始する()
{
// 追加するセルの後方にあるセルをすべて削除する。
this.現在のセル.次にセルが追加される位置0to++;
this.現在のセル = セルリスト;
}
+
public void トランザクション記録を終了する()
{
// リストセルを閉じる。
}
}
}
+
public void UndoRedoリストをすべて空にする()
{
this.ルート = new Cセルリスト( null );
}
protected Cセルリスト ルート = new Cセルリスト( null );
+
protected Cセルリスト 現在のセル = null;
}
}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="System.ValueTuple" version="4.3.0" targetFramework="net452" />
+</packages>
\ No newline at end of file
{
this.Form = form;
}
+
public void クリアする()
{
this.セルリスト.Clear();
}
+
public void 現在選択されているチップをボードにコピーする()
{
this.クリアする();
}
}
}
+
public void チップを指定位置から貼り付ける( int 貼り付け先頭の譜面内絶対位置grid )
{
if( 0 == this.セル数 )
}
protected メインフォーム Form;
+
protected readonly List<Cセル> セルリスト = new List<Cセル>();
}
}
public bool 未保存である
{
- get
- {
- return this._未保存である;
- }
+ get => this._未保存である;
set
{
// まず値を保存。
}
public bool 選択モードである => ( ( CheckState.Checked == this.toolStripButton選択モード.CheckState ) ? true : false );
+
public bool 編集モードである => ( ( CheckState.Checked == this.toolStripButton編集モード.CheckState ) ? true : false );
/// <summary>
public int GRID_PER_PIXEL => (int) ( int.Parse( Properties.Resources.GRID_PER_PIXEL ) / ( 1 + 0.25 * this.toolStripComboBox譜面拡大率.SelectedIndex ) );
public Config Config;
+
public 選択モード 選択モード;
+
public 編集モード 編集モード;
+
public C譜面 譜面;
+
public UndoRedo.管理 UndoRedo管理;
+
public クリップボード クリップボード;
+
public Size 譜面パネルサイズ => ( this.pictureBox譜面パネル.ClientSize );
+
public チップ種別 現在のチップ種別
{
get
this.label現在のチップ種別.Text = this._チップto名前[ value ];
}
}
+
public int 現在のチップ音量 = メインフォーム.最大音量;
+
public bool 初期化完了 = false;
public メインフォーム()
this._アプリの起動処理を行う();
}
+
public void 選択モードに切替えて関連GUIを設定する()
{
this.toolStripButton選択モード.CheckState = CheckState.Checked;
this.譜面をリフレッシュする();
}
+
public void 編集モードに切替えて関連GUIを設定する()
{
this.選択モード.全チップの選択を解除する();
this.toolStripMenuItem選択モード.CheckState = CheckState.Unchecked;
this.toolStripMenuItem編集モード.CheckState = CheckState.Checked;
}
+
public void 選択チップの有無に応じて編集用GUIのEnabledを設定する()
{
bool 譜面上に選択チップがある = this._選択チップが1個以上ある;
this.toolStripMenuItem選択チップの貼り付け.Enabled = クリップボードに選択チップがある;
this.toolStripMenuItem選択チップの削除.Enabled = 譜面上に選択チップがある;
}
+
public void 譜面をリフレッシュする()
{
this.pictureBox譜面パネル.Refresh();
}
+
public void UndoRedo用GUIのEnabledを設定する()
{
bool Undo可 = ( 0 < this.UndoRedo管理.Undo可能な回数 ) ? true : false;
this.toolStripButton元に戻す.Enabled = Undo可;
this.toolStripButtonやり直す.Enabled = Redo可;
}
+
public void 選択モードのコンテクストメニューを表示する( int x, int y )
{
this.contextMenuStrip譜面右メニュー.Show( this.pictureBox譜面パネル, x, y );
// メニューを表示した時のマウス座標を控えておく。
this._選択モードでコンテクストメニューを開いたときのマウスの位置 = new Point( x, y );
}
+
public void 譜面を縦スクロールする( int スクロール量grid )
{
int 現在の位置grid = this.vScrollBar譜面用垂直スクロールバー.Value;
private チップ種別 _現在のチップ種別 = チップ種別.Unknown;
-
#region " フォルダ、ファイルパス "
//----------------
//----------------
#endregion
-
#region " WCF サービス "
//----------------
private ChannelFactory<SST.IStrokeStyleTService> _SSTファクトリ = null;
}
private void _現在位置から再生する()
{
- int 小節番号 = this.譜面.譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( this.譜面.カレントラインの譜面内絶対位置grid, out int 位置grid );
+ int 小節番号 = this.譜面.譜面内絶対位置gridに位置する小節の情報を返す( this.譜面.カレントラインの譜面内絶対位置grid ).小節番号;
this._指定された小節の先頭から再生する( 小節番号 );
}
private void _現在位置からBGMのみ再生する()
{
- int 小節番号 = this.譜面.譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( this.譜面.カレントラインの譜面内絶対位置grid, out int 位置grid );
+ int 小節番号 = this.譜面.譜面内絶対位置gridに位置する小節の情報を返す( this.譜面.カレントラインの譜面内絶対位置grid ).小節番号;
this._指定された小節の先頭から再生する( 小節番号, ドラム音を発声する: false );
}
private void _指定された小節の先頭から再生する( int 小節番号, bool ドラム音を発声する = true )
}
private enum タブ種別 : int { 基本情報 = 0 }
+
private void _タブを選択する( タブ種別 eタブ種別 )
{
this.tabControl情報タブコンテナ.SelectedIndex = (int) eタブ種別;
{
UndoRedo.管理.UndoRedoした直後である = true;
}
+
private void _次のプロパティ変更がUndoRedoリストに載るようにする()
{
UndoRedo.管理.UndoRedoした直後である = false;
#region " メインフォーム イベント "
//-----------------
- protected void メインフォーム_DragDrop( object sender, DragEventArgs e )
- {
- string[] data = (string[]) e.Data.GetData( DataFormats.FileDrop );
-
- if( 1 <= data.Length )
- this._指定されたファイルを開く( data[ 0 ] ); // Dropされたファイルが複数個あっても、先頭のファイルだけを有効とする。
- }
protected void メインフォーム_DragEnter( object sender, DragEventArgs e )
{
if( e.Data.GetDataPresent( DataFormats.FileDrop ) )
{
- e.Effect = DragDropEffects.Copy; // ファイルならコピーと見なす(カーソルがコピー型になる)
+ e.Effect = DragDropEffects.Copy; // ファイルならコピーと見なす(カーソルがコピー型になる)
}
else
{
- e.Effect = DragDropEffects.None; // ファイルじゃないなら無視(カーソル変化なし)
+ e.Effect = DragDropEffects.None; // ファイルじゃないなら無視(カーソル変化なし)
}
}
+
+ protected void メインフォーム_DragDrop( object sender, DragEventArgs e )
+ {
+ string[] data = (string[]) e.Data.GetData( DataFormats.FileDrop );
+
+ if( 1 <= data.Length )
+ this._指定されたファイルを開く( data[ 0 ] ); // Dropされたファイルが複数個あっても、先頭のファイルだけを有効とする。
+ }
+
protected void メインフォーム_FormClosing( object sender, FormClosingEventArgs e )
{
if( DialogResult.Cancel == this._未保存なら保存する() )
{
this._新規作成する();
}
+
protected void toolStripMenuItem開く_Click( object sender, EventArgs e )
{
this._開く();
}
+
protected void toolStripMenuItem上書き保存_Click( object sender, EventArgs e )
{
this._上書き保存する();
}
+
protected void toolStripMenuItem名前を付けて保存_Click( object sender, EventArgs e )
{
this._名前を付けて保存する();
}
+
protected void toolStripMenuItem終了_Click( object sender, EventArgs e )
{
this._終了する();
}
+
protected void toolStripMenuItem最近使ったファイル_Click( object sender, EventArgs e )
{
// ※このイベントハンドラに対応する「toolStripMenuItem最近使ったファイル」というアイテムはデザイナにはないので注意。
{
this._元に戻す();
}
+
protected void toolStripMenuItemやり直す_Click( object sender, EventArgs e )
{
this._やり直す();
}
+
protected void toolStripMenuItem切り取り_Click( object sender, EventArgs e )
{
this._切り取る();
}
+
protected void toolStripMenuItemコピー_Click( object sender, EventArgs e )
{
this._コピーする();
}
+
protected void toolStripMenuItem貼り付け_Click( object sender, EventArgs e )
{
var マウスの位置 = this._現在のマウス位置を譜面パネル内座標pxに変換して返す();
this._貼り付ける( this.譜面.譜面パネル内Y座標pxにおける譜面内絶対位置gridをガイド幅単位で返す( マウスの位置.Y ) );
}
}
+
protected void toolStripMenuItem削除_Click( object sender, EventArgs e )
{
this._削除する();
}
+
protected void toolStripMenuItemすべて選択_Click( object sender, EventArgs e )
{
this._すべて選択する();
}
+
protected void toolStripMenuItem選択モード_Click( object sender, EventArgs e )
{
this.選択モードに切替えて関連GUIを設定する();
}
+
protected void toolStripMenuItem編集モード_Click( object sender, EventArgs e )
{
this.編集モードに切替えて関連GUIを設定する();
}
+
protected void toolStripMenuItemモード切替え_Click( object sender, EventArgs e )
{
if( this.選択モードである )
this.選択モードに切替えて関連GUIを設定する();
}
}
+
protected void toolStripMenuItem検索_Click( object sender, EventArgs e )
{
this._検索する();
{
this._ガイド間隔を変更する( 4 );
}
+
protected void toolStripMenuItemガイド間隔6分_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 6 );
}
+
protected void toolStripMenuItemガイド間隔8分_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 8 );
}
+
protected void toolStripMenuItemガイド間隔12分_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 12 );
}
+
protected void toolStripMenuItemガイド間隔16分_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 16 );
}
+
protected void toolStripMenuItemガイド間隔24分_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 24 );
}
+
protected void toolStripMenuItemガイド間隔32分_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 32 );
}
+
protected void toolStripMenuItemガイド間隔36分_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 36 );
}
+
protected void toolStripMenuItemガイド間隔48分_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 48 );
}
+
protected void toolStripMenuItemガイド間隔64分_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 64 );
}
+
protected void toolStripMenuItemガイド間隔128分_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 128 );
}
+
protected void toolStripMenuItemガイド間隔フリー_Click( object sender, EventArgs e )
{
this._ガイド間隔を変更する( 0 );
}
+
protected void toolStripMenuItemガイド間隔拡大_Click( object sender, EventArgs e )
{
this._ガイド間隔を拡大する();
}
+
protected void toolStripMenuItemガイド間隔縮小_Click( object sender, EventArgs e )
{
this._ガイド間隔を縮小する();
{
this._最初から再生する();
}
+
protected void toolStripMenuItem現在位置から再生_Click( object sender, EventArgs e )
{
this._現在位置から再生する();
}
+
protected void toolStripMenuItem現在位置からBGMのみ再生_Click( object sender, EventArgs e )
{
this._現在位置からBGMのみ再生する();
}
+
protected void toolStripMenuItem再生停止_Click( object sender, EventArgs e )
{
this._再生を停止する();
{
this._新規作成する();
}
+
protected void toolStripButton開く_Click( object sender, EventArgs e )
{
this._開く();
}
+
protected void toolStripButton上書き保存_Click( object sender, EventArgs e )
{
this._上書き保存する();
}
+
protected void toolStripButton切り取り_Click( object sender, EventArgs e )
{
this._切り取る();
}
+
protected void toolStripButtonコピー_Click( object sender, EventArgs e )
{
this._コピーする();
}
+
protected void toolStripButton貼り付け_Click( object sender, EventArgs e )
{
var マウスの位置 = this._現在のマウス位置を譜面パネル内座標pxに変換して返す();
this._貼り付ける( this.譜面.譜面パネル内Y座標pxにおける譜面内絶対位置gridをガイド幅単位で返す( マウスの位置.Y ) );
}
}
+
protected void toolStripButton削除_Click( object sender, EventArgs e )
{
this._削除する();
{
this._元に戻す();
}
+
protected void toolStripButtonやり直す_Click( object sender, EventArgs e )
{
this._やり直す();
{
this._譜面拡大率を変更する( this.toolStripComboBox譜面拡大率.SelectedIndex + 1 );
}
+
protected void toolStripComboBoxガイド間隔_SelectedIndexChanged( object sender, EventArgs e )
{
switch( this.toolStripComboBoxガイド間隔.SelectedIndex )
}
}
+
protected void toolStripButton選択モード_Click( object sender, EventArgs e )
{
this._選択モードにする();
}
+
protected void toolStripButton編集モード_Click( object sender, EventArgs e )
{
this._編集モードにする();
{
this._最初から再生する();
}
+
protected void toolStripButton現在位置から再生_Click( object sender, EventArgs e )
{
this._現在位置から再生する();
}
+
protected void toolStripButton現在位置からBGMのみ再生_Click( object sender, EventArgs e )
{
this._現在位置からBGMのみ再生する();
}
+
protected void toolStripButton再生停止_Click( object sender, EventArgs e )
{
this._再生を停止する();
this._現在のチップ音量をツールバーに表示する();
}
+
protected void toolStripButton音量UP_Click( object sender, EventArgs e )
{
int 新音量 = this.現在のチップ音量 + 1;
this.編集モード.MouseClick( e );
}
}
+
protected void pictureBox譜面パネル_MouseDown( object sender, MouseEventArgs e )
{
// 選択モードオブジェクトへ処理を引き継ぐ。
if( this.選択モードである )
this.選択モード.MouseDown( e );
}
+
protected void pictureBox譜面パネル_MouseEnter( object sender, EventArgs e )
{
// オートフォーカスが有効の場合、譜面にマウスが入ったら譜面がフォーカスを得る。"
if( this.Config.AutoFocus )
this.pictureBox譜面パネル.Focus();
}
+
protected void pictureBox譜面パネル_MouseLeave( object sender, EventArgs e )
{
// 編集モードオブジェクトへ処理を引き継ぐ。
if( this.編集モードである )
this.編集モード.MouseLeave( e );
}
+
protected void pictureBox譜面パネル_MouseMove( object sender, MouseEventArgs e )
{
// 選択・編集モードオブジェクトのいずれかへ処理を引き継ぐ。
this.編集モード.MouseMove( e );
}
}
+
protected void pictureBox譜面パネル_Paint( object sender, PaintEventArgs e )
{
if( false == this.初期化完了 )
this.編集モード.Paint( e );
}
}
+
protected void pictureBox譜面パネル_PreviewKeyDown( object sender, PreviewKeyDownEventArgs e )
{
if( Keys.Prior == e.KeyCode )
//-----------------
#endregion
}
+
protected void splitContainer分割パネルコンテナ_Panel2_SizeChanged( object sender, EventArgs e )
{
if( false == this.初期化完了 )
this._垂直スクロールバーと譜面の上下位置を調整する();
}
+
protected void splitContainer分割パネルコンテナ_Panel2_Paint( object sender, PaintEventArgs e )
{
if( false == this.初期化完了 )
{
this._切り取る();
}
+
protected void toolStripMenuItem選択チップのコピー_Click( object sender, EventArgs e )
{
this._コピーする();
}
+
protected void toolStripMenuItem選択チップの貼り付け_Click( object sender, EventArgs e )
{
// メニューが開かれたときのマウスの座標を取得。
// アクションを実行。
this._貼り付ける( this.譜面.譜面パネル内Y座標pxにおける譜面内絶対位置gridをガイド幅単位で返す( マウスの位置.Y ) );
}
+
protected void toolStripMenuItem選択チップの削除_Click( object sender, EventArgs e )
{
this._削除する();
}
+
protected void toolStripMenuItemすべてのチップの選択_Click( object sender, EventArgs e )
{
// 編集モードなら強制的に選択モードにする。
// 全チップを選択。
this.選択モード.全チップを選択する();
}
+
protected void toolStripMenuItem小節長変更_Click( object sender, EventArgs e )
{
// メニューが開かれたときのマウスの座標を取得。
// アクションを実行。
this._小節長倍率を変更する( this.譜面.譜面パネル内Y座標pxにおける小節番号を返す( マウスの位置.Y ) );
}
+
protected void toolStripMenuItem小節の挿入_Click( object sender, EventArgs e )
{
// メニューが開かれたときのマウスの座標を取得。
// アクションを実行。
this._小節を挿入する( this.譜面.譜面パネル内Y座標pxにおける小節番号を返す( マウスの位置.Y ) );
}
+
protected void toolStripMenuItem小節の削除_Click( object sender, EventArgs e )
{
// メニューが開かれたときのマウスの座標を取得。
// スコアには随時保存する。
譜面.SSTFormatScore.Header.曲名 = this.textBox曲名.Text;
}
+
protected void textBox曲名_Leave( object sender, EventArgs e )
{
// 最新の UndoRedoセル の所有権を放棄する。
this.UndoRedo管理.Undoするセルを取得して返す_見るだけ()?.所有権を放棄する( this.textBox曲名 );
}
+
private string textBox曲名_以前の値 = "";
protected void textBox説明_TextChanged( object sender, EventArgs e )
// スコアには随時保存する。
譜面.SSTFormatScore.Header.説明文 = this.textBox説明.Text;
}
+
protected void textBox説明_Leave( object sender, EventArgs e )
{
// 最新 UndoRedoセル の所有権を放棄する。
this.UndoRedo管理.Undoするセルを取得して返す_見るだけ()?.所有権を放棄する( this.textBox説明 );
}
+
private string textBox説明_以前の値 = "";
protected void textBoxメモ_TextChanged( object sender, EventArgs e )
//-----------------
#endregion
}
+
protected void textBoxメモ_Leave( object sender, EventArgs e )
{
// 最新 UndoRedoセル の所有権を放棄する。
// 小節メモをリフレッシュ。
this.splitContainer分割パネルコンテナ.Panel2.Refresh();
}
+
private string textBoxメモ_以前の値 = "";
protected void numericUpDownメモ用小節番号_ValueChanged( object sender, EventArgs e )
{
InitializeComponent();
}
+
public 数値入力ダイアログ( decimal 開始値, decimal 最小値, decimal 最大値, string 表示するメッセージ )
{
this.InitializeComponent();
partial class 検索条件入力ダイアログ : Form
{
public bool 小節範囲指定CheckBoxがチェックされている => ( this.checkBox小節範囲指定.Checked );
+
public int 小節範囲開始番号
{
get
return 開始番号;
}
}
+
public int 小節範囲終了番号
{
get
this.checkedListBoxレーン選択リスト.Items.Add( this.編集レーン名[ kvp.Key ], 前回の設定値.レーンを検索対象にする[ kvp.Key ] );
//----------------
#endregion
+
#region " チップリストとチップリストチェックに前回値を指定する。未初期化なら初期化する。"
//----------------
// 未初期化なら先に初期化する。
#endregion
this.checkBox小節範囲指定.CheckState = 前回の設定値.検索する小節範囲を指定する;
+
this.チェックに連動して有効無効が決まるパーツについてEnabledを設定する();
}
+
public bool 選択されている( 編集レーン種別 laneType )
{
// First() で要素が見つからなかったらバグなので、そのまま System.InvalidOperationException を放出させる。
var key = this.dic行と編集レーン対応表.First( ( kvp ) => ( kvp.Value == laneType ) ).Key;
return ( this.checkedListBoxレーン選択リスト.GetItemCheckState( key ) == CheckState.Checked );
}
+
public bool 選択されている( チップ種別 chipType )
{
// First() で要素が見つからなかったらバグなので、そのまま System.InvalidOperationException を放出させる。
};
//-----------------
#endregion
+
protected readonly string[] 編集レーン名
#region " *** "
//-----------------
};
//-----------------
#endregion
+
protected readonly Dictionary<int, チップ種別> dic行とチップ種別対応表
#region " *** "
//-----------------
};
//-----------------
#endregion
+
protected readonly string[] チップ種別名
#region " *** "
//-----------------
#endregion
}
}
+
protected void OnKeyDown( object sender, KeyEventArgs e )
{
// ENTER → OK
else if( e.KeyCode == Keys.Escape )
this.buttonキャンセル.PerformClick();
}
+
protected void textBox小節範囲開始_KeyDown( object sender, KeyEventArgs e )
{
// ENTER → OK
else if( e.KeyCode == Keys.Escape )
this.buttonキャンセル.PerformClick();
}
+
protected void textBox小節範囲終了_KeyDown( object sender, KeyEventArgs e )
{
// ENTER → OK
else if( e.KeyCode == Keys.Escape )
this.buttonキャンセル.PerformClick();
}
+
protected void checkBox小節範囲指定_CheckStateChanged( object sender, EventArgs e )
{
this.チェックに連動して有効無効が決まるパーツについてEnabledを設定する();
}
+
protected void checkBox小節範囲指定_KeyDown( object sender, KeyEventArgs e )
{
// ENTER → OK
else if( e.KeyCode == Keys.Escape )
this.buttonキャンセル.PerformClick();
}
+
protected void checkedListBoxレーン選択リスト_KeyDown( object sender, KeyEventArgs e )
{
// ENTER → OK
else if( e.KeyCode == Keys.Escape )
this.buttonキャンセル.PerformClick();
}
+
protected void buttonAllレーン_Click( object sender, EventArgs e )
{
for( int i = 0; i < this.checkedListBoxレーン選択リスト.Items.Count; i++ )
this.checkedListBoxレーン選択リスト.SetItemChecked( i, true );
}
+
protected void buttonAllレーン_KeyDown( object sender, KeyEventArgs e )
{
// ESC → キャンセル
if( e.KeyCode == Keys.Escape )
this.buttonキャンセル.PerformClick();
}
+
protected void buttonClearレーン_Click( object sender, EventArgs e )
{
for( int i = 0; i < this.checkedListBoxレーン選択リスト.Items.Count; i++ )
this.checkedListBoxレーン選択リスト.SetItemCheckState( i, CheckState.Unchecked );
}
+
protected void buttonClearレーン_KeyDown( object sender, KeyEventArgs e )
{
// ESC → キャンセル
if( e.KeyCode == Keys.Escape )
this.buttonキャンセル.PerformClick();
}
+
protected void buttonAllチップ_Click( object sender, EventArgs e )
{
for( int i = 0; i < this.checkedListBoxチップ選択リスト.Items.Count; i++ )
this.checkedListBoxチップ選択リスト.SetItemChecked( i, true );
}
+
protected void buttonAllチップ_KeyDown( object sender, KeyEventArgs e )
{
// ESC → キャンセル
if( e.KeyCode == Keys.Escape )
this.buttonキャンセル.PerformClick();
}
+
protected void buttonClearチップ_Click( object sender, EventArgs e )
{
for( int i = 0; i < this.checkedListBoxチップ選択リスト.Items.Count; i++ )
this.checkedListBoxチップ選択リスト.SetItemCheckState( i, CheckState.Unchecked );
}
+
protected void buttonClearチップ_KeyDown( object sender, KeyEventArgs e )
{
// ESC → キャンセル
this.チップを配置または削除する( e );
this.Form.譜面をリフレッシュする();
}
+
public void MouseLeave( EventArgs e )
{
this.現在のチップカーソル領域 = new Rectangle( 0, 0, 0, 0 );
this.Form.譜面をリフレッシュする();
}
+
public void MouseMove( MouseEventArgs e )
{
#region " チップカーソルの領域を、現在の位置に合わせて更新する。"
//-----------------
#endregion
}
+
public void Paint( PaintEventArgs e )
{
this.チップカーソルを描画する( e.Graphics, this.Form.現在のチップ種別 );
}
+
public void PreviewKeyDown( PreviewKeyDownEventArgs e )
{
// SPACE
}
protected メインフォーム Form;
+
protected 編集レーン種別 現在チップカーソルがある編集レーン;
+
protected int 現在のチップカーソルの譜面先頭からのガイド単位の位置grid;
+
protected bool 強制種別使用中 = false;
+
protected Dictionary<編集レーン種別, int> dicレーン別チップ種別バックアップ;
+
protected Dictionary<編集レーン種別, int> dicレーン別現在のチップ種別番号;
+
protected Dictionary<編集レーン種別, List<チップ種別>> dicレーン別チップ種別対応表;
protected void チップカーソルを描画する( Graphics g, チップ種別 eチップ )
this.Form.譜面.チップを指定領域へ描画する( g, eチップ, this.Form.現在のチップ音量, this.現在のチップカーソル領域, null );
this.Form.譜面.チップの太枠を指定領域へ描画する( g, this.現在のチップカーソル領域 );
}
+
protected void チップカーソルのあるレーンに合わせて現在のチップ種別を変更する()
{
int index = this.dicレーン別現在のチップ種別番号[ this.現在チップカーソルがある編集レーン ];
this.Form.現在のチップ種別 = this.dicレーン別チップ種別対応表[ this.現在チップカーソルがある編集レーン ][ index ];
}
+
protected void チップを配置または削除する( MouseEventArgs eClick )
{
if( ( 0 >= this.現在のチップカーソル領域.Width ) ||( 0 >= this.現在のチップカーソル領域.Height) )
{
this.Form = form;
}
+
public void Dispose()
{
FDK.Utilities.解放する( ref this.選択領域用のブラシ );
FDK.Utilities.解放する( ref this.選択領域用のペン );
this.Form = null;
}
+
public void 個別選択を解除する( チップ chip )
{
var cell = new UndoRedo.セル<チップ>(
cell.Redoを実行する();
this.Form.UndoRedo用GUIのEnabledを設定する();
}
+
public void 全チップを選択する()
{
try
if( chip.選択が確定していない )
{
var 変更前のチップ = new チップ( chip );
+
var 変更後のチップ = new チップ( chip ) {
ドラッグ操作により選択中である = false,
選択が確定している = true,
変更後の値: 変更後のチップ );
this.Form.UndoRedo管理.セルを追加する( cell );
+
cell.Redoを実行する();
}
}
this.Form.譜面をリフレッシュする();
}
}
+
public void 全チップの選択を解除する()
{
try
this.Form.未保存である = true;
}
}
+
public void 検索する()
{
using( var dialog = new 検索条件入力ダイアログ() )
if( dialog.ShowDialog( this.Form ) != DialogResult.OK )
return;
- int 開始小節番号 = dialog.小節範囲指定CheckBoxがチェックされている ? dialog.小節範囲開始番号 : 0;
- int 終了小節番号 = dialog.小節範囲指定CheckBoxがチェックされている ? dialog.小節範囲終了番号 : this.Form.譜面.SSTFormatScore.最大小節番号;
+ int 開始小節番号 = ( dialog.小節範囲指定CheckBoxがチェックされている ) ? dialog.小節範囲開始番号 : 0;
+ int 終了小節番号 = ( dialog.小節範囲指定CheckBoxがチェックされている ) ? dialog.小節範囲終了番号 : this.Form.譜面.SSTFormatScore.最大小節番号;
if( 0 > 開始小節番号 )
開始小節番号 = 0; // 省略時は 0 とみなす。
if( e.Button == MouseButtons.Right )
this.Form.選択モードのコンテクストメニューを表示する( e.X, e.Y );
}
+
public void MouseDown( MouseEventArgs e )
{
// 左クリック
this.移動の開始処理( e );
}
}
+
public void MouseMove( MouseEventArgs e )
{
// (A) 左ボタンが押されながら移動している場合 → 継続処理
// 譜面を再描画する。
this.Form.譜面をリフレッシュする();
}
+
public void Paint( PaintEventArgs e )
{
if( this.範囲選択のためにドラッグ中である )
// 全般
protected メインフォーム Form = null;
+
protected SolidBrush 選択領域用のブラシ = new SolidBrush( Color.FromArgb( 80, 55, 55, 255 ) );
+
protected Pen 選択領域用のペン = new Pen( Color.LightBlue );
+
protected void 現在の選択範囲を描画する( Graphics g )
{
var 現在の選択領域px = new Rectangle() {
g.DrawRectangle( Pens.LightBlue, 現在の選択領域px );
}
}
+
protected void 譜面パネルの上下端にきたならスクロールする( MouseEventArgs e )
{
const int 上端スクロール発動幅px = 70;
// 移動関連
protected bool 移動のためにドラッグ中である = false;
+
protected Point 現在の移動用ドラッグ開始位置px = new Point( 0, 0 );
+
protected Point 現在の移動用ドラッグ終了位置px = new Point( 0, 0 );
+
protected Dictionary<チップ, チップ> 移動開始時のチップ状態 = new Dictionary<チップ, チップ>();
+
protected struct STレーングリッド座標
{
public int 編集レーン番号; // X座標に相当。
public int 譜面内絶対位置grid; // Y座標に相当。
};
protected STレーングリッド座標 前回のマウス位置LaneGrid = new STレーングリッド座標();
+
protected void 移動の開始処理( MouseEventArgs e )
{
this.移動のためにドラッグ中である = true;
this.移動開始時のチップ状態.Add( chip, new チップ( chip ) );
}
}
+
protected void 移動の継続処理( MouseEventArgs e )
{
// ドラッグ終了位置を現在のマウスの位置に更新。
//-----------------
#endregion
}
+
protected void 移動の終了処理( MouseEventArgs e )
{
try
// ここではまだ、譜面範囲外に出ている(=枠外レーン数がゼロでない)チップの削除は行わない。(その後再び移動される可能性があるため。)
// これらの削除処理は「t全チップの選択を解除する()」で行う。
var chip変更前 = this.移動開始時のチップ状態[ chip ];
- int 小節番号 = this.Form.譜面.譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( chip.譜面内絶対位置grid, out int 小節先頭位置grid );
+ var 小節情報 = this.Form.譜面.譜面内絶対位置gridに位置する小節の情報を返す( chip.譜面内絶対位置grid );
var chip変更後 = new チップ( chip ) {
- 小節番号 = 小節番号,
- 小節解像度 = (int) ( this.Form.GRID_PER_PART * this.Form.譜面.SSTFormatScore.小節長倍率を取得する( 小節番号 ) ),
- 小節内位置 = chip.譜面内絶対位置grid - 小節先頭位置grid,
+ 小節番号 = 小節情報.小節番号,
+ 小節解像度 = (int) ( this.Form.GRID_PER_PART * this.Form.譜面.SSTFormatScore.小節長倍率を取得する( 小節情報.小節番号 ) ),
+ 小節内位置 = chip.譜面内絶対位置grid - 小節情報.小節の先頭位置grid,
};
var cell = new UndoRedo.セル<チップ>(
所有者ID: null,
// 範囲選択関連
protected bool 範囲選択のためにドラッグ中である = false;
+
protected Point 現在の範囲選択用ドラッグ開始位置px = new Point( 0, 0 );
+
protected Point 現在の範囲選択用ドラッグ終了位置px = new Point( 0, 0 );
+
protected void 範囲選択の開始処理( MouseEventArgs e )
{
this.範囲選択のためにドラッグ中である = true;
// 全チップについて、選択・選択解除の取捨選択。
this.現在のドラッグ範囲中のチップをすべて選択状態にしそれ以外は選択を解除する();
}
+
protected void 範囲選択の継続処理( MouseEventArgs e )
{
// クリッピング。
// チップの選択 or 選択解除。
this.現在のドラッグ範囲中のチップをすべて選択状態にしそれ以外は選択を解除する();
}
+
protected void 範囲選択の終了処理( MouseEventArgs e )
{
this.範囲選択のためにドラッグ中である = false;
this.Form.選択チップの有無に応じて編集用GUIのEnabledを設定する();
}
}
+
protected void 現在のドラッグ範囲中のチップをすべて選択状態にしそれ以外は選択を解除する()
{
// 現在のドラッグ範囲を lane×grid 座標で算出する。
{
public bool 後続も全部変更する
{
- get
- {
- return this.checkBox後続設定.Checked;
- }
- set
- {
- this.checkBox後続設定.CheckState = value ? CheckState.Checked : CheckState.Unchecked;
- }
+ get => this.checkBox後続設定.Checked;
+ set => this.checkBox後続設定.CheckState = value ? CheckState.Checked : CheckState.Unchecked;
}
+
public float 倍率
{
- get
- {
- return (float) this.numericUpDown小節長の倍率.Value;
- }
- set
- {
- this.numericUpDown小節長の倍率.Value = (decimal) value;
- }
+ get => (float) this.numericUpDown小節長の倍率.Value;
+ set => this.numericUpDown小節長の倍率.Value = (decimal) value;
}
public 小節長倍率入力ダイアログ( int 小節番号 )
else if( e.KeyCode == Keys.Escape )
this.buttonキャンセル.PerformClick();
}
+
protected void checkBox後続設定_KeyDown( object sender, KeyEventArgs e )
{
// ENTER → OK
// プロパティ
public スコア SSTFormatScore;
+
public int 譜面表示下辺の譜面内絶対位置grid { get; set; }
+
public int カレントラインの譜面内絶対位置grid
{
- get
- {
- return ( this.譜面表示下辺の譜面内絶対位置grid + ( 230 * this.Form.GRID_PER_PIXEL ) ); // 譜面拡大率によらず、大体下辺から -230 pixel くらいで。
- }
+ get => ( this.譜面表示下辺の譜面内絶対位置grid + ( 230 * this.Form.GRID_PER_PIXEL ) ); // 譜面拡大率によらず、大体下辺から -230 pixel くらいで。
}
+
public int 全小節の高さgrid
{
get
return 高さgrid;
}
}
+
public int レーンの合計幅px
{
- get
- {
- return ( Enum.GetValues( typeof( 編集レーン種別 ) ).Length - 1 ) * this.チップサイズpx.Width; // -1 は Unknown の分
- }
+ get => ( Enum.GetValues( typeof( 編集レーン種別 ) ).Length - 1 ) * this.チップサイズpx.Width; // -1 は Unknown の分
}
+
public int 譜面表示下辺に位置する小節番号
{
- get
- {
- return this.譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( this.譜面表示下辺の譜面内絶対位置grid, out _ );
- }
+ get => this.譜面内絶対位置gridに位置する小節の情報を返す( this.譜面表示下辺の譜面内絶対位置grid ).小節番号;
}
+
public int カレントラインに位置する小節番号
{
- get
- {
- return this.譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( this.カレントラインの譜面内絶対位置grid, out _ );
- }
+ get => this.譜面内絶対位置gridに位置する小節の情報を返す( this.カレントラインの譜面内絶対位置grid ).小節番号;
}
// メソッド
//-----------------
#endregion
}
+
public void Dispose()
{
FDK.Utilities.解放する( ref this.SSTFormatScore );
//-----------------
#endregion
}
+
public void SSTFファイルを書き出す( string ファイル名, string ヘッダ行 )
{
this.SSTFormatScore.曲データファイルを書き出す( ファイル名, $"{ヘッダ行}{Environment.NewLine}" );
//-----------------
#endregion
}
+
public void チップを指定領域へ描画する( Graphics g, チップ種別 eチップ, int 音量, Rectangle チップ描画領域, string チップ内文字列 )
{
// ※SSTFormat.チップ の描画以外の目的でも呼ばれるため、本メソッドの引数には SSTFormat.チップ を入れていない。
break;
}
}
+
public void チップの太枠を指定領域へ描画する( Graphics g, Rectangle チップ描画領域 )
{
g.DrawRectangle( this.チップの太枠ペン, チップ描画領域 );
return 高さgrid;
}
+
public 編集レーン種別 譜面パネル内X座標pxにある編集レーンを返す( int 譜面パネル内X座標px )
{
int レーン番号 = 譜面パネル内X座標px / this.チップサイズpx.Width;
return 編集レーン種別.Unknown;
}
+
public int 編集レーンのX座標pxを返す( 編集レーン種別 lane )
{
if( lane == 編集レーン種別.Unknown )
return this.dicレーン番号[ lane ] * this.チップサイズpx.Width;
}
+
public int 譜面パネル内Y座標pxにおける小節番号を返す( int 譜面パネル内Y座標px )
{
- return this.譜面パネル内Y座標pxにおける小節番号とその小節の譜面内絶対位置gridを返す( 譜面パネル内Y座標px, out _ );
+ return this.譜面パネル内Y座標pxにおける小節番号とその小節の譜面内絶対位置gridを返す( 譜面パネル内Y座標px ).小節番号;
}
+
public int 譜面パネル内Y座標pxにおける小節の譜面内絶対位置gridを返す( int 譜面パネル内Y座標px )
{
- this.譜面パネル内Y座標pxにおける小節番号とその小節の譜面内絶対位置gridを返す( 譜面パネル内Y座標px, out int 小節の譜面内絶対位置grid );
- return 小節の譜面内絶対位置grid;
+ return this.譜面パネル内Y座標pxにおける小節番号とその小節の譜面内絶対位置gridを返す( 譜面パネル内Y座標px ).小節の譜面内絶対位置grid;
}
- public int 譜面パネル内Y座標pxにおける小節番号とその小節の譜面内絶対位置gridを返す( int 譜面パネル内Y座標px, out int 小節の譜面内絶対位置grid )
+
+ public ( int 小節番号, int 小節の譜面内絶対位置grid ) 譜面パネル内Y座標pxにおける小節番号とその小節の譜面内絶対位置gridを返す( int 譜面パネル内Y座標px )
{
int 譜面パネル内Y座標に対応する譜面内絶対位置grid =
this.譜面表示下辺の譜面内絶対位置grid + ( this.Form.譜面パネルサイズ.Height - 譜面パネル内Y座標px ) * this.Form.GRID_PER_PIXEL;
if( 譜面パネル内Y座標に対応する譜面内絶対位置grid < 0 )
{
- 小節の譜面内絶対位置grid = -1;
- return -1;
+ return ( 小節番号: -1, 小節の譜面内絶対位置grid: -1 );
}
int 現在の小節の先頭までの長さgrid = 0;
if( 譜面パネル内Y座標に対応する譜面内絶対位置grid < 次の小節の先頭までの長さgrid )
{
- 小節の譜面内絶対位置grid = 現在の小節の先頭までの長さgrid;
- return i;
+ return (小節番号: i, 小節の譜面内絶対位置grid: 現在の小節の先頭までの長さgrid);
}
i++;
}
}
+
public int 譜面パネル内Y座標pxにおける譜面内絶対位置gridを返す( int 譜面パネル内Y座標px )
{
int 譜面パネル底辺からの高さpx = this.Form.譜面パネルサイズ.Height - 譜面パネル内Y座標px;
return this.譜面表示下辺の譜面内絶対位置grid + ( 譜面パネル底辺からの高さpx * this.Form.GRID_PER_PIXEL );
}
+
public int 譜面パネル内Y座標pxにおける譜面内絶対位置gridをガイド幅単位で返す( int 譜面パネル内Y座標px )
{
int 最高解像度での譜面内絶対位置grid = this.譜面パネル内Y座標pxにおける譜面内絶対位置gridを返す( 譜面パネル内Y座標px );
int 対応する小節の小節先頭からの相対位置grid = ( ( 最高解像度での譜面内絶対位置grid - 対応する小節の譜面内絶対位置grid ) / this.ガイド間隔grid ) * this.ガイド間隔grid;
return 対応する小節の譜面内絶対位置grid + 対応する小節の小節先頭からの相対位置grid;
}
+
public int 譜面内絶対位置gridにおける対象領域内のY座標pxを返す( int 譜面内絶対位置grid, Size 対象領域サイズpx )
{
int 対象領域内の高さgrid = 譜面内絶対位置grid - this.譜面表示下辺の譜面内絶対位置grid;
return ( 対象領域サイズpx.Height - ( 対象領域内の高さgrid / this.Form.GRID_PER_PIXEL ) );
}
- public int 譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( int 譜面内絶対位置grid, out int 位置grid )
+
+ public ( int 小節番号, int 小節の先頭位置grid ) 譜面内絶対位置gridに位置する小節の情報を返す( int 譜面内絶対位置grid )
{
if( 0 > 譜面内絶対位置grid )
- throw new ArgumentOutOfRangeException( "n譜面内絶対位置grid が負数です。" );
+ throw new ArgumentOutOfRangeException( "譜面内絶対位置grid が負数です。" );
+
+ var result = ( 小節番号: 0, 小節の先頭位置grid : -1 );
- 位置grid = -1;
int n = 0;
int back = 0;
int i = 0;
if( 譜面内絶対位置grid < n )
{
- 位置grid = back;
- return i;
+ result.小節の先頭位置grid = back;
+ result.小節番号 = i;
+ break;
}
i++;
}
+
+ return result;
}
+
public double 譜面内絶対位置gridにおけるBPMを返す( int 譜面内絶対位置grid )
{
double bpm = スコア.初期BPM;
return bpm;
}
+
public チップ 譜面パネル内座標pxに存在するチップがあれば返す( int x, int y )
{
var 座標の編集レーン = this.譜面パネル内X座標pxにある編集レーンを返す( x );
return null;
}
+
public int 小節長をグリッドで返す( int 小節番号 )
{
double この小節の倍率 = this.SSTFormatScore.小節長倍率を取得する( 小節番号 );
{
this.ガイド間隔grid = ( n分 == 0 ) ? 1 : ( this.Form.GRID_PER_PART / n分 );
}
+
public void チップを配置または置換する( 編集レーン種別 e編集レーン, チップ種別 eチップ, int 譜面内絶対位置grid, string チップ文字列, int 音量, double BPM, bool 選択確定中 )
{
try
this.チップを削除する( e編集レーン, 譜面内絶対位置grid ); // そこにチップがなければ何もしない。
// 新しいチップを作成し配置する。
- int 小節番号 = this.譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( 譜面内絶対位置grid, out int 小節先頭位置grid );
- int 小節の長さgrid = this.小節長をグリッドで返す( 小節番号 );
+ var 小節情報 = this.譜面内絶対位置gridに位置する小節の情報を返す( 譜面内絶対位置grid );
+ int 小節の長さgrid = this.小節長をグリッドで返す( 小節情報.小節番号 );
var chip = new チップ() {
ヒット済みである = false, // SSTFEditorでは使わない
チップ種別 = eチップ,
音量 = 音量,
小節解像度 = 小節の長さgrid,
- 小節内位置 = 譜面内絶対位置grid - 小節先頭位置grid,
- 小節番号 = 小節番号,
+ 小節内位置 = 譜面内絶対位置grid - 小節情報.小節の先頭位置grid,
+ 小節番号 = 小節情報.小節番号,
譜面内絶対位置grid = 譜面内絶対位置grid,
チップ内文字列 = チップ文字列,
};
this.Form.未保存である = true;
}
}
+
public void チップを削除する( 編集レーン種別 e編集レーン, int 譜面内絶対位置grid )
{
var 削除チップ =
this.Form.UndoRedo用GUIのEnabledを設定する();
}
}
+
public void 最後の小節の後ろに小節を4つ追加する()
{
// 最終小節の小節先頭位置grid と 小節長倍率 を取得する。
}
protected メインフォーム Form;
+
protected int ガイド間隔grid = 0;
protected const int レーン番号表示高さpx = 32;
+
protected const int チップ背景色透明度 = 192;
+
protected const int チップ明影透明度 = 255;
+
protected const int チップ暗影透明度 = 64;
+
protected const int レーン背景色透明度 = 25;
protected readonly Dictionary<編集レーン種別, Color> レーンto背景色
#endregion
protected Bitmap 譜面パネル背景 = null;
+
protected Font 小節番号文字フォント = new Font( "MS UI Gothic", 50f, FontStyle.Regular );
+
protected Brush 小節番号文字ブラシ = new SolidBrush( Color.FromArgb( 80, Color.White ) );
+
protected StringFormat 小節番号文字フォーマット = new StringFormat() { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center };
+
protected Pen ガイド線ペン = new Pen( Color.FromArgb( 50, 50, 50 ) );
+
protected Pen 小節線ペン = new Pen( Color.White, 2.0f );
+
protected Pen 拍線ペン = new Pen( Color.Gray );
+
protected Pen レーン区分線ペン = new Pen( Color.Gray );
+
protected Pen レーン区分線太ペン = new Pen( Color.Gray, 3.0f );
+
protected Pen カレントラインペン = new Pen( Color.Red );
+
protected Font レーン名文字フォント = new Font( "MS US Gothic", 8.0f, FontStyle.Regular );
+
protected Brush レーン名文字ブラシ = new SolidBrush( Color.FromArgb( 0xff, 220, 220, 220 ) );
+
protected Brush レーン名文字影ブラシ = new SolidBrush( Color.Black );
+
protected StringFormat レーン名文字フォーマット = new StringFormat() { LineAlignment = StringAlignment.Near, Alignment = StringAlignment.Center };
+
protected Pen チップの太枠ペン = new Pen( Color.White, 2.0f );
+
protected StringFormat チップ内文字列フォーマット = new StringFormat() { LineAlignment = StringAlignment.Near, Alignment = StringAlignment.Center };
+
protected Font チップ内文字列フォント = new Font( "MS Gothic", 8f, FontStyle.Bold );
+
protected Pen 白丸白バツペン = new Pen( Color.White );
protected void 譜面に定間隔で線を描画する( Graphics g, int 小節番号, Rectangle 小節の描画領域, int 間隔grid, Pen 描画ペン )
}
}
}
+
protected void チップを描画する_通常( Graphics g, チップ種別 eチップ, int 音量, Rectangle チップ描画領域, string チップ内文字列, Color 描画色 )
{
using( var 背景ブラシ = new SolidBrush( 描画色 ) )
}
}
}
+
protected void チップを描画する_通常( Graphics g, チップ種別 eチップ, int 音量, Rectangle チップ描画領域, string チップ内文字列 )
{
this.チップを描画する_通常( g, eチップ, 音量, チップ描画領域, チップ内文字列, this.チップto色[ eチップ ] );
}
+
protected void チップを描画する_幅狭( Graphics g, チップ種別 eチップ, int 音量, Rectangle チップ描画領域, string チップ内文字列, Color 描画色 )
{
// チップの幅を半分にする。
this.チップを描画する_通常( g, eチップ, 音量, チップ描画領域, チップ内文字列, 描画色 );
}
+
protected void チップを描画する_幅狭( Graphics g, チップ種別 eチップ, int 音量, Rectangle チップ描画領域, string チップ内文字列 )
{
this.チップを描画する_幅狭( g, eチップ, 音量, チップ描画領域, チップ内文字列, this.チップto色[eチップ]);
}
+
protected void チップを描画する_幅狭白丸( Graphics g, チップ種別 eチップ, int 音量, Rectangle チップ描画領域, string チップ内文字列 )
{
// 幅狭チップを描画。
this.チップ音量に合わせてチップ描画領域を縮小する( 音量, ref チップ描画領域 );
g.DrawEllipse( this.白丸白バツペン, チップ描画領域 );
}
+
protected void チップを描画する_幅狭白狭丸( Graphics g, チップ種別 eチップ, int 音量, Rectangle チップ描画領域, string チップ内文字列 )
{
// 幅狭チップを描画。
チップ描画領域.X += w / 3 - 1; // -1 は見た目のバランス(直感)
g.DrawEllipse( this.白丸白バツペン, チップ描画領域 );
}
+
protected void チップを描画する_幅狭白バツ( Graphics g, チップ種別 eチップ, int 音量, Rectangle チップ描画領域, string チップ内文字列 )
{
// 幅狭チップを描画。
g.DrawLine( this.白丸白バツペン, new Point( チップ描画領域.Left, チップ描画領域.Top ), new Point( チップ描画領域.Right, チップ描画領域.Bottom ) );
g.DrawLine( this.白丸白バツペン, new Point( チップ描画領域.Left, チップ描画領域.Bottom ), new Point( チップ描画領域.Right, チップ描画領域.Top ) );
}
+
protected void チップを描画する_小丸( Graphics g, チップ種別 eチップ, int 音量, Rectangle チップ描画領域, string チップ内文字列 )
{
this.チップ音量に合わせてチップ描画領域を縮小する( 音量, ref チップ描画領域 );
g.DrawEllipse( 枠ペン, チップ描画領域 );
}
}
+
protected void チップ音量に合わせてチップ描画領域を縮小する( int チップ音量, ref Rectangle 描画領域 )
{
double 縮小率 = (double) チップ音量 * ( 1.0 / ( メインフォーム.最大音量 - メインフォーム.最小音量 + 1 ) );
/// </summary>
/// <remarks>
/// .dtx や box.def 等で使用されている "#<コマンド名>[:]<パラメータ>[;コメント]" 形式の文字列(対象文字列)について、
- /// 指定されたコマンドを使用する行であるかどうかを判別し、使用する行であるなら、そのパラメータ部分の文字列を引数に格納し、true を返す。
- /// 対象文字列のコマンド名が指定したコマンド名と異なる場合には、パラメータ文字列に null を格納して false を返す。
- /// コマンド名は正しくてもパラメータが存在しない場合には、空文字列("") を格納して true を返す。
+ /// 指定されたコマンドを使用する行であるかどうかを判別し、使用する行であるなら、そのパラメータ部分の文字列を返す。
+ /// 対象文字列のコマンド名が指定したコマンド名と異なる場合には null を返す。
+ /// コマンド名は正しくてもパラメータが存在しない場合には、空文字列("")を返す。
/// </remarks>
/// <param name="対象文字列">
/// 調べる対象の文字列。(例: "#TITLE: 曲名 ;コメント")
/// 調べるコマンドの名前(例:"TITLE")。#は不要、大文字小文字は区別されない。
/// </param>
/// <returns>
- /// パラメータ文字列の取得に成功したら true、異なるコマンドだったなら false。
+ /// パラメータ文字列の取得に成功したらその文字列、異なるコマンドだったなら null。
/// </returns>
public static bool コマンドのパラメータ文字列部分を返す( string 対象文字列, string コマンド名, out string パラメータ文字列 )
{
// 定数プロパティ
- public readonly Version SSTFVersion = new Version( 2, 0, 0, 0 );
+ public Version SSTFVersion { get; } = new Version( 2, 0, 0, 0 );
public const double 初期BPM = 120.0;
-
public const double 初期小節解像度 = 480.0;
-
public const double BPM初期値固定での1小節4拍の時間ms = ( 60.0 * 1000 ) / ( スコア.初期BPM / 4.0 );
-
public const double BPM初期値固定での1小節4拍の時間sec = 60.0 / ( スコア.初期BPM / 4.0 );
-
/// <summary>
/// 1ms あたりの設計ピクセル数 [dpx] 。
/// </summary>
/// → 1ms の間に、8775[dpx]÷60000[ms]=0.14625[dpx/ms]。割り切れて良かった。
/// </remarks>
public const double 基準譜面速度dpxms = 0.14625 * 2.25; // "* 2.25" は「x1.0はもう少し速くてもいいんではないか?」という感覚的な調整分。
-
/// <summary>
/// 1秒あたりの設計ピクセル数 [dpx] 。
/// </summary>
this.サウンドデバイス遅延ms = v1header.サウンドデバイス遅延ms;
}
}
- public CHeader Header = new CHeader();
+ public CHeader Header { get; protected set; } = new CHeader();
- public List<チップ> チップリスト
- {
- get;
- protected set;
- }
+ public List<チップ> チップリスト { get; protected set; }
- public List<double> 小節長倍率リスト
- {
- get;
- protected set;
- }
+ public List<double> 小節長倍率リスト { get; protected set; }
public int 最大小節番号
{
}
}
- public Dictionary<int, string> dicメモ = new Dictionary<int, string>();
+ public Dictionary<int, string> dicメモ { get; protected set; } = new Dictionary<int, string>();
// メソッド
/// チップの描画時刻[sec]。
/// 譜面の先頭(小節番号 -1 の小節の先頭)からの時刻を秒単位で表す。
/// </summary>
- public double 描画時刻sec => ( this.描画時刻ms / 1000.0 );
+ public double 描画時刻sec
+ => this.描画時刻ms / 1000.0;
/// <summary>
/// チップの発声時刻[ms]。
/// <remarks>
/// サウンドの発声遅延を考慮して、描画時刻よりも遅く設定すること。
/// </remarks>
- public double 発声時刻sec => ( this.発声時刻ms / 1000.0 );
+ public double 発声時刻sec
+ => this.発声時刻ms / 1000.0;
/// <summary>
/// チップの音量(小:1~8:大)。
/// </summary>
public int 音量
{
- get
- {
- return this._音量;
- }
- set
- {
- if( ( 1 > value ) || ( チップ.最大音量 < value ) )
- throw new ArgumentException( $"音量の値域(1~{チップ.最大音量})を超える値 '{value}' が指定されました。" );
+ get => this._音量;
- this._音量 = value;
- }
+ set => this._音量 = ( ( 1 > value ) || ( チップ.最大音量 < value ) ) ?
+ throw new ArgumentException( $"音量の値域(1~{チップ.最大音量})を超える値 '{value}' が指定されました。" ) :
+ value;
}
/// <summary>
/// それ以外の場合は無効。
/// </summary>
public double BPM { get; set; } = 120.0;
-
//----------------
#endregion
#region " プロパティ(2) 演奏用(増減したら CopyFrom() を修正のこと)"
//----------------
public bool 可視 { get; set; } = true;
-
public bool 不可視
{
- get { return !this.可視; }
- set { this.可視 = !value; }
+ get => !this.可視;
+ set => this.可視 = !value;
}
public bool 可視の初期値
}
public bool ヒット済みである { get; set; } = false;
-
public bool ヒットされていない
{
- get { return !this.ヒット済みである; }
- set { this.ヒット済みである = !value; }
+ get => !this.ヒット済みである;
+ set => this.ヒット済みである = !value;
}
public bool 発声済みである { get; set; } = false;
-
public bool 発声されていない
{
- get { return !this.発声済みである; }
- set { this.発声済みである = !value; }
+ get => !this.発声済みである;
+ set => this.発声済みである = !value;
}
//----------------
#endregion
public bool ドラッグ操作により選択中である { get; set; } = false;
public bool 選択が確定している { get; set; } = false;
-
public bool 選択が確定していない
{
- get { return !this.選択が確定している; }
- set { this.選択が確定している = !value; }
+ get => !this.選択が確定している;
+ set => this.選択が確定している = !value;
}
public bool 移動済みである { get; set; } = true;
-
public bool 移動されていない
{
- get { return !this.移動済みである; }
- set { this.移動済みである = !value; }
+ get => !this.移動済みである;
+ set => this.移動済みである = !value;
}
public string チップ内文字列 { get; set; } = null;
{
return チップ.チップ種別.排他発声グループID();
}
-
public static bool 直前のチップを消音する( this チップ種別 今回のチップの種別, チップ種別 直前のチップの種別 )
{
int 今回のチップのGID = 今回のチップの種別.排他発声グループID();
finally
{
// サービスの受付を終了する。
- serviceHost.Close( new TimeSpan( 0, 0, 2 ) ); // 最大2sec待つ
+ serviceHost.Close( new TimeSpan( 0, 0, 2 ) ); // 最大2sec待つ
}
//----------------
#endregion
{
// グローバルリソース (static)
- public static フォルダ フォルダ { get; protected set; }
-
- public static SoundDevice サウンドデバイス { get; protected set; }
-
- public static Random 乱数 { get; protected set; }
-
- public static ユーザ管理 ユーザ管理 { get; protected set; }
-
- public static 曲ツリー管理 曲ツリー管理 { get; protected set; }
-
- public static スコア 演奏スコア { get; set; }
-
- public static Config Config { get; protected set; }
-
- public static 入力管理 入力管理 { get; protected set; }
+ public static フォルダ フォルダ
+ {
+ get;
+ protected set;
+ }
+ public static SoundDevice サウンドデバイス
+ {
+ get;
+ protected set;
+ }
+ public static Random 乱数
+ {
+ get;
+ protected set;
+ }
+ public static ユーザ管理 ユーザ管理
+ {
+ get;
+ protected set;
+ }
+ public static 曲ツリー管理 曲ツリー管理
+ {
+ get;
+ protected set;
+ }
+ public static スコア 演奏スコア
+ {
+ get;
+ set;
+ }
+ public static Config Config
+ {
+ get;
+ protected set;
+ }
+ public static 入力管理 入力管理
+ {
+ get;
+ protected set;
+ }
public static bool ビュアーモードである
{
get;
set;
} = false;
-
public static bool ビュアーモードではない
{
- get { return !StrokeStyleT.ビュアーモードである; }
- set { StrokeStyleT.ビュアーモードである = !value; }
- }
+ get
+ => !( StrokeStyleT.ビュアーモードである );
- public static ViewerMessage 最後に取得したビュアーメッセージ { get; protected set; }
+ set
+ => StrokeStyleT.ビュアーモードである = !( value );
+ }
+ public static ViewerMessage 最後に取得したビュアーメッセージ
+ {
+ get;
+ protected set;
+ }
public static void すべての入力デバイスをポーリングする( bool 入力履歴を記録する = true )
{
StrokeStyleT.入力管理.すべての入力デバイスをポーリングする( 入力履歴を記録する );
}
-
- /// <summary>
- /// static コンストラクタ。
- /// </summary>
static StrokeStyleT()
{
// フォルダ変数を真っ先に登録する。(ほかのメンバのコンストラクタでフォルダ変数を利用できるようにするため。)
// その他。
StrokeStyleT.乱数 = new Random( DateTime.Now.Millisecond );
}
-
- /// <summary>
- /// コンストラクタ。
- /// </summary>
- /// <param name="args">
- /// コマンドライン引数。
- /// </param>
public StrokeStyleT( string[] args )
{
#region " ビュアーモードかどうかを確認する。"
this._MainForm.FormClosing += ( sender, e ) => { this._終了する(); };
}
- /// <summary>
- /// アプリのメインエントリ。
- /// </summary>
public void Run()
{
Debug.Assert( null != this._MainForm );
/// </param>
public void ViewerPlay( string path, int startPart = 0, bool drumsSound = true )
{
- if( ビュアーモードではない )
+ if( StrokeStyleT.ビュアーモードではない )
return;
this._ビュアーメッセージキュー.Enqueue( new ViewerMessage() {
private ApplicationState _State;
private enum ApplicationState { 起動, 初期化, 進行描画, 終了 }
- /// <summary>
- /// アプリのメインフォーム。
- /// </summary>
private SST.RenderForm _MainForm = null;
/// <summary>
private ConcurrentQueue<ViewerMessage> _ビュアーメッセージキュー = new ConcurrentQueue<ViewerMessage>();
private FDK.メディア.デバイスリソース _デバイスリソース = null;
-
-
private ステージ.ステージ _最初のダミーステージ = null;
-
private 起動ステージ _起動ステージ = null;
-
private タイトルステージ _タイトルステージ = null;
-
private ログインステージ _ログインステージ = null;
-
private 選曲ステージ _選曲ステージ = null;
-
private 曲読込ステージ _曲読込ステージ = null;
-
private 演奏ステージ _演奏ステージ = null;
-
private 結果ステージ _結果ステージ = null;
-
private ステージ.ステージ _現在のステージ = null;
-
-
private readonly string _ConfigXmlファイルパス = @"$(AppData)\Config.xml";
-
private readonly string _UsersXmlファイルパス = @"$(AppData)\Users.xml";
private void _起動する()
Log.EndInfo( $"{Utilities.現在のメソッド名}" );
}
-
private void _初期化する()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
this._結果ステージ = new 結果ステージ();
// 外部依存アクションを接続する。
- this._曲読込ステージ.読込曲のファイルパスを取得する = () => ( ( StrokeStyleT.曲ツリー管理.現在選択されているノード as MusicNode )?.sstfファイルパス );
- this._結果ステージ.演奏ステージインスタンスを取得する = () => ( this._演奏ステージ );
- this._結果ステージ.BGMを停止する = () => { this._演奏ステージ.BGMを停止する(); };
+ this._曲読込ステージ.読込曲のファイルパスを取得する = ()
+ => ( ( StrokeStyleT.曲ツリー管理.現在選択されているノード as MusicNode )?.sstfファイルパス );
+ this._結果ステージ.演奏ステージインスタンスを取得する = ()
+ => ( this._演奏ステージ );
+ this._結果ステージ.BGMを停止する = ()
+ => { this._演奏ステージ.BGMを停止する(); };
//----------------
#endregion
#region " ユーザを初期化する。"
Log.EndInfo( $"{Utilities.現在のメソッド名}" );
}
-
private void _終了する()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
Log.EndInfo( $"{Utilities.現在のメソッド名}" );
}
-
private void _進行描画する()
{
// 現在のステージの進行描画とスワップチェーンを表示する。
}
//----------------
#endregion
+
#region " 描画の前処理を行う。"
//----------------
{
}
//----------------
#endregion
+
#region " 現在のステージを進行描画する。"
//----------------
this._現在のステージ?.進行描画する( this._デバイスリソース );
//----------------
#endregion
+
#region " スワップチェーンを表示する。"
//----------------
if( StrokeStyleT.Config.垂直帰線待ちを行う )
}
}
}
-
private void _デバイス依存リソースを解放する()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
Log.EndInfo( $"{Utilities.現在のメソッド名}" );
}
-
private void _デバイス依存リソースを再構築する()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
Log.EndInfo( $"{Utilities.現在のメソッド名}" );
}
-
private void _フォームサイズが変更された( object sender, EventArgs e )
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
Log.EndInfo( $"{Utilities.現在のメソッド名}" );
}
-
private void _全画面モードとウィンドウモードを切り替える()
{
Log.BeginInfo( $"{Utilities.現在のメソッド名}" );
Log.EndInfo( $"{Utilities.現在のメソッド名}" );
}
-
private void _ログインする( string ユーザ名 )
{
StrokeStyleT.ユーザ管理.ユーザを選択する( ユーザ名 );
Log.Info( $"ユーザが選択されました。[{StrokeStyleT.ユーザ管理.現在選択されているユーザ.名前}]" );
}
-
+
/// <summary>
/// ビュアーメッセージキューからメッセージを取り出す。
/// </summary>
<StartupObject />
</PropertyGroup>
<ItemGroup>
- <Reference Include="CSCore, Version=1.1.5992.18249, Culture=neutral, PublicKeyToken=5a08f2b6f4415dea, processorArchitecture=MSIL">
- <HintPath>..\packages\CSCore.1.1.0\lib\net35-client\CSCore.dll</HintPath>
- <Private>True</Private>
+ <Reference Include="CSCore, Version=1.1.6245.30570, Culture=neutral, PublicKeyToken=5a08f2b6f4415dea, processorArchitecture=MSIL">
+ <HintPath>..\packages\CSCore.1.2.0\lib\net35-client\CSCore.dll</HintPath>
</Reference>
<Reference Include="SharpDX, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<HintPath>..\packages\SharpDX.3.1.1\lib\net45\SharpDX.dll</HintPath>
演奏開始,
演奏停止,
}
+ public Type 種別
+ {
+ get;
+ set;
+ } = Type.指示なし;
- public Type 種別 { get; set; } = Type.指示なし;
-
- public string 演奏対象曲のファイルパス { get; set; } = null;
-
- public int 演奏を開始する小節番号 { get; set; } = 0;
-
- public bool ドラムチップのヒット時に発声する { get; set; } = true;
+ public string 演奏対象曲のファイルパス
+ {
+ get;
+ set;
+ } = null;
+ public int 演奏を開始する小節番号
+ {
+ get;
+ set;
+ } = 0;
+ public bool ドラムチップのヒット時に発声する
+ {
+ get;
+ set;
+ } = true;
public override string ToString()
{
$"種別={this.種別}" +
$", 演奏開始小節番号={this.演奏を開始する小節番号}" +
$", ドラムチップ発声={this.ドラムチップのヒット時に発声する}" +
- $", 曲ファイルパス=[{FDK.フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( this.演奏対象曲のファイルパス )}]";
+ $", 曲ファイルパス=[{フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( this.演奏対象曲のファイルパス )}]";
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="CSCore" version="1.1.0" targetFramework="net452" />
+ <package id="CSCore" version="1.2.0" targetFramework="net452" />
<package id="SharpDX" version="3.1.1" targetFramework="net452" />
<package id="SharpDX.D3DCompiler" version="3.1.1" targetFramework="net452" />
<package id="SharpDX.Desktop" version="3.1.1" targetFramework="net452" />
class コンソールフォント : FDK.Activity
{
public const float 改行幅dpx = 32f;
-
public const float 文字幅dpx = 14f; // 画像では文字幅は 16px だが、表示するときはちょっと狭くする。
public コンソールフォント()
height: 32 );
}
}
-
public void 描画する( FDK.メディア.デバイスリソース dr, float Xdpx, float Ydpx, string 描画する文字列, float 不透明度0to1 = 1f )
{
if( null == this._フォント白32x16 )
}
private FDK.メディア.画像 _フォント白32x16 = null;
-
private SharpDX.RectangleF[] _文字の矩形領域 = null; // [96]
-
private readonly string _表記可能文字 = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ";
}
}
確定,
キャンセル,
}
-
- public フェーズ 現在のフェーズ { get; protected set; } = フェーズ.初期状態;
+ public フェーズ 現在のフェーズ
+ {
+ get;
+ protected set;
+ } = フェーズ.初期状態;
public タイトルステージ()
{
this.子リスト.Add( this._背景画像 = new 画像( @"$(Static)\images\タイトル画面.jpg" ) );
}
-
protected override void On活性化( デバイスリソース dr )
{
Log.Info( "タイトルステージを開始します。" );
this.現在のフェーズ = フェーズ.表示中;
}
-
protected override void On非活性化( デバイスリソース dr )
{
Log.Info( "タイトルステージを終了します。" );
}
-
public override void 進行描画する( デバイスリソース dr )
{
if( this.現在のフェーズ != フェーズ.表示中 )
public float ハイハットの開度
{
get
- {
- return this._ハイハットの開度;
- }
- set
- {
- if( 1.0f < value )
- throw new OutOfMemoryException( "1.0 を超える値は設定できません。" );
- else if( 0.0f > value )
- throw new OutOfMemoryException( "負数は設定できません。" );
- else
- this._ハイハットの開度 = value;
- }
+ => this._ハイハットの開度;
+
+ set
+ => this._ハイハットの開度 =
+ ( 1.0f < value ) ? throw new OutOfMemoryException( "1.0 を超える値は設定できません。" ) :
+ ( 0.0f > value ) ? throw new OutOfMemoryException( "負数は設定できません。" ) :
+ value;
}
public ドラムセット()
this.子リスト.Add( this._RCymbalStand = new 画像( @"$(Static)\images\DrumKit RCymbal Stand.png" ) );
this.子リスト.Add( this._RCymbalTop = new 画像( @"$(Static)\images\DrumKit RCymbal Top.png" ) );
}
-
public void 進行描画する( デバイスリソース dr )
{
this._Bass.描画する( dr, 881f, 891f );
}
private float _ハイハットの開度 = 1.0f;
-
- private FDK.メディア.画像 _HiHatTop;
-
- private FDK.メディア.画像 _HiHatBottom;
-
- private FDK.メディア.画像 _Snare;
-
- private FDK.メディア.画像 _Bass;
-
- private FDK.メディア.画像 _HiTom;
-
- private FDK.メディア.画像 _LowTom;
-
- private FDK.メディア.画像 _FloorTom;
-
- private FDK.メディア.画像 _LCymbal;
-
- private FDK.メディア.画像 _LCymbalStand;
-
- private FDK.メディア.画像 _LCymbalTop;
-
- private FDK.メディア.画像 _RCymbal;
-
- private FDK.メディア.画像 _RCymbalStand;
-
- private FDK.メディア.画像 _RCymbalTop;
+ private 画像 _HiHatTop;
+ private 画像 _HiHatBottom;
+ private 画像 _Snare;
+ private 画像 _Bass;
+ private 画像 _HiTom;
+ private 画像 _LowTom;
+ private 画像 _FloorTom;
+ private 画像 _LCymbal;
+ private 画像 _LCymbalStand;
+ private 画像 _LCymbalTop;
+ private 画像 _RCymbal;
+ private 画像 _RCymbalStand;
+ private 画像 _RCymbalTop;
}
}
/// </remarks>
class フェードアウト : FDK.Activity
{
- public bool 完了した => this._透明度カウンタ.終了値に達した;
-
- public bool 完了していない => !( this.完了した );
+ public bool 完了した
+ {
+ get
+ => this._透明度カウンタ.終了値に達した;
+ }
+ public bool 完了していない
+ {
+ get
+ => !( this.完了した );
+ }
public フェードアウト( float 総フェード時間sec )
{
this._総フェード時間sec = 総フェード時間sec;
}
-
public void 開始する()
{
int 最初の値 = 0;
this._透明度カウンタ.開始する( 最初の値, 最後の値, 値をひとつ増加させるのにかける時間ms );
}
-
public void 進行描画する( デバイスリソース dr, FDK.メディア.画像 フェードアウトに使うタイル画像 )
{
int タイル枚数X = (int) ( dr.設計画面サイズdpx.Width / フェードアウトに使うタイル画像.サイズdpx.Width ) + 1; // 剰余は切り捨てられるので +1 。
}
private float _総フェード時間sec = 1.0f;
-
private FDK.カウンタ.単純増加後不変カウンタ _透明度カウンタ = new FDK.カウンタ.単純増加後不変カウンタ();
}
}
/// </remarks>
class フェードイン : FDK.Activity
{
- public bool 完了した => this._透明度カウンタ.終了値に達した;
-
- public bool 完了していない => !( this.完了した );
+ public bool 完了した
+ {
+ get
+ => this._透明度カウンタ.終了値に達した;
+ }
+ public bool 完了していない
+ {
+ get
+ => !( this.完了した );
+ }
public フェードイン( float 総フェード時間sec )
{
this._総フェード時間sec = 総フェード時間sec;
}
-
public void 開始する()
{
int 最初の値 = 0;
this._透明度カウンタ.開始する( 最初の値, 最後の値, 値をひとつ増加させるのにかける時間ms );
}
-
public void 進行描画する( デバイスリソース dr, FDK.メディア.画像 フェードインに使うタイル画像 )
{
int タイル枚数X = (int) ( dr.設計画面サイズdpx.Width / フェードインに使うタイル画像.サイズdpx.Width ) + 1; // 剰余は切り捨てられるので +1 。
}
private float _総フェード時間sec = 1.0f;
-
private FDK.カウンタ.単純増加後不変カウンタ _透明度カウンタ = new FDK.カウンタ.単純増加後不変カウンタ();
}
}
確定,
キャンセル,
}
-
- public フェーズ 現在のフェーズ { get; protected set; } = フェーズ.初期状態;
+ public フェーズ 現在のフェーズ
+ {
+ get;
+ protected set;
+ } = フェーズ.初期状態;
public ログインステージ()
{
this.子リスト.Add( this._ログイン画面見出し = new 文字列画像( "ユーザ選択" ) );
}
-
protected override void On活性化( デバイスリソース dr )
{
Log.Info( "ログインステージを開始します。" );
StrokeStyleT.ユーザ管理.最初のユーザを選択する();
}
-
protected override void On非活性化( デバイスリソース dr )
{
Log.Info( "ログインステージを終了します。" );
foreach( var panel in this._ユーザパネル )
+ {
this.子リスト.Remove( panel ); // 子リストから削除
+ }
this._ユーザパネル.Clear();
}
-
public override void 進行描画する( デバイスリソース dr )
{
if( this.現在のフェーズ != フェーズ.表示中 )
}
private List<文字列画像> _ユーザパネル = null;
-
private 文字列画像 _ログイン画面見出し = null;
}
}
{
public Func<string> 読込曲のファイルパスを取得する = null;
- public enum フェーズ { 初期状態, 読込中, 終了 }
-
- public フェーズ 現在のフェーズ { get; protected set; } = フェーズ.初期状態;
+ public enum フェーズ
+ {
+ 初期状態,
+ 読込中,
+ 終了
+ }
+ public フェーズ 現在のフェーズ
+ {
+ get;
+ protected set;
+ } = フェーズ.初期状態;
+ public 曲読込ステージ()
+ {
+ }
protected override void On活性化( デバイスリソース dr )
{
Log.Info( "曲読込ステージを開始します。" + ( StrokeStyleT.ビュアーモードである ? "(ViewerMode)" : "" ) );
this.現在のフェーズ = フェーズ.読込中;
}
-
protected override void On非活性化( デバイスリソース dr )
{
Log.Info( "曲読込ステージを終了します。" );
}
-
public override void 進行描画する( デバイスリソース dr )
{
switch( this.現在のフェーズ )
this.子リスト.Add( this._単位付き数値 = new コンボジャンプ() );
}
-
protected override void On活性化( デバイスリソース dr )
{
this._現在のモード = EMode.非表示中;
this._コンボが切れたときのCOMBO値 = 0;
this._コンボが切れた時刻 = QPCTimer.未使用;
}
-
protected override void On非活性化( デバイスリソース dr )
{
}
-
public void 進行描画する( デバイスリソース dr )
{
lock( this._スレッド間同期 )
同一数値,
ミス
}
-
protected enum EMode
{
非表示中,
}
private const float _ドラムコンボの文字間隔 = 2f;
-
private const int _表示可能な最小コンボ数 = 11;
-
private EMode _現在のモード;
-
private int _COMBO値 = 0;
-
private int _前回のCOMBO値 = 0;
-
private long _コンボが切れた時刻 = QPCTimer.未使用;
-
private int _コンボが切れたときのCOMBO値 = 0;
-
private コンボジャンプ _単位付き数値 = null;
-
private readonly object _スレッド間同期 = new object();
}
}
for( int i = 0; i < 180; i++ )
this._ジャンプ差分値[ i ] = (int) ( -15.0 * Math.Sin( MathUtil.DegreesToRadians( i ) ) );
}
-
protected override void On活性化( デバイスリソース dr )
{
this._表示する数値 = 0;
this._ジャンプインデックス値 = 99999;
this._ジャンプインデックス進行 = new 定間隔進行();
}
-
protected override void On非活性化( デバイスリソース dr )
{
}
-
public void 進行描画する( デバイスリソース dr, float 中央Xdpx, float 下辺Ydpx, float 文字間隔dpx )
{
lock( this._スレッド間同期 )
}
//-----------------
#endregion
+
#region " 数値が増加していればジャンプを開始する。"
//-----------------
if( this._表示する数値 > this._現在の数値 )
}
//-----------------
#endregion
+
#region " 単位文字の矩形(COMBO矩形) と 数字と単位を合わせた画像の全幅dpx を算出。"
//-----------------
var COMBO矩形 = this._文字矩形dpx[ 10 ];
float 数字と単位を合わせた画像の全幅dpx = this._文字矩形dpx[ 10 ].Width;
+
for( int i = 0; i < 桁数; i++ )
数字と単位を合わせた画像の全幅dpx += this._文字矩形dpx[ 位の数[ i ] ].Width;
//-----------------
#endregion
+
#region " 位の数[] を、COMBO文字→ 1の位 → 10の位 … の順に、右から左へ向かって順番に表示する。"
//-----------------
float x = 中央Xdpx + ( 数字と単位を合わせた画像の全幅dpx / 2f ); // 右端X
}
private int _表示する数値 = 0;
-
private bool _表示する = false;
-
private int _現在の数値 = -1;
-
- private FDK.メディア.画像 _数値と単位の画像 = null;
-
+ private 画像 _数値と単位の画像 = null;
private readonly List<RectangleF> _文字矩形dpx = new List<RectangleF>() {
#region " [0]~[9]: '0'~'9', [10]:'COMBO' "
//----------------
//----------------
#endregion
};
-
private const int _桁ごとのジャンプの遅れ = 50; // 1桁につき 50 インデックス遅れる
-
private const float _不透明度0to1 = 0.7f;
-
private int _ジャンプインデックス値 = 9999;
-
private int[] _ジャンプ差分値 = new int[ 180 ];
-
private 定間隔進行 _ジャンプインデックス進行 = null;
-
private readonly object _スレッド間同期 = new object();
}
}
{
class スクロール譜面 : Activity
{
- public Action<チップ種別, ヒット判定種別> ヒット判定文字列を開始する = null;
-
- public Action コンボをリセットする = null;
-
- public Action コンボを1つ加算する = null;
-
- public Action<ヒット判定種別> ヒット判定数を1つ加算する = null;
-
- public Func<double> 背景動画の長さsecを取得する = null;
-
- public Action<double> 背景動画の再生を開始する = null;
-
- public Action<チップ> チップをヒットする = null;
-
- public Action ステージをクリアする = null;
-
- public Action ステージをキャンセルする = null;
-
- public Action 譜面スクロール速度の倍率を増やす = null;
-
- public Action 譜面スクロール速度の倍率を減らす = null;
-
- public Func<double> リアルタイム演奏時刻secを取得する = null;
-
- public Func<double> 譜面スクロール速度の倍率を取得する = null;
-
- public Action FPSをカウントアップする = null;
-
-
public スクロール譜面()
{
this.子リスト.Add( this._チップ画像 = new 画像( @"$(Static)\images\Chips.png" ) );
this.子リスト.Add( this._コンソールフォント = new コンソールフォント() );
}
-
protected override void On活性化( デバイスリソース dr )
{
this._描画開始チップ番号 = -1;
-
this._チップ画像の矩形リスト = new 矩形リスト( @"$(Static)\images\Chips Rectangle List.xml" ); // ここで読み込む → 変更があったら反映される(デバッグ用途)。
-
this._活性化した直後である = true;
}
-
protected override void On非活性化( デバイスリソース dr )
{
if( null != this._高頻度進行タスクへのイベント )
//this._高頻度進行タスクへのイベント.無効になるまでブロックする();
}
}
-
protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
{
this._画面の高さdpx = dr.設計画面サイズdpx.Height;
}
-
-
public double 演奏開始小節番号を設定しその時刻secを返す( int 演奏開始小節番号 )
{
lock( this._スレッド間同期 )
return 0.0;
double 演奏開始時刻sec = 0.0;
+
for( int i = 0; i < スコア.チップリスト.Count; i++ )
{
if( スコア.チップリスト[ i ].小節番号 < 演奏開始小節番号 )
演奏開始時刻sec = スコア.チップリスト[ i ].発声時刻sec;
- #region " 演奏開始時刻sec を少し早めに設定する。"
- //----------------
+ // 演奏開始時刻sec を少し早めに設定する。
演奏開始時刻sec -= 0.5;
-
for( int j = i; j >= 0; j-- )
{
if( スコア.チップリスト[ j ].ヒット済みである )
スコア.チップリスト[ j ].ヒット前の状態にする();
}
- //----------------
- #endregion
break;
}
return 演奏開始時刻sec;
}
}
-
public void 再生中の時刻なら動画とBGMを再生開始する( double 時刻sec )
{
Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
- try
+ lock( this._スレッド間同期 )
{
- lock( this._スレッド間同期 )
- {
- Log.Info( $"現在の時刻sec = {時刻sec}" );
+ Log.Info( $"現在の時刻sec = {時刻sec}" );
- var スコア = StrokeStyleT.演奏スコア;
+ var スコア = StrokeStyleT.演奏スコア;
- if( null == スコア )
- {
- Log.Info( "演奏スコアが未設定です。" );
- return;
- }
+ if( null == スコア )
+ {
+ Log.Info( "演奏スコアが未設定です。" );
+ return;
+ }
- foreach( var チップ in スコア.チップリスト )
+ foreach( var チップ in スコア.チップリスト )
+ {
+ if( チップ.チップ種別 == チップ種別.背景動画 )
{
- if( チップ.チップ種別 == チップ種別.背景動画 )
- {
- double 背景動画の長さsec = this.背景動画の長さsecを取得する();
+ double 背景動画の長さsec = this.背景動画の長さsecを取得する();
- Log.Info( $"背景動画の発生時刻sec:{チップ.発声時刻sec}" );
- Log.Info( $"背景動画の長さsec:{背景動画の長さsec}" );
+ Log.Info( $"背景動画の発生時刻sec:{チップ.発声時刻sec}" );
+ Log.Info( $"背景動画の長さsec:{背景動画の長さsec}" );
- if( ( チップ.発声時刻sec <= 時刻sec ) &&
- ( 時刻sec < ( チップ.発声時刻sec + 背景動画の長さsec ) ) )
- {
- double 再生開始時刻sec = 時刻sec - チップ.発声時刻sec;
- this.背景動画の再生を開始する( 再生開始時刻sec );
+ if( ( チップ.発声時刻sec <= 時刻sec ) &&
+ ( 時刻sec < ( チップ.発声時刻sec + 背景動画の長さsec ) ) )
+ {
+ double 再生開始時刻sec = 時刻sec - チップ.発声時刻sec;
+ this.背景動画の再生を開始する( 再生開始時刻sec );
- Log.Info( $"背景動画の再生を開始しました。(再生開始時刻sec={再生開始時刻sec})" );
- }
- else
- {
- Log.Info( $"背景動画は再生中ではないので何もしません。" );
- }
- return;
+ Log.Info( $"背景動画の再生を開始しました。(再生開始時刻sec={再生開始時刻sec})" );
+ }
+ else
+ {
+ Log.Info( $"背景動画は再生中ではないので何もしません。" );
}
+ return;
}
-
- Log.Info( $"背景動画は存在しません。" );
}
+
+ Log.Info( $"背景動画は存在しません。" );
}
- finally
- {
- Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
- }
- }
+ Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
+ }
public void 演奏を停止する()
{
lock( this._スレッド間同期 )
this._描画開始チップ番号 = -1;
}
}
-
public void 小節線拍線を進行描画する( デバイスリソース dr, double 現在の演奏時刻sec )
{
lock( this._スレッド間同期 )
現在の演奏時刻sec );
}
}
-
public void チップを進行描画する( デバイスリソース dr, double 現在の演奏時刻sec )
{
lock( this._スレッド間同期 )
}
}
-
- private bool _活性化した直後である = false;
+ internal Action<チップ種別, ヒット判定種別> ヒット判定文字列を開始する = null;
+ internal Action コンボをリセットする = null;
+ internal Action コンボを1つ加算する = null;
+ internal Action<ヒット判定種別> ヒット判定数を1つ加算する = null;
+ internal Func<double> 背景動画の長さsecを取得する = null;
+ internal Action<double> 背景動画の再生を開始する = null;
+ internal Action<チップ> チップをヒットする = null;
+ internal Action ステージをクリアする = null;
+ internal Action ステージをキャンセルする = null;
+ internal Action 譜面スクロール速度の倍率を増やす = null;
+ internal Action 譜面スクロール速度の倍率を減らす = null;
+ internal Func<double> リアルタイム演奏時刻secを取得する = null;
+ internal Func<double> 譜面スクロール速度の倍率を取得する = null;
+ internal Action FPSをカウントアップする = null;
/// <summary>
/// 演奏スコア.チップリスト[] のうち、描画を始めるチップのインデックス番号を示す。
/// 未演奏時・演奏終了時は -1 。
/// </summary>
private int _描画開始チップ番号 = -1;
-
+ private bool _活性化した直後である = false;
private 画像 _チップ画像 = null;
-
private 矩形リスト _チップ画像の矩形リスト = null;
-
private 単純増加後反復カウンタ _チップアニメ = new 単純増加後反復カウンタ();
-
private コンソールフォント _コンソールフォント = null;
-
private float _画面の高さdpx = 0f; // 進行スレッドからはデバイスリソースを参照しないので、ここにキャッシュしておく。
-
private TriStateEvent _高頻度進行タスクへのイベント = null;
-
private readonly object _スレッド間同期 = new object();
// 描画関連
}
}
}
-
private void _小節線拍線を1つ描画する( デバイスリソース dr, チップ chip, float Ydpx )
{
lock( this._スレッド間同期 )
switch( chip.チップ種別 )
{
+ #region " 小節線 "
+ //----------------
case チップ種別.小節線:
- #region " 小節線を1本表示する。"
- //----------------
{
float 左位置dpx = 座標.レーンフレーム左端のX座標dpx + レーン種別.LeftCrash.Toレーンフレーム左端からの相対X位置dpx() - 1f;
float 上位置dpx = Ydpx - 1f;
- var 画像範囲orNull = this._チップ画像の矩形リスト[ nameof( チップ種別.小節線 ) ];
- if( null != 画像範囲orNull )
+ if( this._チップ画像の矩形リスト[ nameof( チップ種別.小節線 ) ] is RectangleF 画像範囲 )
{
- var 画像範囲 = (SharpDX.RectangleF) 画像範囲orNull;
this._チップ画像.描画する( dr, 左位置dpx, 上位置dpx, 転送元矩形dpx: 画像範囲 );
this._チップ画像.描画する( dr, 左位置dpx + 画像範囲.Width, 上位置dpx, 転送元矩形dpx: 画像範囲 );
}
}
- //----------------
- #endregion
break;
+ //----------------
+ #endregion
+ #region " 拍線 "
+ //----------------
case チップ種別.拍線:
- #region " 拍線を1本表示する。"
- //----------------
{
float 左位置dpx = 座標.レーンフレーム左端のX座標dpx + レーン種別.LeftCrash.Toレーンフレーム左端からの相対X位置dpx() - 1f;
float 上位置dpx = Ydpx;
- var 画像範囲orNull = this._チップ画像の矩形リスト[ nameof( チップ種別.拍線 ) ];
- if( null != 画像範囲orNull )
+ if( this._チップ画像の矩形リスト[ nameof( チップ種別.拍線 ) ] is RectangleF 画像範囲 )
{
- var 画像範囲 = (RectangleF) 画像範囲orNull;
this._チップ画像.描画する( dr, 左位置dpx: 左位置dpx, 上位置dpx: 上位置dpx, 転送元矩形dpx: 画像範囲 );
this._チップ画像.描画する( dr, 左位置dpx: 左位置dpx + 画像範囲.Width, 上位置dpx: 上位置dpx, 転送元矩形dpx: 画像範囲 );
}
}
- //----------------
- #endregion
break;
+ //----------------
+ #endregion
}
}
}
-
private void _チップを1つ描画する( デバイスリソース dr, チップ chip, float Ydpx )
{
lock( this._スレッド間同期 )
}
}
}
-
private void _アニメチップを1つ描画する( デバイスリソース dr, レーン種別 elane, RectangleF? 画像範囲orNull, float Ydpx, float 音量0to1 )
{
lock( this._スレッド間同期 )
{
if( null == 画像範囲orNull )
return;
-
var 画像範囲 = (RectangleF) 画像範囲orNull;
float チップ1枚の高さdpx = 18f;
this._チップ画像.描画する( dr, 左位置dpx, 上位置dpx, 転送元矩形dpx: 画像範囲, Y方向拡大率: 音量0to1 );
}
}
-
private void _単画チップを1つ描画する( デバイスリソース dr, レーン種別 eLane, RectangleF? 元矩形dpx, float 上位置dpx, float 音量0to1 )
{
lock( this._スレッド間同期 )
{
if( null == 元矩形dpx )
return;
-
var 画像範囲dpx = (RectangleF) 元矩形dpx;
this._チップ画像.描画する(
this.ステージをキャンセルする();
//----------------
#endregion
+
#region " カーソルの上下 → 譜面スクロールの増減 "
//----------------
if( StrokeStyleT.入力管理.キーボード入力.キーが押された( Key.Up ) )
Log.Info( $"{FDK.Utilities.現在のメソッド名} <-- 終了" );
}
-
private void _チップのヒット判定と不可視判定を行う( チップ chip, double 判定バーとの時間sec, double 判定バーとの距離dpx )
{
var オプション = StrokeStyleT.ユーザ管理.現在選択されているユーザ.オプション;
//----------------
#endregion
}
-
private void _チップのヒット処理を行う( チップ chip, ヒット判定種別 hitType )
{
lock( this._スレッド間同期 )
public ドラムサウンド()
{
}
-
protected override void On活性化( デバイスリソース dr )
{
this._KitXmlを読み込む();
}
-
protected override void On非活性化( デバイスリソース dr )
{
this._すべてのコンテキストを初期化する();
}
-
public void 発声する( チップ種別 chipType, float 音量0to1 )
{
lock( this._スレッド間同期 )
{
if( false == this._チップtoコンテキスト.ContainsKey( chipType ) )
return; // コンテキスト未登録のチップなら何もしない。
+
var context = this._チップtoコンテキスト[ chipType ];
// 現在発声中のサウンドを全部止めるチップ種別の場合は止める。
// 集めた Sounds[] をすべて停止する。
foreach( var sounds in 停止するサウンド群 )
+ {
foreach( var sound in sounds )
+ {
sound.Stop();
+ }
+ }
}
// 再生する。
// サウンドローテーション。
context.次に再生するSound番号++;
+
if( context.次に再生するSound番号 >= ドラムサウンド._多重度 )
context.次に再生するSound番号 = 0;
}
}
private const int _多重度 = 2;
-
private class Cコンテキスト : IDisposable
{
public Sound[] Sounds = new Sound[ ドラムサウンド._多重度 ];
}
}
};
-
private Dictionary<チップ種別, Cコンテキスト> _チップtoコンテキスト = null;
-
private readonly string _KitXmlファイルパス = @"$(Static)\sounds\Kit.xml";
-
private readonly object _スレッド間同期 = new object();
private void _すべてのコンテキストを初期化する()
this._チップtoコンテキスト = new Dictionary<チップ種別, Cコンテキスト>();
}
}
-
private void _KitXmlを読み込む()
{
lock( this._スレッド間同期 )
{
this._すべてのコンテキストを初期化する();
- string xmlPath = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this._KitXmlファイルパス );
+ string xmlPath = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this._KitXmlファイルパス );
if( false == File.Exists( xmlPath ) )
{
// 多重度分のサウンドを生成する。
for( int i = 0; i < context.Sounds.Length; i++ )
- context.Sounds[ i ] = StrokeStyleT.サウンドデバイス.CreateSound( サウンドファイルパス );
+ context.Sounds[ i ] = StrokeStyleT.サウンドデバイス.サウンドを生成する( サウンドファイルパス );
// コンテキストを辞書に追加する。
this._チップtoコンテキスト.Add( chipType, context );
}
else
{
- Log.WARNING( $"サウンドファイル {FDK.フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( サウンドファイルパス )} が存在しません。[{this._KitXmlファイルパス}]" );
+ Log.WARNING( $"サウンドファイル {フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( サウンドファイルパス )} が存在しません。[{this._KitXmlファイルパス}]" );
}
}
else
foreach( var kvp in this._文字列画像 )
this.子リスト.Add( kvp.Value );
}
-
public void 表示開始( 表示レーン種別 表示レーン種別, ヒット判定種別 ヒット判定種別 )
{
lock( this._スレッド間同期 )
// レーンに対応する進行描画コンテキストを更新し、表示を開始するためのパラメータを設定する。
if( this._ヒット判定レーンto進行描画コンテキスト.ContainsKey( 表示レーン種別 ) ) // すでにあったら、
this._ヒット判定レーンto進行描画コンテキスト.Remove( 表示レーン種別 ); // 削除してから、
- this._ヒット判定レーンto進行描画コンテキスト.Add( // 追加。
+
+ this._ヒット判定レーンto進行描画コンテキスト.Add( // 追加。
表示レーン種別,
new 進行描画コンテキスト() {
ヒット判定種別 = ヒット判定種別,
} );
}
}
-
public void 表示開始( チップ種別 チップ種別, ヒット判定種別 ヒット判定種別 )
{
lock( this._スレッド間同期 )
this.表示開始( mapping.表示レーン種別, ヒット判定種別 );
}
}
-
protected override void On活性化( デバイスリソース dr )
{
// すべてのヒット判定レーンに対する進行描画コンテキストを生成する。
this._ヒット判定レーンto進行描画コンテキスト = new Dictionary<表示レーン種別, 進行描画コンテキスト>();
foreach( var judgeType in typeof( 表示レーン種別 ).GetEnumValues() )
- this._ヒット判定レーンto進行描画コンテキスト.Add( (表示レーン種別) judgeType, new 進行描画コンテキスト() );
+ {
+ this._ヒット判定レーンto進行描画コンテキスト.Add( ( 表示レーン種別 ) judgeType, new 進行描画コンテキスト() );
+ }
}
-
protected override void On非活性化( デバイスリソース dr )
{
this._ヒット判定レーンto進行描画コンテキスト.Clear();
}
-
public void 進行描画する( デバイスリソース dr )
{
lock( this._スレッド間同期 )
{
this.初期化する();
}
+
public void 初期化する()
{
this.進行カウンタ = new 単純増加後不変カウンタ();
this.透明度0to1 = 1.0f;
}
}
-
private Dictionary<表示レーン種別, 進行描画コンテキスト> _ヒット判定レーンto進行描画コンテキスト = null;
private readonly Dictionary<ヒット判定種別, 画像> _文字列画像 = new Dictionary<ヒット判定種別, 画像>() {
{ ヒット判定種別.MISS, new 画像( @"$(Static)\images\Judge Miss.png" ) },
{ ヒット判定種別.AUTO, new 画像( @"$(Static)\images\Judge Auto.png" ) },
};
-
private readonly Dictionary<表示レーン種別, float> _ヒットレーン種別toレーン表示位置Y中央dpx = new Dictionary<表示レーン種別, float>() {
{ 表示レーン種別.Unknown, 0f },
{ 表示レーン種別.LeftCrash, 469f },
{ 表示レーン種別.Tom3, 798f },
{ 表示レーン種別.RightCrash, 633f },
};
-
private readonly object _スレッド間同期 = new object();
}
}
}
}
}
-
public float 高さdpx
{
get
{
this.子リスト.Add( this._レーンフレーム画像 = new 画像( @"$(Static)\images\レーン.png" ) );
}
-
public void 進行描画する( デバイスリソース dr )
{
lock( this._スレッド間同期 )
}
private 画像 _レーンフレーム画像 = null;
-
private float _左端位置dpx = 400f;
-
private float _高さdpx = 1080f;
-
private readonly object _スレッド間同期 = new object();
}
}
this._コンテキスト = new コンテキスト[ 最大同時発火数 * 8 ]; // 1発火につき羽を大小 8 枚使う
}
-
public void 発火する( チップ種別 chipType )
{
lock( this._スレッド間同期 )
}
}
}
-
public void 発火する( Vector2 中央位置dpx )
{
lock( this._スレッド間同期 )
}
}
}
-
protected override void On活性化( デバイスリソース dr )
{
// 全コンテキストを初期化。
};
}
}
-
protected override void On非活性化( デバイスリソース dr )
{
}
-
public void 進行描画する( デバイスリソース dr )
{
lock( this._スレッド間同期 )
public float サイズ;
public 単純増加後不変カウンタ 進行カウンタ;
}
-
private コンテキスト[] _コンテキスト = null;
-
private 画像 _羽画像 = null;
-
private readonly object _スレッド間同期 = new object();
}
}
static class 座標
{
public const float 判定バーの中央Y座標dpx = 869f + 43f;
-
public const float レーンフレーム左端のX座標dpx = 619f;
}
}
/// </remarks>
class 演奏ステージ : ステージ
{
- public ConcurrentDictionary<ヒット判定種別, int> ヒットした回数 { get; } = new ConcurrentDictionary<ヒット判定種別, int>();
+ public ConcurrentDictionary<ヒット判定種別, int> ヒットした回数
+ {
+ get;
+ } = new ConcurrentDictionary<ヒット判定種別, int>();
public enum フェーズ
{
キャンセル,
ビュアーメッセージ待機中,
}
-
- public RWLock<フェーズ> 現在のフェーズ { get; } = new RWLock<フェーズ>( フェーズ.初期状態 );
-
-
+ public RWLock<フェーズ> 現在のフェーズ
+ {
+ get;
+ } = new RWLock<フェーズ>( フェーズ.初期状態 );
+
public 演奏ステージ()
{
this.子リスト.Add( this._コンボ = new コンボ() );
this._スクロール譜面.背景動画の再生を開始する = ( 開始位置sec ) => {
this._背景動画?.再生を開始する( 開始位置sec );
this._背景動画開始済み.Value = true;
- if( null != this._BGM )
- this._BGM?.Play( 開始位置sec );
+ this._BGM?.Play( 開始位置sec );
this._BGM再生開始済み = true;
};
this._スクロール譜面.チップをヒットする = ( chip ) => {
this._FPS.FPSをカウントしプロパティを更新する();
};
}
-
protected override void On活性化( デバイスリソース dr )
{
Log.Info( "演奏ステージを開始します。" + ( StrokeStyleT.ビュアーモードである ? "(ViewerMode)" : "" ) );
StrokeStyleT.サウンドデバイス.WaveFormat );
}
this._BGM?.Dispose();
- this._BGM = StrokeStyleT.サウンドデバイス.CreateSound( this._デコード済みWaveSource );
+ this._BGM = StrokeStyleT.サウンドデバイス.サウンドを生成する( this._デコード済みWaveSource );
}
else
{
this._活性化した直後である = true;
}
-
protected override void On非活性化( デバイスリソース dr )
{
Log.Info( "演奏ステージを終了します。" );
if( null != this._背景動画 )
this.子リスト.Remove( this._背景動画 ); // 子リストから削除
}
-
public override void 進行描画する( デバイスリソース dr )
{
// 進行描画。
// --> 入力処理は、スクロール譜面.高頻度処理タスク で行う。
}
-
public void 演奏を停止する()
{
this._スクロール譜面?.演奏を停止する();
this._コンボ.COMBO値 = 0;
}
-
public void BGMを停止する()
{
if( null != this._BGM ) // 背景動画がなければ BGM も null であり、それはエラーではない。
//FDK.Utilities.解放する( ref this._デコード済みWaveSource ); → ここでは解放しない。
}
}
-
public void BGMのキャッシュを解放する()
{
this.BGMを停止する();
FDK.Utilities.解放する( ref this._デコード済みWaveSource );
}
-
private bool _活性化した直後である = false;
-
private double _演奏開始時刻sec = 0.0;
-
private bool _Autoチップのドラム音を再生する = true;
-
private readonly コンボ _コンボ;
-
private readonly レーンフレーム _レーンフレーム;
-
private readonly スクロール譜面 _スクロール譜面;
-
private readonly ヒット判定文字列 _ヒット判定文字列;
-
private readonly 回転羽 _回転羽 = new 回転羽( 32 );
-
private readonly ドラムサウンド _ドラムサウンド;
-
private readonly ドラムセット _ドラムセット;
-
private readonly 画像 _判定バー;
-
private readonly 画像 _ステージ台;
-
private readonly 文字列画像 _FPS画像;
-
private RWLock<bool> _背景動画開始済み = new RWLock<bool>( false );
-
private bool _BGM再生開始済み = false;
-
private RWLock<double> _現在進行描画中の譜面スクロール速度の倍率 = new RWLock<double>( 0.0 );
-
/// <remarks>
/// 停止と解放は、演奏ステージクラスの非活性化後に、外部から行われる。
/// <see cref="SST.ステージ.演奏.演奏ステージ.BGMを停止する"/>
/// <see cref="SST.ステージ.演奏.演奏ステージ.BGMのキャッシュを解放する"/>
/// </remarks>
- private FDK.メディア.サウンド.WASAPI.Sound _BGM = null;
-
+ private Sound _BGM = null;
/// <summary>
/// BGM の生成もとになるデコード済みサウンドデータ。
/// </summary>
/// 演奏ステージインスタンスを破棄する際に、このインスタンスもDisposeすること。
/// </remarks>
private DecodedWaveSource _デコード済みWaveSource = null;
-
private 動画 _背景動画 = null;
-
private FPS _FPS = null;
private double _現在の演奏時刻secを返す()
表示中,
終了,
}
-
- public フェーズ 現在のフェーズ { get; protected set; } = フェーズ.初期状態;
-
- public Action BGMを停止する = null;
-
- public Func<演奏.演奏ステージ> 演奏ステージインスタンスを取得する = null;
+ public フェーズ 現在のフェーズ
+ {
+ get;
+ protected set;
+ } = フェーズ.初期状態;
public 結果ステージ()
{
this.子リスト.Add( this._結果表示パラメータパネル = new 画像( @"$(Static)\images\結果画面 パラメータパネル.png" ) );
this.子リスト.Add( this._結果表示パラメータ = new 画像フォント( @"$(Static)\images\結果画面 パラメータフォント.png", @"$(Static)\images\結果画面 パラメータフォント矩形リスト.xml" ) );
}
+ protected override void On活性化( デバイスリソース dr )
+ {
+ FDK.Log.Info( "結果ステージを開始します。" );
+ this.現在のフェーズ = フェーズ.表示中;
+ this._活性化した直後である = true;
+ }
+ protected override void On非活性化( デバイスリソース dr )
+ {
+ FDK.Log.Info( "結果ステージを終了します。" );
+
+ this.BGMを停止する?.Invoke(); // ここでBGMを終了する。
+ }
public override void 進行描画する( デバイスリソース dr )
{
if( this._活性化した直後である )
}
}
- protected override void On活性化( デバイスリソース dr )
- {
- FDK.Log.Info( "結果ステージを開始します。" );
-
- this.現在のフェーズ = フェーズ.表示中;
- this._活性化した直後である = true;
- }
-
- protected override void On非活性化( デバイスリソース dr )
- {
- FDK.Log.Info( "結果ステージを終了します。" );
-
- this.BGMを停止する?.Invoke(); // ここでBGMを終了する。
- }
+ internal Action BGMを停止する = null;
+ internal Func<演奏.演奏ステージ> 演奏ステージインスタンスを取得する = null;
private bool _活性化した直後である = false;
-
private 動画 _背景動画;
-
private 画像 _背景画像;
-
private 画像 _結果表示パラメータパネル;
-
private 画像フォント _結果表示パラメータ;
}
}
フェードアウト,
終了,
}
-
- public フェーズ 現在のフェーズ { get; protected set; } = フェーズ.初期状態;
+ public フェーズ 現在のフェーズ
+ {
+ get;
+ protected set;
+ } = フェーズ.初期状態;
public 起動ステージ()
{
this.子リスト.Add( this._フェードイン = new フェードイン( 総フェード時間sec: 1.0f ) );
this.子リスト.Add( this._フェードアウト = new フェードアウト( 総フェード時間sec: 1.0f ) );
}
-
protected override void On活性化( デバイスリソース dr )
{
Log.Info( "起動ステージを開始します。" );
this.現在のフェーズ = フェーズ.開始;
}
-
protected override void On非活性化( デバイスリソース dr )
{
Log.Info( "起動ステージを終了します。" );
}
-
public override void 進行描画する( デバイスリソース dr )
{
if( this.現在のフェーズ == フェーズ.終了 )
}
private 単純増加後不変カウンタ _フェーズカウンタ = null;
-
private readonly 画像 _背景画像;
-
private readonly 画像 _フェードインアウト兼用パネル;
-
private readonly フェードイン _フェードイン;
-
private readonly フェードアウト _フェードアウト;
}
}
public 曲パネルビュー( WeakReference<SST.曲.曲ツリー管理> 曲ツリー管理 )
{
this._曲ツリー管理 = 曲ツリー管理;
-
this.子リスト.Add( this._Nullパネルの画像 = new テクスチャ( @"$(Static)\images\選曲パネル.png" ) );
}
-
public void カーソルを上に移動する()
{
if( 0 < this._カーソル位置.Y )
// カーソルと一緒に、選択曲も移動する。
this._曲ツリー管理.GetTarget()?.前のノードを選択する();
}
-
public void カーソルを下に移動する()
{
if( 2 > this._カーソル位置.Y )
// カーソルと一緒に、選択曲も移動する。
this._曲ツリー管理.GetTarget()?.次のノードを選択する();
}
-
public void カーソルを左に移動する()
{
this._カーソル位置.X--; // 制限なし
this._曲ツリー管理.GetTarget()?.前のノードを選択する();
this._曲ツリー管理.GetTarget()?.前のノードを選択する();
}
-
public void カーソルを右に移動する()
{
this._カーソル位置.X++; // 制限なし
this._曲ツリー管理.GetTarget()?.次のノードを選択する();
this._曲ツリー管理.GetTarget()?.次のノードを選択する();
}
-
protected override void On活性化( デバイスリソース dr )
{
this._活性化した直後である = true;
}
-
protected override void On非活性化( デバイスリソース dr )
{
}
-
public void 進行描画する( デバイスリソース dr )
{
// 進行。
}
//-----------------
#endregion
+
#region " 9×3枚の曲パネルを描画する。"
//-----------------
for( int i = 0; i < ( 9 * 3 ); i++ )
}
//-----------------
#endregion
+
#region " カーソル位置のパネルを描画する。"
//-----------------
if( ( 0 <= this._カーソル位置.X ) && ( 9 > this._カーソル位置.X ) &&
}
private const float _カーソル位置のパネルの拡大率 = 1.25f;
-
private bool _活性化した直後である = false;
-
private WeakReference<SST.曲.曲ツリー管理> _曲ツリー管理 = null;
-
private テクスチャ _Nullパネルの画像;
-
private FDK.カウンタ.定間隔進行 _横スクロール用カウンタ = null;
/// <summary>
var Y軸回転 = Matrix.RotationY( MathUtil.DegreesToRadians( ( ( パネル位置.X - 4 ) + ( this._パネル全体のY軸回転オフセット / 64f ) ) * パネル間X方向角度deg ) );
- // Nullパネル画像を表示する。
+ #region " Nullパネル画像を表示する。"
+ //----------------
{
var 拡大縮小 = Matrix.Scaling( SST.曲.Node.ノードの全体サイズdpx.Width, SST.曲.Node.ノードの全体サイズdpx.Height, 1f )
* Matrix.Scaling( 見かけの倍率 )
this._Nullパネルの画像.描画する( dr, ( 拡大縮小 * 平行移動 * Y軸回転 ) );
}
+ //----------------
+ #endregion
+
if( null != パネルノード )
{
- // ノード画像を表示する。
+ #region " ノード画像を表示する。"
+ //----------------
{
var 拡大縮小 = Matrix.Scaling( パネルノード.ノードの画像領域のサイズdpx.Width, パネルノード.ノードの画像領域のサイズdpx.Height, 1f )
* Matrix.Scaling( 見かけの倍率 )
パネルノード.ノード画像を描画する( dr, ( 拡大縮小 * 平行移動 * Y軸回転 ) );
}
+ //----------------
+ #endregion
- // タイトル文字列画像を表示する。
+ #region " タイトル文字列画像を表示する。"
+ //----------------
{
float 幅dpx = Math.Min( パネルノード.タイトル画像サイズdpx.Width, パネルノード.ノードのタイトル領域のサイズdpx.Width ); // 左右にはみ出さないよう圧縮
var 拡大縮小 = Matrix.Scaling( 幅dpx, パネルノード.タイトル画像サイズdpx.Height, 1f )
パネルノード.タイトル画像を描画する( dr, ( 拡大縮小 * 平行移動 * Y軸回転 ) );
}
+ //----------------
+ #endregion
}
}
}
曲確定,
キャンセル,
}
-
- public フェーズ 現在のフェーズ { get; protected set; } = フェーズ.初期状態;
+ public フェーズ 現在のフェーズ
+ {
+ get;
+ protected set;
+ } = フェーズ.初期状態;
public 選曲ステージ()
{
this.子リスト.Add( this._ドラムセット = new ドラムセット() );
this.子リスト.Add( this._曲パネルビュー = new 曲パネルビュー( new WeakReference<曲.曲ツリー管理>( StrokeStyleT.曲ツリー管理 ) ) );
}
-
protected override void On活性化( デバイスリソース dr )
{
FDK.Log.Info( "選曲ステージを開始します。" );
this.現在のフェーズ = フェーズ.表示中;
this._活性化した直後である = true;
}
-
protected override void On非活性化( デバイスリソース dr )
{
FDK.Log.Info( "選曲ステージを終了します。" );
this.子リスト.Remove( StrokeStyleT.曲ツリー管理 );
}
-
public override void 進行描画する( デバイスリソース dr )
{
if( this.現在のフェーズ != フェーズ.表示中 )
return;
+ #region " 初めての進行描画 "
+ //----------------
if( this._活性化した直後である )
{
this._背景動画.再生を開始する( 開始位置sec: 0.0, ループ再生する: true );
this._活性化した直後である = false;
}
+ //----------------
+ #endregion
// 進行描画。
// 決定 → 現在選択されているノードによって異なる。
if( StrokeStyleT.入力管理.操作コマンドが入力された( 入力.操作コマンド種別.決定 ) )
{
- if( StrokeStyleT.曲ツリー管理.現在選択されているノード is 曲.MusicNode )
- {
- FDK.Log.Info( "MusicNode が選択されました。" );
- this.現在のフェーズ = フェーズ.曲確定;
- }
- else if( StrokeStyleT.曲ツリー管理.現在選択されているノード is 曲.BoxNode )
- {
- FDK.Log.Info( "BoxNode が選択されました。" );
- // todo: BOX が選択された場合の実装。
- }
- else if( StrokeStyleT.曲ツリー管理.現在選択されているノード is 曲.BackNode )
- {
- FDK.Log.Info( "BackNode が選択されました。" );
- // todo: 戻る が選択された場合の実装。
- }
- else if( null == StrokeStyleT.曲ツリー管理.現在選択されているノード )
- {
- FDK.Log.Info( "曲リストが空です。何もしません。" );
- }
- else
+ switch( StrokeStyleT.曲ツリー管理.現在選択されているノード )
{
- Trace.Fail( "[バグあり] 確定された曲が、曲でも BOX でも 戻る でも null でもありません。" );
+ case 曲.MusicNode music:
+ FDK.Log.Info( "MusicNode が選択されました。" );
+ this.現在のフェーズ = フェーズ.曲確定;
+ break;
+
+ case 曲.BoxNode box:
+ FDK.Log.Info( "BoxNode が選択されました。" );
+
+ // todo: BOX が選択された場合の実装。
+ break;
+
+ case 曲.BackNode back:
+ FDK.Log.Info( "BackNode が選択されました。" );
+
+ // todo: 戻る が選択された場合の実装。
+
+ break;
+
+ case null:
+ FDK.Log.Info( "曲リストが空です。何もしません。" );
+ break;
+
+ default:
+ Trace.Fail( "[バグあり] 確定された曲が、曲でも BOX でも 戻る でも null でもありません。" );
+ break;
}
}
+
// キャンセル or ESC → 戻る
else if( StrokeStyleT.入力管理.操作コマンドが入力された( 入力.操作コマンド種別.キャンセル ) ||
StrokeStyleT.入力管理.キーボード入力.キーが押された( Key.Escape ) )
-
{
this.現在のフェーズ = フェーズ.キャンセル;
}
// 上下左右またはカーソルキー(キーバインドに寄らず固定)→ 選曲カーソル移動
- else if( StrokeStyleT.入力管理.操作コマンドが入力された( 入力.操作コマンド種別.左 ) ||
- StrokeStyleT.入力管理.キーボード入力.キーが押された( Key.Left ) )
+ else if( StrokeStyleT.入力管理.操作コマンドが入力された( 入力.操作コマンド種別.左 ) || StrokeStyleT.入力管理.キーボード入力.キーが押された( Key.Left ) )
{
this._曲パネルビュー.カーソルを左に移動する();
}
- else if( StrokeStyleT.入力管理.操作コマンドが入力された( 入力.操作コマンド種別.右 ) ||
- StrokeStyleT.入力管理.キーボード入力.キーが押された( Key.Right ) )
+ else if( StrokeStyleT.入力管理.操作コマンドが入力された( 入力.操作コマンド種別.右 ) || StrokeStyleT.入力管理.キーボード入力.キーが押された( Key.Right ) )
{
this._曲パネルビュー.カーソルを右に移動する();
}
- else if( StrokeStyleT.入力管理.操作コマンドが入力された( 入力.操作コマンド種別.上 ) ||
- StrokeStyleT.入力管理.キーボード入力.キーが押された( Key.Up ) )
+ else if( StrokeStyleT.入力管理.操作コマンドが入力された( 入力.操作コマンド種別.上 ) || StrokeStyleT.入力管理.キーボード入力.キーが押された( Key.Up ) )
{
this._曲パネルビュー.カーソルを上に移動する();
}
- else if( StrokeStyleT.入力管理.操作コマンドが入力された( 入力.操作コマンド種別.下 ) ||
- StrokeStyleT.入力管理.キーボード入力.キーが押された( Key.Down ) )
+ else if( StrokeStyleT.入力管理.操作コマンドが入力された( 入力.操作コマンド種別.下 ) || StrokeStyleT.入力管理.キーボード入力.キーが押された( Key.Down ) )
{
this._曲パネルビュー.カーソルを下に移動する();
}
}
private bool _活性化した直後である = false;
-
private 動画 _背景動画 = null;
-
private 画像 _ステージ台 = null;
-
private ドラムセット _ドラムセット = null;
-
private 曲パネルビュー _曲パネルビュー = null;
}
}
/// 静的な(アプリを通じて不変の)リソースファイルが置かれる場所のルートフォルダ。絶対パス。
/// 具体的には、exe のあるフォルダ。
/// </summary>
- public string StaticFolder => Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location );
+ public string StaticFolder
+ {
+ get
+ => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location );
+ }
/// <summary>
/// 共通データが保存される場所のルートフォルダ。絶対パス。
public string AppDataFolder
{
get
- {
- return Path.Combine(
- Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create ),
- @"StrokeStyleT\" );
- }
+ => Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create ), @"StrokeStyleT\" );
}
/// <summary>
public virtual ExtensionDataObject ExtensionData
{
- get { return _ExData; }
- set { _ExData = value; }
+ get
+ => this._ExData;
+
+ set
+ => this._ExData = value;
}
//----------------
#endregion
/// </param>
public void ユーザを選択する( string ユーザ名 )
{
- var userName = this.ユーザリスト.Find( ( user ) => ( user.名前 == ユーザ名 ) );
-
- if( null == userName )
+ this.現在選択されているユーザ = this.ユーザリスト.Find( ( user ) => ( user.名前 == ユーザ名 ) ) ??
throw new SSTException( $"存在しない名前「{ユーザ名}」のユーザを選択しようとしました。" );
-
- this.現在選択されているユーザ = userName;
}
/// <summary>
Indent = true, // 改行&インデントを使う(falseにすると改行もされなくなる)
};
- using( var writer = XmlWriter.Create( FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス ), settings ) )
+ using( var writer = XmlWriter.Create( フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス ), settings ) )
{
serializer.WriteObject( writer, this );
}
{
var userManager = new ユーザ管理();
- string xmlPath = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス );
+ string xmlPath = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス );
if( File.Exists( xmlPath ) )
{
public virtual ExtensionDataObject ExtensionData
{
- get { return _ExData; }
- set { _ExData = value; }
+ get
+ => this._ExData;
+
+ set
+ => this._ExData = value;
}
//----------------
#endregion
{
var keyBinding = new キーバインディング();
- string xmlPath = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス );
+ string xmlPath = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス );
if( File.Exists( xmlPath ) )
{
public virtual ExtensionDataObject ExtensionData
{
- get { return _ExData; }
- set { _ExData = value; }
+ get
+ => this._ExData;
+
+ set
+ => this._ExData = value;
}
//----------------
#endregion
/// </remarks>
class ドラム様式入力イベント
{
- public ドラム様式入力種別 ドラム様式入力種別 { get; protected set; }
-
- public InputEvent 入力イベント { get; protected set; }
+ public ドラム様式入力種別 ドラム様式入力種別
+ {
+ get;
+ protected set;
+ } = ドラム様式入力種別.Unknown;
+ public InputEvent 入力イベント
+ {
+ get;
+ protected set;
+ } = null;
public ドラム様式入力イベント( InputEvent 入力イベント, ドラム様式入力種別 ドラム様式入力種別 )
{
/// <remarks>
/// ポーリングのたびにクリアされ、前回のポーリング以降の入力イベントで再構築される。
/// </remarks>
- public List<ドラム様式入力イベント> ドラム様式入力イベントリスト { get; protected set; }
+ public List<ドラム様式入力イベント> ドラム様式入力イベントリスト
+ {
+ get;
+ protected set;
+ }
/// <summary>
/// キーボード入力デバイス。
/// <remarks>
/// ポーリング後、入力リストへ格納する前の入力データに直接アクセスできる。
/// </remarks>
- public Keyboard キーボード入力 { get; protected set; }
+ public Keyboard キーボード入力
+ {
+ get;
+ protected set;
+ }
/// <summary>
/// MIDI入力デバイス。
/// <remarks>
/// ポーリング後、入力リストへ格納する前の入力データに直接アクセスできる。
/// </remarks>
- public MidiIn MIDI入力 { get; protected set; }
+ public MidiIn MIDI入力
+ {
+ get;
+ protected set;
+ }
/// <summary>
/// コンストラクタ。入力デバイスを生成する。
}
private キーバインディング _キーバインディング = null;
-
private string _KeyBindingsXmlファイルパス = @"$(AppData)\KeyBindings.xml";
-
private const double _連続入力だとみなす最大の間隔sec = 0.5;
-
private int _入力履歴で保管する最大履歴数;
-
private List<ドラム様式入力種別> _入力履歴 = null;
-
private bool _現在入力履歴を記録中である = true;
-
private double? _前回の入力履歴の追加時刻sec = null; // null なら次が初回。
/// <summary>
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text.RegularExpressions;
using SSTFormat.v2;
namespace SST.曲
/// </summary>
public class BoxDef
{
- public string Title { get; set; } = "";
-
- public string Artist { get; set; } = "";
+ public string Title
+ {
+ get;
+ set;
+ } = "";
+ public string Artist
+ {
+ get;
+ set;
+ } = "";
/// <summary>
/// コンストラクタ。
{
this.BOX定義ファイルを読み込む( boxdefファイルパス );
}
-
public void BOX定義ファイルを読み込む( string boxdefファイルパス )
{
using( var sr = new StreamReader( boxdefファイルパス ) )
{
string 行;
+
while( ( 行 = sr.ReadLine() ) != null )
{
try
}
//---------------------
#endregion
+
#region " ARTIST コマンド "
//---------------------
if( スコア.コマンドのパラメータ文字列部分を返す( 行, @"ARTIST", out パラメータ ) )
/// <summary>
/// この曲ノードに対応するSSTFファイル。
/// </summary>
- public string sstfファイルパス { get; protected set; } = null;
+ public string sstfファイルパス
+ {
+ get;
+ protected set;
+ } = null;
/// <summary>
/// この曲ノードに対応する動画ファイル。
/// <remarks>
/// 現在のSSTF仕様では、SSTFファイルと同じ場所に存在する動画ファイルが自動的に選択される。
/// </remarks>
- public string 動画ファイルパス { get; protected set; } = null;
+ public string 動画ファイルパス
+ {
+ get;
+ protected set;
+ } = null;
/// <summary>
/// コンストラクタ。
public MusicNode()
{
}
-
+
/// <summary>
/// コンストラクタ。
/// </summary>
/// </param>
public MusicNode( string sstfファイルパス, Node 親ノード = null ) : this()
{
- this.sstfファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( sstfファイルパス );
+ this.sstfファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( sstfファイルパス );
this.親ノード = 親ノード;
// sstf ファイルから情報を取得する。
base.On活性化( dr ); // Node の活性化
}
-
protected override void On非活性化( デバイスリソース dr )
{
if( null != this._サムネイル画像 )
base.On非活性化( dr ); // Node の非活性化
}
-
public override void ノード画像を描画する( デバイスリソース dr, SharpDX.Matrix ワールド変換行列 )
{
this._サムネイル画像?.描画する( dr, ワールド変換行列 );
/// <remarks>
/// ノードの画像として表示される。
/// </remarks>
- private FDK.メディア.テクスチャ _サムネイル画像 = null;
+ private テクスチャ _サムネイル画像 = null;
/// <summary>
/// 動画ファイルを自動検索する際に、検索対象とする拡張子。固定。
/// </remarks>
public abstract class Node : FDK.Activity
{
- public string タイトル { set; get; } = "(no title)";
+ public string タイトル
+ {
+ set;
+ get;
+ } = "(no title)";
/// <summary>
/// 曲ツリーにおいて、このノードの親となるノード。
/// このプロパティが null の場合は、ルートノードであるか、または曲ツリーにまだ登録されていないノードであることを意味する。
/// ルートノードの親ノードは、常に null である。
/// </remarks>
- public Node 親ノード { get; set; } = null;
+ public Node 親ノード
+ {
+ get;
+ set;
+ } = null;
/// <summary>
/// 曲ツリーにおいて、このノードの子となるノードの集合。
/// <remarks>
/// 原則として、ルートノード と BOXノード 以外は 空リスト である。
/// </remarks>
- public List<Node> 子ノードリスト { get; } = new List<Node>(); // 一応、null にはしない。
+ public List<Node> 子ノードリスト
+ {
+ get;
+ } = new List<Node>(); // 一応、null にはしない。
/// <summary>
/// 曲ツリーにおいて、このノードの1つ前に位置する兄弟ノードを示す。
get
{
var 兄弟リスト = this.親ノード.子ノードリスト;
+
if( 0 == 兄弟リスト.Count )
throw new SSTException( "兄弟リストの Count が 0 です。" );
/// <remarks>
/// ノードの表示エリアは、画像領域(上側)とタイトル画像領域(下側)の合計である。
/// </remarks>
- public static SharpDX.Size2F ノードの全体サイズdpx => new SharpDX.Size2F( 314f, 220f );
+ public static SharpDX.Size2F ノードの全体サイズdpx
+ => new SharpDX.Size2F( 314f, 220f );
/// <summary>
/// 選曲ステージでノードを表示する際の、1つのノードの画像領域の表示サイズ(設計単位)。
/// <remarks>
/// ノードの表示エリアは、画像領域(上側)とタイトル画像領域(下側)の合計である。
/// </remarks>
- public virtual SharpDX.Size2F ノードの画像領域のサイズdpx => new SharpDX.Size2F( 314f, 220f-82f );
+ public virtual SharpDX.Size2F ノードの画像領域のサイズdpx
+ => new SharpDX.Size2F( 314f, 220f-82f );
/// <summary>
/// 選曲ステージでノードを表示する際の、1つのノードのタイトル画像領域の表示サイズ(設計単位)。
/// <remarks>
/// ノードの表示エリアは、画像領域(上側)とタイトル画像領域(下側)の合計である。
/// </remarks>
- public virtual SharpDX.Size2F ノードのタイトル領域のサイズdpx => new SharpDX.Size2F( 314f, 82f );
+ public virtual SharpDX.Size2F ノードのタイトル領域のサイズdpx
+ => new SharpDX.Size2F( 314f, 82f );
/// <summary>
/// タイトル画像のサイズ(論理設計単位)。
/// <remarks>
/// タイトル画像は、ノードのタイトル領域に表示される画像である。
/// </remarks>
- public SharpDX.Size2F タイトル画像サイズdpx => this._タイトル文画像.サイズdpx;
+ public SharpDX.Size2F タイトル画像サイズdpx
+ {
+ get
+ => this._タイトル文画像.サイズdpx;
+ }
protected override void On活性化( デバイスリソース dr )
{
// タイトル部の文字画像を作成する。
this.子リスト.Add( this._タイトル文画像 = new タイトルテクスチャ( this.タイトル ) );
}
-
protected override void On非活性化( デバイスリソース dr )
{
this.子リスト.Remove( this._タイトル文画像 );
}
-
protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
{
// まだ既定の曲画像が生成されてなかったら生成する。
Node._既定の曲画像.活性化する( dr ); // 既定の曲画像には子Activityはないので、ここで活性化してよい。
}
}
-
protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
{
// まだ既定の曲画像が解放されてなかったら解放する。
Node._既定の曲画像 = null;
}
}
-
public virtual void ノード画像を描画する( デバイスリソース dr, SharpDX.Matrix ワールド変換行列 )
{
if( Node._既定の曲画像.活性化している )
Node._既定の曲画像.描画する( dr, ワールド変換行列 );
}
-
public virtual void タイトル画像を描画する( デバイスリソース dr, SharpDX.Matrix ワールド変換行列 )
{
if( this._タイトル文画像.活性化している )
/// 描画したい文字列。
/// この文字列を変更すると、次回の「描画する()」メソッドの呼び出し時にテクスチャが更新される。
/// </summary>
- public string 表示文字列 { get; set; } = null;
+ public string 表示文字列
+ {
+ get;
+ set;
+ } = null;
- /// <summary>
- /// コンストラクタ。
- /// </summary>
- /// <param name="初期文字列">
- /// </param>
public タイトルテクスチャ( string 初期文字列 ) : base( new SharpDX.Size2( 1, 1 ) )
{
this.表示文字列 = 初期文字列 ?? "(no title)";
}
-
protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
{
this._テクスチャを更新する( dr );
}
-
protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
{
// テキスト描画用リソースを解放する。
// ビットマップ付きテクスチャを解放する。
base.Onデバイス依存リソースの解放( dr );
}
-
public new void 描画する( デバイスリソース dr, SharpDX.Matrix ワールド行列変換, SharpDX.RectangleF? 転送元矩形 = null )
{
if( this.表示文字列.Nullまたは空である() )
}
private float _フォントサイズpt = 30.0f;
-
private string _フォント名 = "メイリオ";
-
private string _前回の表示文字列 = null;
-
private SharpDX.DirectWrite.TextFormat _テキストフォーマット = null;
-
private SharpDX.DirectWrite.TextLayout _テキストレイアウト = null;
-
private SharpDX.Direct2D1.SolidColorBrush _白ブラシ = null;
-
private SharpDX.Direct2D1.SolidColorBrush _黒ブラシ = null;
private void _テクスチャを更新する( デバイスリソース dr )
};
var 最大サイズdpx = dr.設計画面サイズdpx;
+
this._テキストレイアウト = new SharpDX.DirectWrite.TextLayout(
dr.DWriteFactory,
this.表示文字列.Nullまたは空である() ? " " : this.表示文字列,
public RootNode 現在の管理対象ツリー
{
get
- {
- return this._現在の管理対象ツリー;
- }
+ => this._現在の管理対象ツリー;
+
set
{
this._現在の管理対象ツリー = value;
/// 通常時はこのノードは 現在の管理対象ツリー 内に存在するが、ビュアーモード時はツリー自体が存在しない。
/// ビュアーモード時は、このプロパティに直接ノード(MusicNode)を指定する。
/// </remarks>
- public Node 現在選択されているノード { get; set; } = null;
+ public Node 現在選択されているノード
+ {
+ get;
+ set;
+ } = null;
/// <summary>
/// 現在の管理対象ツリーにおいて、現在選択されているノードの1つ前に位置する兄弟ノードを選択する。
// フォルダが存在しないなら何もしない。
if( false == Directory.Exists( フォルダパス ) )
{
- FDK.Log.WARNING( $"指定されたフォルダが存在しません。無視します。[{FDK.フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( フォルダパス )}]" );
+ FDK.Log.WARNING( $"指定されたフォルダが存在しません。無視します。[{フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( フォルダパス )}]" );
return;
}
+
var dirInfo = new DirectoryInfo( フォルダパス );
// (1) このフォルダにあるすべてのsstfファイルから、曲ノードを作成する。
{
// box.def を含むフォルダの場合 → BOX ノードを作成する。
var boxファイルパス = Path.Combine( subDirInfo.FullName, @"box.def" );
+
if( File.Exists( boxファイルパス ) )
{
追加対象の親ノード.子ノードリスト.Add( new BoxNode( boxファイルパス, 追加対象の親ノード ) );
this.子リスト.Add( node );
}
}
-
protected override void On非活性化( デバイスリソース dr )
{
if( null != this._現在の管理対象ツリー )
Indent = true, // 改行&インデントを使う(falseにすると改行もされなくなる)
};
- using( var writer = XmlWriter.Create( FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス ), settings ) )
+ using( var writer = XmlWriter.Create( フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス ), settings ) )
{
serializer.WriteObject( writer, this );
}
{
var config = new Config();
- string xmlPath = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス );
+ string xmlPath = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス );
if( File.Exists( xmlPath ) )
{
public virtual ExtensionDataObject ExtensionData
{
- get { return _ExData; }
- set { _ExData = value; }
+ get
+ => this._ExData;
+
+ set
+ => this._ExData = value;
}
//----------------
#endregion
{
foreach( var itemObject in Enum.GetValues( typeof( ドラム様式入力種別 ) ) )
{
- var item = (ドラム様式入力種別) itemObject;
-
- if( this.AutoPlay.ContainsKey( item ) )
- this.AutoPlay.Remove( item );
-
- this.AutoPlay[ item ] = 設定値;
+ this.AutoPlay[ ( ドラム様式入力種別 ) itemObject ] = 設定値;
}
}
Indent = true, // 改行&インデントを使う(falseにすると改行もされなくなる)
};
- using( var writer = XmlWriter.Create( FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス ), settings ) )
+ using( var writer = XmlWriter.Create( フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス ), settings ) )
{
serializer.WriteObject( writer, this );
}
{
var option = new Option();
- string xmlPath = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス );
+ string xmlPath = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( XMLファイルパス );
if( File.Exists( xmlPath ) )
{
return true; // 既定値。
}
public bool チップは自動演奏である( チップ chip )
- {
- return this.チップは自動演奏である( chip.チップ種別 );
- }
-
+ => this.チップは自動演奏である( chip.チップ種別 );
public ドラムチップからのマッピング マッピングを取得する( チップ種別 chipType )
{
if( this._チップからのマッピング.ContainsKey( chipType ) )
{
return this._チップからのマッピング[ chipType ];
}
-
- return null; // チップはドラムチップじゃない
+ else
+ {
+ return null; // チップはドラムチップじゃない
+ }
}
public ドラムチップからのマッピング マッピングを取得する( チップ chip )
- {
- return this.マッピングを取得する( chip.チップ種別 );
- }
+ => this.マッピングを取得する( chip.チップ種別 );
/// <summary>
/// チップ種別と各種種別との対応表。
public virtual ExtensionDataObject ExtensionData
{
- get { return _ExData; }
- set { _ExData = value; }
+ get
+ => this._ExData;
+
+ set
+ => this._ExData = value;
}
//----------------
#endregion
/// <summary>
/// ドラムチップの種別。マッピングのキー値。
/// </summary>
- public チップ種別 チップ種別 = チップ種別.Unknown;
+ public チップ種別 チップ種別
+ {
+ get;
+ set;
+ } = チップ種別.Unknown;
/// <summary>
/// ドラムチップに対応するレーン種別。
/// </summary>
- public レーン種別 レーン種別 = レーン種別.Bass;
+ public レーン種別 レーン種別
+ {
+ get;
+ set;
+ } = レーン種別.Bass;
/// <summary>
/// ドラムチップの表示先レーン。
/// </summary>
- public 表示レーン種別 表示レーン種別 = 表示レーン種別.Unknown;
+ public 表示レーン種別 表示レーン種別
+ {
+ get;
+ set;
+ } = 表示レーン種別.Unknown;
/// <summary>
/// ドラムチップとヒット判定を行うドラム様式入力。
/// ヒット判定を行わないチップなら、Unknown。
/// </summary>
- public ドラム様式入力種別 ドラム様式入力種別 = ドラム様式入力種別.Unknown;
+ public ドラム様式入力種別 ドラム様式入力種別
+ {
+ get;
+ set;
+ } = ドラム様式入力種別.Unknown;
- public bool ヒット判定する => ( this.ドラム様式入力種別 != ドラム様式入力種別.Unknown );
- public bool ヒット判定しない => !this.ヒット判定する;
+ public bool ヒット判定する
+ {
+ get
+ => ( this.ドラム様式入力種別 != ドラム様式入力種別.Unknown );
+ }
+ public bool ヒット判定しない
+ {
+ get
+ => !( this.ヒット判定する );
+ }
public ドラムチップからのマッピング( チップ種別 chipType, レーン種別 laneType, 表示レーン種別 locationLaneType, ドラム様式入力種別 drumType )
{