2 using System.Collections.Generic;
\r
4 using System.Threading;
\r
5 using System.Runtime.InteropServices;
\r
6 using System.Diagnostics;
\r
7 using SharpDX.DirectInput;
\r
11 public class CSoundTimer : CTimerBase
\r
13 public override long nシステム時刻ms
\r
17 if( this.Device.e出力デバイス == ESoundDeviceType.ExclusiveWASAPI ||
\r
18 this.Device.e出力デバイス == ESoundDeviceType.SharedWASAPI ||
\r
19 this.Device.e出力デバイス == ESoundDeviceType.ASIO )
\r
21 // BASS 系の ISoundDevice.n経過時間ms はオーディオバッファの更新間隔ずつでしか更新されないため、単にこれを返すだけではとびとびの値になる。
\r
22 // そこで、更新間隔の最中に呼ばれた場合は、システムタイマを使って補間する。
\r
23 // この場合の経過時間との誤差は更新間隔以内に収まるので問題ないと判断する。
\r
24 // ただし、ASIOの場合は、転送byte数から時間算出しているため、ASIOの音声合成処理の負荷が大きすぎる場合(処理時間が実時間を超えている場合)は
\r
25 // 動作がおかしくなる。(具体的には、ここで返すタイマー値の逆行が発生し、スクロールが巻き戻る)
\r
26 // この場合の対策は、ASIOのバッファ量を増やして、ASIOの音声合成処理の負荷を下げること。
\r
28 if ( this.Device.n経過時間を更新したシステム時刻ms == CTimer.n未使用 ) // #33890 2014.5.27 yyagi
\r
30 // 環境によっては、ASIOベースの演奏タイマーが動作する前(つまりASIOのサウンド転送が始まる前)に
\r
31 // DTXデータの演奏が始まる場合がある。
\r
32 // その場合、"this.Device.n経過時間を更新したシステム時刻" が正しい値でないため、
\r
33 // 演奏タイマの値が正しいものとはならない。そして、演奏タイマーの動作が始まると同時に、
\r
34 // 演奏タイマの値がすっ飛ぶ(極端な負の値になる)ため、演奏のみならず画面表示もされない状態となる。
\r
35 // (画面表示はタイマの値に連動して行われるが、0以上のタイマ値に合わせて動作するため、
\r
36 // 不の値が来ると画面に何も表示されなくなる)
\r
38 // そこで、演奏タイマが動作を始める前(this.Device.n経過時間を更新したシステム時刻ms == CTimer.n未使用)は、
\r
39 // 補正部分をゼロにして、n経過時間msだけを返すようにする。
\r
40 // こうすることで、演奏タイマが動作を始めても、破綻しなくなる。
\r
41 return this.Device.n経過時間ms;
\r
45 if ( FDK.CSound管理.bUseOSTimer )
\r
48 return ctDInputTimer.nシステム時刻ms; // 仮にCSoundTimerをCTimer相当の動作にしてみた
\r
52 return this.Device.n経過時間ms
\r
53 + ( this.Device.tmシステムタイマ.nシステム時刻ms - this.Device.n経過時間を更新したシステム時刻ms );
\r
57 else if( this.Device.e出力デバイス == ESoundDeviceType.DirectSound )
\r
59 //return this.Device.n経過時間ms; // #24820 2013.2.3 yyagi TESTCODE DirectSoundでスクロールが滑らかにならないため、
\r
60 return ct.nシステム時刻ms; // 仮にCSoundTimerをCTimer相当の動作にしてみた
\r
62 return CTimerBase.n未使用;
\r
66 public CSoundTimer( ISoundDevice device )
\r
68 this.Device = device;
\r
70 if ( this.Device.e出力デバイス != ESoundDeviceType.DirectSound )
\r
72 TimerCallback timerDelegate = new TimerCallback( SnapTimers ); // CSoundTimerをシステム時刻に変換するために、
\r
73 timer = new Timer( timerDelegate, null, 0, 1000 ); // CSoundTimerとCTimerを両方とも走らせておき、
\r
74 ctDInputTimer = new CTimer( CTimer.E種別.MultiMedia ); // 1秒に1回時差を測定するようにしておく
\r
76 else // TESTCODE DirectSound時のみ、CSoundTimerでなくCTimerを使う
\r
78 ct = new CTimer( CTimer.E種別.MultiMedia );
\r
82 private void SnapTimers(object o) // 1秒に1回呼び出され、2つのタイマー間の現在値をそれぞれ保持する。
\r
84 if ( this.Device.e出力デバイス != ESoundDeviceType.DirectSound )
\r
88 this.nDInputTimerCounter = this.ctDInputTimer.nシステム時刻ms;
\r
89 this.nSoundTimerCounter = this.nシステム時刻ms;
\r
90 //Debug.WriteLine( "BaseCounter: " + nDInputTimerCounter + ", " + nSoundTimerCounter );
\r
92 catch ( Exception e )
\r
93 // サウンド設定変更時に、timer.Dispose()した後、timerが実際に停止する前にここに来てしまう場合があり
\r
94 // その際にNullReferenceExceptionが発生する
\r
95 // timerが実際に停止したことを検出してから次の設定をすべきだが、実装が難しいため、
\r
96 // ここで単に例外破棄することで代替する
\r
98 Trace.TraceInformation("FDK: CSoundTimer.SnapTimers(): 例外発生しましたが、継続します。" + e.Message );
\r
102 public long nサウンドタイマーのシステム時刻msへの変換( long nDInputのタイムスタンプ )
\r
104 return nDInputのタイムスタンプ - this.nDInputTimerCounter + this.nSoundTimerCounter; // Timer違いによる時差を補正する
\r
108 // キーボードイベント(keybd_eventの引数と同様のデータ)
\r
109 [StructLayout( LayoutKind.Sequential )]
\r
110 private struct KEYBDINPUT
\r
113 public ushort wScan;
\r
114 public uint dwFlags;
\r
116 public IntPtr dwExtraInfo;
\r
118 // 各種イベント(SendInputの引数データ)
\r
119 [StructLayout( LayoutKind.Sequential )]
\r
120 private struct INPUT
\r
123 public KEYBDINPUT ki;
\r
125 // キー操作、マウス操作をシミュレート(擬似的に操作する)
\r
126 [DllImport( "user32.dll" )]
\r
127 private extern static void SendInput(
\r
128 int nInputs, ref INPUT pInputs, int cbsize );
\r
130 // 仮想キーコードをスキャンコードに変換
\r
131 [DllImport( "user32.dll", EntryPoint = "MapVirtualKeyA" )]
\r
132 private extern static int MapVirtualKey(
\r
133 int wCode, int wMapType );
\r
135 [DllImport( "user32.dll" )]
\r
136 static extern IntPtr GetMessageExtraInfo();
\r
138 private const int INPUT_MOUSE = 0; // マウスイベント
\r
139 private const int INPUT_KEYBOARD = 1; // キーボードイベント
\r
140 private const int INPUT_HARDWARE = 2; // ハードウェアイベント
\r
141 private const int KEYEVENTF_KEYDOWN = 0x0; // キーを押す
\r
142 private const int KEYEVENTF_KEYUP = 0x2; // キーを離す
\r
143 private const int KEYEVENTF_EXTENDEDKEY = 0x1; // 拡張コード
\r
144 private const int KEYEVENTF_SCANCODE = 0x8;
\r
145 private const int KEYEVENTF_UNIOCODE = 0x4;
\r
146 private const int VK_SHIFT = 0x10; // SHIFTキー
\r
148 private void pollingSendInput()
\r
150 // INPUT[] inp = new INPUT[ 2 ];
\r
151 INPUT inp = new INPUT();
\r
155 //inp[0].type = INPUT_KEYBOARD;
\r
156 //inp[ 0 ].ki.wVk = ( ushort ) Key.B;
\r
157 //inp[ 0 ].ki.wScan = ( ushort ) MapVirtualKey( inp[ 0 ].ki.wVk, 0 );
\r
158 //inp[ 0 ].ki.dwFlags = KEYEVENTF_KEYDOWN;
\r
159 //inp[ 0 ].ki.dwExtraInfo = IntPtr.Zero;
\r
160 //inp[ 0 ].ki.time = 0;
\r
161 inp.type = INPUT_KEYBOARD;
\r
162 inp.ki.wVk = ( ushort ) Key.B;
\r
163 inp.ki.wScan = ( ushort ) MapVirtualKey( inp.ki.wVk, 0 );
\r
164 inp.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYDOWN;
\r
165 inp.ki.dwExtraInfo = GetMessageExtraInfo();
\r
168 //// (3)キーボード(A)を離す
\r
169 //inp[ 1 ].type = INPUT_KEYBOARD;
\r
170 //inp[ 1 ].ki.wVk = ( short ) Key.B;
\r
171 //inp[ 1 ].ki.wScan = ( short ) MapVirtualKey( inp[ 1 ].ki.wVk, 0 );
\r
172 //inp[ 1 ].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
\r
173 //inp[ 1 ].ki.dwExtraInfo = 0;
\r
174 //inp[ 1 ].ki.time = 0;
\r
177 SendInput( 1, ref inp, Marshal.SizeOf( inp ) );
\r
178 Debug.WriteLine( "B" );
\r
179 Thread.Sleep( 1000 );
\r
183 public override void Dispose()
\r
185 // 特になし; ISoundDevice の解放は呼び出し元で行うこと。
\r
188 if ( timer != null )
\r
190 timer.Change( System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite );
\r
191 // ここで、実際にtimerが停止したことを確認するコードを追加すべきだが、やり方わからず。
\r
192 // 代替策として、SnapTimers()中で、例外発生を破棄している。
\r
204 public ISoundDevice Device = null; // debugのため、一時的にprotectedをpublicにする。後で元に戻しておくこと。
\r
205 //protected Thread thSendInput = null;
\r
206 //protected Thread thSnapTimers = null;
\r
207 private CTimer ctDInputTimer = null;
\r
208 private long nDInputTimerCounter = 0;
\r
209 private long nSoundTimerCounter = 0;
\r
210 Timer timer = null;
\r
212 private CTimer ct = null; // TESTCODE
\r