OSDN Git Service

bc62ad4500b94207b0068d9ada72cee3c8bf21dd
[strokestylet/CsWin10Desktop3.git] / FDK24 / メディア / サウンド / WASAPI / SoundTimer.cs
1 using System;
2
3 namespace FDK.メディア.サウンド.WASAPI
4 {
5         /// <summary>
6         /// WASAPIデバイス(のIAudioClock)をソースとするタイマー。
7         /// </summary>
8         /// <remarks>
9         /// WASAPIデバイスと厳密に同期をとる場合には、QPCTimer ではなく、このクラスを使用する。
10         /// </remarks>
11         public class SoundTimer
12         {
13                 /// <summary>
14                 /// エラー時は double.NaN を返す。
15                 /// </summary>
16                 public double 現在のデバイス位置secを取得する( CSCore.CoreAudioAPI.AudioClock audioClock )
17                 {
18                         lock( this.スレッド間同期 )
19                         {
20                                 int hr = 0;
21                                 long デバイス周波数 = 0;// audioClock.Pu64Frequency;
22                                 audioClock.GetFrequencyNative( out デバイス周波数 );
23                                 long QPC周波数 = FDK.カウンタ.QPCTimer.周波数;
24                                 long デバイス位置 = 0;
25                                 long デバイス位置取得時のパフォーマンスカウンタを100ns単位に変換した時間 = 0;
26
27                                 if( 0.0 >= デバイス周波数 )
28                                         return double.NaN;
29
30                                 // IAudioClock::GetPosition() は、S_FALSE を返すことがある。
31                                 // これは、WASAPI排他モードにおいて、GetPosition 時に優先度の高いイベントが発生しており
32                                 // 既定時間内にデバイス位置を取得できなかった場合に返される。(MSDNより)
33                                 for( int リトライ回数 = 0; リトライ回数 < 10; リトライ回数++ )    // 最大10回までリトライ。
34                                 {
35                                         hr = audioClock.GetPositionNative( out デバイス位置, out デバイス位置取得時のパフォーマンスカウンタを100ns単位に変換した時間 );
36
37                                         if( ( (int) CSCore.Win32.HResult.S_OK ) == hr )
38                                         {
39                                                 break;      // OK
40                                         }
41                                         else if( ( (int) CSCore.Win32.HResult.S_FALSE ) == hr )
42                                         {
43                                                 continue;   // リトライ
44                                         }
45                                         else
46                                         {
47                                                 throw new CSCore.Win32.Win32ComException( hr, "IAudioClock", "GetPosition" );
48                                         }
49                                 }
50                                 if( ( (int) CSCore.Win32.HResult.S_FALSE ) == hr )
51                                 {
52                                         // 全部リトライしてもまだダメならエラー。
53                                         return double.NaN;
54                                 }
55
56                                 // デバイス位置は、GetPosition() で取得した瞬間からここに至るまでに、既にいくらか進んでいる。
57                                 // そこで、この時点で最新のパフォーマンスカウンタを取得し、時間の増加分をデバイス位置に加えて精度を上げる。(MSDNより)
58                                 double QPCから調べた差分sec =
59                                         ( (double) FDK.カウンタ.QPCTimer.生カウント / QPC周波数 ) -
60                                         ( (double) デバイス位置取得時のパフォーマンスカウンタを100ns単位に変換した時間 ) / 10000000.0;
61
62                                 return ( (double) デバイス位置 ) / デバイス周波数 + QPCから調べた差分sec;
63                         }
64                 }
65
66                 private readonly object スレッド間同期 = new object();
67         }
68 }