3 namespace FDK.メディア.サウンド.WASAPI
6 /// WASAPIデバイス(のIAudioClock)をソースとするタイマー。
9 /// WASAPIデバイスと厳密に同期をとる場合には、QPCTimer ではなく、このクラスを使用する。
11 public class SoundTimer
14 /// エラー時は double.NaN を返す。
16 public double 現在のデバイス位置secを取得する( CSCore.CoreAudioAPI.AudioClock audioClock )
21 long デバイス周波数 = 0;// audioClock.Pu64Frequency;
22 audioClock.GetFrequencyNative( out デバイス周波数 );
23 long QPC周波数 = FDK.カウンタ.QPCTimer.周波数;
25 long デバイス位置取得時のパフォーマンスカウンタを100ns単位に変換した時間 = 0;
30 // IAudioClock::GetPosition() は、S_FALSE を返すことがある。
31 // これは、WASAPI排他モードにおいて、GetPosition 時に優先度の高いイベントが発生しており
32 // 既定時間内にデバイス位置を取得できなかった場合に返される。(MSDNより)
33 for( int リトライ回数 = 0; リトライ回数 < 10; リトライ回数++ ) // 最大10回までリトライ。
35 hr = audioClock.GetPositionNative( out デバイス位置, out デバイス位置取得時のパフォーマンスカウンタを100ns単位に変換した時間 );
37 if( ( (int) CSCore.Win32.HResult.S_OK ) == hr )
41 else if( ( (int) CSCore.Win32.HResult.S_FALSE ) == hr )
47 throw new CSCore.Win32.Win32ComException( hr, "IAudioClock", "GetPosition" );
50 if( ( (int) CSCore.Win32.HResult.S_FALSE ) == hr )
52 // 全部リトライしてもまだダメならエラー。
56 // デバイス位置は、GetPosition() で取得した瞬間からここに至るまでに、既にいくらか進んでいる。
57 // そこで、この時点で最新のパフォーマンスカウンタを取得し、時間の増加分をデバイス位置に加えて精度を上げる。(MSDNより)
58 double QPCから調べた差分sec =
59 ( (double) FDK.カウンタ.QPCTimer.生カウント / QPC周波数 ) -
60 ( (double) デバイス位置取得時のパフォーマンスカウンタを100ns単位に変換した時間 ) / 10000000.0;
62 return ( (double) デバイス位置 ) / デバイス周波数 + QPCから調べた差分sec;
66 private readonly object スレッド間同期 = new object();