OSDN Git Service

#37177 キーアサインでPOVを登録すると、同じ方向のHATが消える問題を修正。
[dtxmania/dtxmania.git] / FDK17プロジェクト / コード / 03.サウンド / CSoundDeviceDirectSound.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.Text;\r
4 using System.Diagnostics;\r
5 using System.IO;\r
6 using System.Threading;\r
7 using SharpDX;\r
8 using SharpDX.DirectSound;\r
9 \r
10 namespace FDK\r
11 {\r
12         public class CSoundDeviceDirectSound : ISoundDevice\r
13         {\r
14                 // プロパティ\r
15 \r
16                 public ESoundDeviceType e出力デバイス\r
17                 {\r
18                         get;\r
19                         protected set;\r
20                 }\r
21                 public long n実出力遅延ms\r
22                 {\r
23                         get;\r
24                         protected set;\r
25                 }\r
26                 public long n実バッファサイズms\r
27                 {\r
28                         get;\r
29                         protected set;\r
30                 }\r
31 \r
32                 public static readonly BufferFlags DefaultFlags = BufferFlags.Defer | BufferFlags.GetCurrentPosition2 | BufferFlags.GlobalFocus | BufferFlags.ControlVolume | BufferFlags.ControlPan | BufferFlags.ControlFrequency;\r
33 \r
34                 // CSoundTimer 用に公開しているプロパティ\r
35 \r
36                 public long n経過時間ms\r
37                 {\r
38                         get\r
39                         {\r
40                                 if ( ctimer != null )\r
41                                 {\r
42                                         this.sd経過時間計測用サウンドバッファ.DirectSoundBuffer.GetCurrentPosition( out int n現在位置, out _ );\r
43                                         long n現在のシステム時刻ms = this.tmシステムタイマ.nシステム時刻ms;\r
44 \r
45 \r
46                                         // ループ回数を調整。\r
47 \r
48                                         long nシステム時刻での間隔ms = n現在のシステム時刻ms - this.n前に経過時間を測定したシステム時刻ms;\r
49 \r
50                                         while ( nシステム時刻での間隔ms >= n単位繰り上げ間隔ms )              // 前回から単位繰り上げ間隔以上経過してるなら確実にループしている。誤差は大きくないだろうから無視。\r
51                                         {\r
52                                                 this.nループ回数++;\r
53                                                 nシステム時刻での間隔ms -= n単位繰り上げ間隔ms;\r
54                                         }\r
55 \r
56                                         if ( n現在位置 < this.n前回の位置 )                                                    // 単位繰り上げ間隔以内であっても、現在位置が前回より手前にあるなら1回ループしている。\r
57                                                 this.nループ回数++;\r
58 \r
59 \r
60                                         // 経過時間を算出。\r
61 \r
62                                         long n経過時間ms = (long) ( ( this.nループ回数 * n単位繰り上げ間隔ms ) + ( n現在位置 * 1000.0 / ( 44100.0 * 2 * 2 ) ) );\r
63 \r
64 \r
65                                         // 今回の値を次回に向けて保存。\r
66 \r
67                                         this.n前に経過時間を測定したシステム時刻ms = n現在のシステム時刻ms;\r
68                                         this.n前回の位置 = n現在位置;\r
69 \r
70                                         return n経過時間ms;\r
71                                 }\r
72                                 else\r
73                                 {\r
74                                         long nRet = ctimer.nシステム時刻ms - this.n前に経過時間を測定したシステム時刻ms;\r
75                                         if ( nRet < 0 ) // カウンタがループしたときは\r
76                                         {\r
77                                                 nRet = ( ctimer.nシステム時刻 - long.MinValue ) + ( long.MaxValue - this.n前に経過時間を測定したシステム時刻ms ) + 1;\r
78                                         }\r
79                                         this.n前に経過時間を測定したシステム時刻ms = ctimer.nシステム時刻ms;\r
80         \r
81                                         return nRet;\r
82                                 }\r
83                         }\r
84                 }\r
85                 public long n経過時間を更新したシステム時刻ms\r
86                 {\r
87                         get { throw new NotImplementedException(); }\r
88                 }\r
89                 public CTimer tmシステムタイマ\r
90                 {\r
91                         get;\r
92                         protected set;\r
93                 }\r
94 \r
95                 public int nMasterVolume\r
96                 {\r
97                         get\r
98                         {\r
99                                 return (int) 100;\r
100                         }\r
101                         set\r
102                         {\r
103                                 // 特に何もしない\r
104                         }\r
105                 }\r
106 \r
107 \r
108                 // メソッド\r
109 \r
110                 public CSoundDeviceDirectSound( IntPtr hWnd, long n遅延時間ms )\r
111                 {\r
112                         t初期化( hWnd, n遅延時間ms, false );\r
113                 }\r
114                 public CSoundDeviceDirectSound( IntPtr hWnd, long n遅延時間ms, bool bUseOSTimer )\r
115                 {\r
116                         t初期化( hWnd, n遅延時間ms, bUseOSTimer );\r
117                 }\r
118                 private void t初期化( IntPtr hWnd, long n遅延時間ms, bool bUseOSTimer )\r
119                 {\r
120                         Trace.TraceInformation( "DirectSound の初期化を開始します。" );\r
121 \r
122                         this.e出力デバイス = ESoundDeviceType.Unknown;\r
123                         this.n実バッファサイズms = this.n実出力遅延ms = n遅延時間ms;\r
124                         this.tmシステムタイマ = new CTimer( CTimer.E種別.MultiMedia );\r
125 \r
126                         #region [ DirectSound デバイスを作成する。]\r
127                         //-----------------\r
128                         this.DirectSound = new DirectSound();   // 失敗したら例外をそのまま発出。\r
129 \r
130                         // デバイスの協調レベルを設定する。\r
131 \r
132                         bool priority = true;\r
133                         try\r
134                         {\r
135                                 this.DirectSound.SetCooperativeLevel( hWnd, CooperativeLevel.Priority );\r
136                         }\r
137                         catch\r
138                         {\r
139                                 this.DirectSound.SetCooperativeLevel( hWnd, CooperativeLevel.Normal );  // これでも失敗したら例外をそのまま発出。\r
140                                 priority = false;\r
141                         }\r
142 \r
143                         // デバイス作成完了。\r
144 \r
145                         this.e出力デバイス = ESoundDeviceType.DirectSound;\r
146                         //-----------------\r
147                         #endregion\r
148 \r
149                         if ( !bUseOSTimer )\r
150                         {\r
151                                 #region [ 経過時間計測用サウンドバッファを作成し、ループ再生を開始する。]\r
152                                 //-----------------\r
153 \r
154                                 // 単位繰り上げ間隔[秒]の長さを持つ無音のサウンドを作成。\r
155 \r
156                                 uint nデータサイズbyte = n単位繰り上げ間隔sec * 44100 * 2 * 2;\r
157                                 var ms = new MemoryStream();\r
158                                 var bw = new BinaryWriter( ms );\r
159                                 bw.Write( (uint) 0x46464952 );                                          // 'RIFF'\r
160                                 bw.Write( (uint) ( 44 + nデータサイズbyte - 8 ) );        // ファイルサイズ - 8\r
161                                 bw.Write( (uint) 0x45564157 );                                          // 'WAVE'\r
162                                 bw.Write( (uint) 0x20746d66 );                                          // 'fmt '\r
163                                 bw.Write( (uint) 16 );                                                          // バイト数\r
164                                 bw.Write( (ushort) 1 );                                                         // フォーマットID(リニアPCM)\r
165                                 bw.Write( (ushort) 2 );                                                         // チャンネル数\r
166                                 bw.Write( (uint) 44100 );                                                       // サンプリング周波数\r
167                                 bw.Write( (uint) ( 44100 * 2 * 2 ) );                           // bytes/sec\r
168                                 bw.Write( (ushort) ( 2 * 2 ) );                                         // blockサイズ\r
169                                 bw.Write( (ushort) 16 );                                                        // bit/sample\r
170                                 bw.Write( (uint) 0x61746164 );                                          // 'data'\r
171                                 bw.Write( (uint) nデータサイズbyte );                             // データ長\r
172                                 for ( int i = 0; i < nデータサイズbyte / sizeof( long ); i++ )    // PCMデータ\r
173                                         bw.Write( (long) 0 );\r
174                                 var byArrWaveFleImage = ms.ToArray();\r
175                                 bw.Close();\r
176                                 ms = null;\r
177                                 bw = null;\r
178                                 this.sd経過時間計測用サウンドバッファ = this.tサウンドを作成する( byArrWaveFleImage );\r
179                                 CSound.listインスタンス.Remove( this.sd経過時間計測用サウンドバッファ );   // 特殊用途なのでインスタンスリストからは除外する。\r
180 \r
181                                 // サウンドのループ再生開始。\r
182 \r
183                                 this.nループ回数 = 0;\r
184                                 this.n前回の位置 = 0;\r
185                                 this.sd経過時間計測用サウンドバッファ.DirectSoundBuffer.Play( 0, PlayFlags.Looping );\r
186                                 this.n前に経過時間を測定したシステム時刻ms = this.tmシステムタイマ.nシステム時刻ms;\r
187                                 //-----------------\r
188                                 #endregion\r
189                         }\r
190                         else\r
191                         {\r
192                                 ctimer = new CTimer( CTimer.E種別.MultiMedia );\r
193                         }\r
194                         Trace.TraceInformation( "DirectSound を初期化しました。({0})({1})", ( priority ) ? "Priority" : "Normal", bUseOSTimer? "OStimer" : "FDKtimer" );\r
195                 }\r
196 \r
197                 public CSound tサウンドを作成する( string strファイル名 )\r
198                 {\r
199                         var sound = new CSound();\r
200                         sound.tDirectSoundサウンドを作成する( strファイル名, this.DirectSound );\r
201                         return sound;\r
202                 }\r
203                 public CSound tサウンドを作成する( byte[] byArrWAVファイルイメージ )\r
204                 {\r
205                         var sound = new CSound();\r
206                         sound.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, this.DirectSound );\r
207                         return sound;\r
208                 }\r
209                 public CSound tサウンドを作成する( byte[] byArrWAVファイルイメージ, BufferFlags flags )\r
210                 {\r
211                         var sound = new CSound();\r
212                         sound.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, this.DirectSound, flags );\r
213                         return sound;\r
214                 }\r
215 \r
216                 // 既存のインスタンス(生成直後 or Dispose済み)に対してサウンドを生成する。\r
217                 public void tサウンドを作成する( string strファイル名, ref CSound sound )\r
218                 {\r
219                         sound.tDirectSoundサウンドを作成する( strファイル名, this.DirectSound );\r
220                 }\r
221                 public void tサウンドを作成する( byte[] byArrWAVファイルイメージ, ref CSound sound )\r
222                 {\r
223                         sound.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, this.DirectSound );\r
224                 }\r
225                 public void tサウンドを作成する( byte[] byArrWAVファイルイメージ, BufferFlags flags, ref CSound sound )\r
226                 {\r
227                         sound.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, this.DirectSound, flags );\r
228                 }\r
229 \r
230                 #region [ Dispose-Finallizeパターン実装 ]\r
231                 //-----------------\r
232                 public void Dispose()\r
233                 {\r
234                         this.Dispose( true );\r
235                         GC.SuppressFinalize( this );\r
236                 }\r
237                 protected void Dispose( bool bManagedDispose )\r
238                 {\r
239                         this.e出力デバイス = ESoundDeviceType.Unknown;            // まず出力停止する(Dispose中にクラス内にアクセスされることを防ぐ)\r
240                         if ( bManagedDispose )\r
241                         {\r
242                                 #region [ 経緯時間計測用サウンドバッファを解放。]\r
243                                 //-----------------\r
244                                 if ( this.sd経過時間計測用サウンドバッファ != null )\r
245                                 {\r
246                                         this.sd経過時間計測用サウンドバッファ.tサウンドを停止する();\r
247                                         C共通.tDisposeする( ref this.sd経過時間計測用サウンドバッファ );\r
248                                 }\r
249                                 //-----------------\r
250                                 #endregion\r
251                                 #region [ 単位繰り上げ用スレッド停止。]\r
252                                 //-----------------\r
253                                 if( this.th経過時間測定用スレッド != null )\r
254                                 {\r
255                                         this.th経過時間測定用スレッド.Abort();\r
256                                         this.th経過時間測定用スレッド = null;\r
257                                 \r
258                                 }\r
259                                 //-----------------\r
260                                 #endregion\r
261 \r
262                                 C共通.tDisposeする( ref this.DirectSound );\r
263                                 C共通.tDisposeする( this.tmシステムタイマ );\r
264                         }\r
265                         if ( ctimer != null )\r
266                         {\r
267                                 C共通.tDisposeする( ref this.ctimer );\r
268                         }\r
269                 }\r
270                 ~CSoundDeviceDirectSound()\r
271                 {\r
272                         this.Dispose( false );\r
273                 }\r
274                 //-----------------\r
275                 #endregion\r
276 \r
277                 protected DirectSound DirectSound = null;\r
278                 protected CSound sd経過時間計測用サウンドバッファ = null;\r
279                 protected Thread th経過時間測定用スレッド = null;\r
280 //              protected AutoResetEvent autoResetEvent = new AutoResetEvent( false );\r
281                 protected const uint n単位繰り上げ間隔sec = 1;  // [秒]\r
282                 protected const uint n単位繰り上げ間隔ms = n単位繰り上げ間隔sec * 1000; // [ミリ秒]\r
283                 protected int nループ回数 = 0;\r
284 \r
285                 private long n前に経過時間を測定したシステム時刻ms = CTimer.n未使用;\r
286                 private int n前回の位置 = 0;\r
287 \r
288                 private CTimer ctimer = null;\r
289         }\r
290 }\r