OSDN Git Service

#30818 WASAPIのバッファ更新間隔を、サウンドデバイスに設定できる最小値にすることで、バッファサイズをより小さくできるようにした。(CSoundDeviceWA...
[dtxmania/dtxmania.git] / FDK17プロジェクト / コード / 03.サウンド / CSound.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.Text;\r
4 using System.Diagnostics;\r
5 using System.Runtime.InteropServices;\r
6 using System.IO;\r
7 using System.Runtime.CompilerServices;\r
8 using System.Threading;\r
9 using SlimDX;\r
10 using SlimDX.DirectSound;\r
11 using SlimDX.Multimedia;\r
12 using Un4seen.Bass;\r
13 using Un4seen.BassAsio;\r
14 using Un4seen.BassWasapi;\r
15 using Un4seen.Bass.AddOn.Mix;\r
16 using DirectShowLib;\r
17 \r
18 namespace FDK\r
19 {\r
20         #region [ DTXMania用拡張 ]\r
21         public class CSound管理       // : CSound\r
22         {\r
23                 public static ISoundDevice SoundDevice\r
24                 {\r
25                         get; set;\r
26                 }\r
27                 public static ESoundDeviceType SoundDeviceType\r
28                 {\r
29                         get; set;\r
30                 }\r
31                 public static CSoundTimer rc演奏用タイマ = null;\r
32 \r
33                 public static IntPtr WindowHandle;\r
34 \r
35                 public static int nMixing = 0;\r
36                 public int GetMixingStreams()\r
37                 {\r
38                         return nMixing;\r
39                 }\r
40                 public static int nStreams = 0;\r
41                 public int GetStreams()\r
42                 {\r
43                         return nStreams;\r
44                 }\r
45                 #region [ WASAPI/ASIO/DirectSound設定値 ]\r
46                 /// <summary>\r
47                 /// <para>WASAPI 排他モード出力における再生遅延[ms](の希望値)。最終的にはこの数値を基にドライバが決定する)。</para>\r
48                 /// </summary>\r
49                 public static int SoundDelayExclusiveWASAPI = 0;                // SSTでは、50ms\r
50                 public int GetSoundExclusiveWASAPI()\r
51                 {\r
52                         return SoundDelayExclusiveWASAPI;\r
53                 }\r
54                 public void SetSoundDelayExclusiveWASAPI( int value )\r
55                 {\r
56                         SoundDelayExclusiveWASAPI = value;\r
57                 }\r
58                 /// <summary>\r
59                 /// <para>WASAPI 共有モード出力における再生遅延[ms]。ユーザが決定する。</para>\r
60                 /// </summary>\r
61                 public static int SoundDelaySharedWASAPI = 100;\r
62                 /// <summary>\r
63                 /// <para>排他WASAPIバッファの更新間隔。出力間隔ではないので注意。</para>\r
64                 /// <para>SoundDelay よりも小さい値であること。(小さすぎる場合はBASSによって自動修正される。)</para>\r
65                 /// </summary>\r
66                 public static int SoundUpdatePeriodExclusiveWASAPI = 6;\r
67                 /// <summary>\r
68                 /// <para>共有WASAPIバッファの更新間隔。出力間隔ではないので注意。</para>\r
69                 /// <para>SoundDelay よりも小さい値であること。(小さすぎる場合はBASSによって自動修正される。)</para>\r
70                 /// </summary>\r
71                 public static int SoundUpdatePeriodSharedWASAPI = 6;\r
72                 ///// <summary>\r
73                 ///// <para>ASIO 出力における再生遅延[ms](の希望値)。最終的にはこの数値を基にドライバが決定する)。</para>\r
74                 ///// </summary>\r
75                 //public static int SoundDelayASIO = 0;                                 // SSTでは50ms。0にすると、デバイスの設定値をそのまま使う。\r
76                 /// <summary>\r
77                 /// <para>ASIO 出力におけるバッファサイズ。</para>\r
78                 /// </summary>\r
79                 public static int SoundDelayASIO = 0;                                           // 0にすると、デバイスの設定値をそのまま使う。\r
80                 public int GetSoundDelayASIO()\r
81                 {\r
82                         return SoundDelayASIO;\r
83                 }\r
84                 public void SetSoundDelayASIO(int value)\r
85                 {\r
86                         SoundDelayASIO = value;\r
87                 }\r
88                 public static int ASIODevice = 0;\r
89                 public int GetASIODevice()\r
90                 {\r
91                         return ASIODevice;\r
92                 }\r
93                 public void SetASIODevice(int value)\r
94                 {\r
95                         ASIODevice = value;\r
96                 }\r
97                 /// <summary>\r
98                 /// <para>DirectSound 出力における再生遅延[ms]。ユーザが決定する。</para>\r
99                 /// </summary>\r
100                 public static int SoundDelayDirectSound = 100;\r
101 \r
102                 public long GetSoundDelay()\r
103                 {\r
104                         if ( SoundDevice != null )\r
105                         {\r
106                                 return SoundDevice.n実バッファサイズms;\r
107                         }\r
108                         else\r
109                         {\r
110                                 return -1;\r
111                         }\r
112                 }\r
113 \r
114                 #endregion\r
115                 public static int hTempoStream;\r
116 \r
117 \r
118         /// <summary>\r
119         /// コンストラクタ\r
120         /// </summary>\r
121         /// <param name="handle"></param>\r
122                 public CSound管理( IntPtr handle )    // #30803 従来のコンストラクタ相当のI/Fを追加。(DTXC用)\r
123                 {\r
124                         WindowHandle = handle;\r
125                         SoundDevice = null;\r
126                         t初期化( ESoundDeviceType.DirectSound, 0, 0, 0 );\r
127                 }\r
128                 public CSound管理( IntPtr handle, ESoundDeviceType soundDeviceType, int nSoundDelayExclusiveWASAPI, int nSoundDelayASIO, int nASIODevice )\r
129                 {\r
130                         WindowHandle = handle;\r
131                         SoundDevice = null;\r
132                         t初期化( soundDeviceType, nSoundDelayExclusiveWASAPI, nSoundDelayASIO, nASIODevice );\r
133                 }\r
134                 public void Dispose()\r
135                 {\r
136                         t終了();\r
137                 }\r
138 \r
139                 public static void t初期化()\r
140                 {\r
141                         t初期化( ESoundDeviceType.DirectSound, 0, 0, 0 );\r
142                 }\r
143 \r
144 \r
145                 public static void t初期化( ESoundDeviceType soundDeviceType, int _nSoundDelayExclusiveWASAPI, int _nSoundDelayASIO, int _nASIODevice )\r
146                 {\r
147                         //SoundDevice = null;                                           // 後で再初期化することがあるので、null初期化はコンストラクタに回す\r
148                         rc演奏用タイマ = null;                                            // Global.Bass 依存(つまりユーザ依存)\r
149                         nMixing = 0;\r
150 \r
151                         SoundDelayExclusiveWASAPI = _nSoundDelayExclusiveWASAPI;\r
152                         SoundDelayASIO = _nSoundDelayASIO;\r
153                         ASIODevice = _nASIODevice;\r
154 \r
155                         ESoundDeviceType[] ESoundDeviceTypes = new ESoundDeviceType[ 4 ]\r
156                         {\r
157                                 ESoundDeviceType.ExclusiveWASAPI,\r
158                                 ESoundDeviceType.ASIO,\r
159                                 ESoundDeviceType.DirectSound,\r
160                                 ESoundDeviceType.Unknown\r
161                         };\r
162 \r
163                         int n初期デバイス;\r
164                         switch ( soundDeviceType )\r
165                         {\r
166                                 case ESoundDeviceType.ExclusiveWASAPI:\r
167                                         n初期デバイス = 0;\r
168                                         break;\r
169                                 case ESoundDeviceType.ASIO:\r
170                                         n初期デバイス = 1;\r
171                                         break;\r
172                                 case ESoundDeviceType.DirectSound:\r
173                                         n初期デバイス = 2;\r
174                                         break;\r
175                                 default:\r
176                                         n初期デバイス = 3;\r
177                                         break;\r
178                         }\r
179                         for ( SoundDeviceType = ESoundDeviceTypes[ n初期デバイス ]; ; SoundDeviceType = ESoundDeviceTypes[ ++n初期デバイス ] )\r
180                         {\r
181                                 try\r
182                                 {\r
183                                         t現在のユーザConfigに従ってサウンドデバイスとすべての既存サウンドを再構築する();\r
184                                         break;\r
185                                 }\r
186                                 catch ( Exception e )\r
187                                 {\r
188                                         Trace.TraceInformation( e.Message );\r
189                                         if ( ESoundDeviceTypes[ n初期デバイス ] == ESoundDeviceType.Unknown )\r
190                                         {\r
191                                                 throw new Exception( string.Format( "サウンドデバイスの初期化に失敗しました。" ) );\r
192                                         }\r
193                                 }\r
194                         }\r
195                 }\r
196 \r
197 \r
198                 public static void t終了()\r
199                 {\r
200                         C共通.tDisposeする( SoundDevice ); SoundDevice = null;\r
201                         C共通.tDisposeする( ref rc演奏用タイマ );     // Global.Bass を解放した後に解放すること。(Global.Bass で参照されているため)\r
202                 }\r
203 \r
204 \r
205                 public static void t現在のユーザConfigに従ってサウンドデバイスとすべての既存サウンドを再構築する()\r
206                 {\r
207                         #region [ すでにサウンドデバイスと演奏タイマが構築されていれば解放する。]\r
208                         //-----------------\r
209                         if ( SoundDevice != null )\r
210                         {\r
211                                 // すでに生成済みのサウンドがあれば初期状態に戻す。\r
212 \r
213                                 CSound.tすべてのサウンドを初期状態に戻す();             // リソースは解放するが、CSoundのインスタンスは残す。\r
214 \r
215 \r
216                                 // サウンドデバイスと演奏タイマを解放する。\r
217 \r
218                                 C共通.tDisposeする( SoundDevice ); SoundDevice = null;\r
219                                 C共通.tDisposeする( ref rc演奏用タイマ );     // Global.SoundDevice を解放した後に解放すること。(Global.SoundDevice で参照されているため)\r
220                         }\r
221                         //-----------------\r
222                         #endregion\r
223 \r
224                         #region [ 新しいサウンドデバイスを構築する。]\r
225                         //-----------------\r
226                         switch ( SoundDeviceType )\r
227                         {\r
228                                 case ESoundDeviceType.ExclusiveWASAPI:\r
229                                         SoundDevice = new CSoundDeviceWASAPI( CSoundDeviceWASAPI.Eデバイスモード.排他, SoundDelayExclusiveWASAPI, SoundUpdatePeriodExclusiveWASAPI );\r
230                                         break;\r
231 \r
232                                 case ESoundDeviceType.SharedWASAPI:\r
233                                         SoundDevice = new CSoundDeviceWASAPI( CSoundDeviceWASAPI.Eデバイスモード.共有, SoundDelaySharedWASAPI, SoundUpdatePeriodSharedWASAPI );\r
234                                         break;\r
235 \r
236                                 case ESoundDeviceType.ASIO:\r
237                                         SoundDevice = new CSoundDeviceASIO( SoundDelayASIO, ASIODevice );\r
238                                         break;\r
239 \r
240                                 case ESoundDeviceType.DirectSound:\r
241                                         SoundDevice = new CSoundDeviceDirectSound( WindowHandle, SoundDelayDirectSound );\r
242                                         break;\r
243 \r
244                                 default:\r
245                                         throw new Exception( string.Format( "未対応の SoundDeviceType です。[{0}]", SoundDeviceType.ToString() ) );\r
246                         }\r
247                         //-----------------\r
248                         #endregion\r
249                         #region [ 新しい演奏タイマを構築する。]\r
250                         //-----------------\r
251                         rc演奏用タイマ = new CSoundTimer( SoundDevice );\r
252                         //-----------------\r
253                         #endregion\r
254                         if ( SoundDevice.e出力デバイス != ESoundDeviceType.DirectSound && SoundDevice.e出力デバイス != ESoundDeviceType.Unknown )\r
255                         {\r
256                                 hTempoStream = SoundDevice.hTempoStream;\r
257                         }\r
258                         \r
259 \r
260                         CSound.tすべてのサウンドを再構築する( SoundDevice );              // すでに生成済みのサウンドがあれば作り直す。\r
261                 }\r
262                 public CSound tサウンドを生成する( string filename )\r
263                 {\r
264                         if ( SoundDeviceType == ESoundDeviceType.Unknown )\r
265                         {\r
266                                 throw new Exception( string.Format( "未対応の SoundDeviceType です。[{0}]", SoundDeviceType.ToString() ) );\r
267                         }\r
268                         return SoundDevice.tサウンドを作成する( filename );\r
269                 }\r
270 \r
271                 public void t再生中の処理をする( object o )                    // #26122 2011.9.1 yyagi; delegate経由の呼び出し用\r
272                 {\r
273                         t再生中の処理をする();\r
274                 }\r
275                 public void t再生中の処理をする()\r
276                 {\r
277 //★★★★★★★★★★★★★★★★★★★★★ダミー★★★★★★★★★★★★★★★★★★\r
278 //                      Debug.Write( "再生中の処理をする()" );\r
279                 }\r
280 \r
281                 public void tサウンドを破棄する( CSound csound )\r
282                 {\r
283                         csound.t解放する( true );                   // インスタンスは存続→破棄にする。\r
284                         csound = null;\r
285                 }\r
286 \r
287                 public float GetCPUusage()\r
288                 {\r
289                         float f;\r
290                         switch ( SoundDeviceType )\r
291                         {\r
292                                 case ESoundDeviceType.ExclusiveWASAPI:\r
293                                 case ESoundDeviceType.SharedWASAPI:\r
294                                         f = BassWasapi.BASS_WASAPI_GetCPU();\r
295                                         break;\r
296                                 case ESoundDeviceType.ASIO:\r
297                                         f = BassAsio.BASS_ASIO_GetCPU();\r
298                                         break;\r
299                                 case ESoundDeviceType.DirectSound:\r
300                                         f = 0.0f;\r
301                                         break;\r
302                                 default:\r
303                                         f = 0.0f;\r
304                                         break;\r
305                         }\r
306                         return f;\r
307                 }\r
308 \r
309                 public string GetCurrentSoundDeviceType()\r
310                 {\r
311                         switch ( SoundDeviceType )\r
312                         {\r
313                                 case ESoundDeviceType.ExclusiveWASAPI:\r
314                                 case ESoundDeviceType.SharedWASAPI:\r
315                                         return "WASAPI";\r
316                                 case ESoundDeviceType.ASIO:\r
317                                         return "ASIO";\r
318                                 case ESoundDeviceType.DirectSound:\r
319                                         return "DirectSound";\r
320                                 default:\r
321                                         return "Unknown";\r
322                         }\r
323                 }\r
324 \r
325                 public void AddMixer( CSound cs )\r
326                 {\r
327                         cs.tBASSサウンドをミキサーに追加する();\r
328                 }\r
329                 public void RemoveMixer( CSound cs )\r
330                 {\r
331                         cs.tBASSサウンドをミキサーから削除する();\r
332                 }\r
333 }\r
334         #endregion\r
335 \r
336         // CSound は、サウンドデバイスが変更されたときも、インスタンスを再作成することなく、新しいデバイスで作り直せる必要がある。\r
337         // そのため、デバイスごとに別のクラスに分割するのではなく、1つのクラスに集約するものとする。\r
338 \r
339         public class CSound : IDisposable, ICloneable\r
340         {\r
341                 #region [ DTXMania用拡張 ]\r
342                 public int n総演奏時間ms\r
343                 {\r
344                         get;\r
345                         private set;\r
346                 }\r
347                 public int nサウンドバッファサイズ           // 取りあえず0固定★★★★★★★★★★★★★★★★★★★★\r
348                 {\r
349                         get { return 0; }\r
350                 }\r
351                 public bool bストリーム再生する                        // 取りあえずfalse固定★★★★★★★★★★★★★★★★★★★★\r
352                                                                                                 // trueにすると同一チップ音の多重再生で問題が出る(4POLY音源として動かない)\r
353                 {\r
354                         get { return false; }\r
355                 }\r
356                 public double db周波数倍率\r
357                 {\r
358                         get\r
359                         {\r
360                                 return _db周波数倍率;\r
361                         }\r
362                         set\r
363                         {\r
364                                 if ( _db周波数倍率 != value )\r
365                                 {\r
366                                         _db周波数倍率 = value;\r
367                                         if ( bBASSサウンドである )\r
368                                         {\r
369                                                 Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ( float ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 ) );\r
370                                         }\r
371                                         else\r
372                                         {\r
373                                                 if ( b再生中 )\r
374                                                 {\r
375                                                         this.Buffer.Frequency = ( int ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 );\r
376                                                 }\r
377                                         }\r
378                                 }\r
379                         }\r
380                 }\r
381                 public double db再生速度\r
382                 {\r
383                         get\r
384                         {\r
385                                 return _db再生速度;\r
386                         }\r
387                         set\r
388                         {\r
389                                 if ( _db再生速度 != value )\r
390                                 {\r
391                                         _db再生速度 = value;\r
392                                         if ( bBASSサウンドである )\r
393                                         {\r
394                                                 Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ( float ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 ) );\r
395                                                 //Bass.BASS_ChannelSetAttribute( CSound管理.hTempoStream, BASSAttribute.BASS_ATTRIB_TEMPO, (float) (db再生速度 * 100 - 100 ) );\r
396                                         }\r
397                                         else\r
398                                         {\r
399                                                 if ( b再生中 )\r
400                                                 {\r
401                                                         this.Buffer.Frequency = ( int ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 );\r
402                                                 }\r
403                                         }\r
404                                 }\r
405                         }\r
406                 }\r
407                 #endregion\r
408 \r
409                 private STREAMPROC _cbStreamXA;         // make it global, so that the GC can not remove it\r
410 //              private SYNCPROC _cbEndofStream;        // ストリームの終端まで再生されたときに呼び出されるコールバック\r
411 //              private WaitCallback _cbRemoveMixerChannel;\r
412 \r
413                 /// <summary>\r
414                 /// <para>0:最小~100:原音</para>\r
415                 /// </summary>\r
416                 public int n音量\r
417                 {\r
418                         get\r
419                         {\r
420                                 if( this.bBASSサウンドである )\r
421                                 {\r
422                                         float f音量 = 0.0f;\r
423                                         if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_VOL, ref f音量 ) )\r
424                                         //if ( BassMix.BASS_Mixer_ChannelGetEnvelopePos( this.hBassStream, BASSMIXEnvelope.BASS_MIXER_ENV_VOL, ref f音量 ) == -1 )\r
425                                             return 100;\r
426                                         return (int) ( f音量 * 100 );\r
427                                 }\r
428                                 else if( this.bDirectSoundである )\r
429                                 {\r
430                                         return this._n音量;\r
431                                 }\r
432                                 return -1;\r
433                         }\r
434                         set\r
435                         {\r
436                                 if( this.bBASSサウンドである )\r
437                                 {\r
438                                         float f音量 = Math.Min( Math.Max( value, 0 ), 100 ) / 100.0f; // 0~100 → 0.0~1.0\r
439                                         //var nodes = new BASS_MIXER_NODE[ 1 ] { new BASS_MIXER_NODE( 0, f音量 ) };\r
440                                         //BassMix.BASS_Mixer_ChannelSetEnvelope( this.hBassStream, BASSMIXEnvelope.BASS_MIXER_ENV_VOL, nodes );\r
441                                         Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_VOL, f音量 );\r
442 \r
443                                 }\r
444                                 else if( this.bDirectSoundである )\r
445                                 {\r
446                                         this._n音量 = value;\r
447 \r
448                                         if( this._n音量 == 0 )\r
449                                         {\r
450                                                 this._n音量db = -10000;\r
451                                         }\r
452                                         else\r
453                                         {\r
454                                                 this._n音量db = (int) ( ( 20.0 * Math.Log10( ( (double) this._n音量 ) / 100.0 ) ) * 100.0 );\r
455                                         }\r
456 \r
457                                         this.Buffer.Volume = this._n音量db;\r
458                                 }\r
459                         }\r
460                 }\r
461 \r
462                 /// <summary>\r
463                 /// <para>左:-100~中央:0~100:右。set のみ。</para>\r
464                 /// </summary>\r
465                 public int n位置\r
466                 {\r
467                         get\r
468                         {\r
469                                 if( this.bBASSサウンドである )\r
470                                 {\r
471                                         float f位置 = 0.0f;\r
472                                         if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_PAN, ref f位置 ) )\r
473                                                 //if( BassMix.BASS_Mixer_ChannelGetEnvelopePos( this.hBassStream, BASSMIXEnvelope.BASS_MIXER_ENV_PAN, ref f位置 ) == -1 )\r
474                                                 return 0;\r
475                                         return (int) ( f位置 * 100 );\r
476                                 }\r
477                                 else if( this.bDirectSoundである )\r
478                                 {\r
479                                         return this._n位置;\r
480                                 }\r
481                                 return -9999;\r
482                         }\r
483                         set\r
484                         {\r
485                                 if( this.bBASSサウンドである )\r
486                                 {\r
487                                         float f位置 = Math.Min( Math.Max( value, -100 ), 100 ) / 100.0f;      // -100~100 → -1.0~1.0\r
488                                         //var nodes = new BASS_MIXER_NODE[ 1 ] { new BASS_MIXER_NODE( 0, f位置 ) };\r
489                                         //BassMix.BASS_Mixer_ChannelSetEnvelope( this.hBassStream, BASSMIXEnvelope.BASS_MIXER_ENV_PAN, nodes );\r
490                                         Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_PAN, f位置 );\r
491                                 }\r
492                                 else if( this.bDirectSoundである )\r
493                                 {\r
494                                         this._n位置 = Math.Min( Math.Max( -100, value ), 100 );               // -100~100\r
495 \r
496                                         if( this._n位置 == 0 )\r
497                                         {\r
498                                                 this._n位置db = 0;\r
499                                         }\r
500                                         else if( this._n位置 == -100 )\r
501                                         {\r
502                                                 this._n位置db = -10000;\r
503                                         }\r
504                                         else if( this._n位置 == 100 )\r
505                                         {\r
506                                                 this._n位置db = 100000;\r
507                                         }\r
508                                         else if( this._n位置 < 0 )\r
509                                         {\r
510                                                 this._n位置db = (int) ( ( 20.0 * Math.Log10( ( (double) ( this._n位置 + 100 ) ) / 100.0 ) ) * 100.0 );\r
511                                         }\r
512                                         else\r
513                                         {\r
514                                                 this._n位置db = (int) ( ( -20.0 * Math.Log10( ( (double) ( 100 - this._n位置 ) ) / 100.0 ) ) * 100.0 );\r
515                                         }\r
516 \r
517                                         this.Buffer.Pan = this._n位置db;\r
518                                 }\r
519                         }\r
520                 }\r
521 \r
522                 /// <summary>\r
523                 /// <para>DirectSoundのセカンダリバッファ。</para>\r
524                 /// </summary>\r
525                 //public SecondarySoundBuffer DirectSoundBuffer\r
526                 public SoundBuffer DirectSoundBuffer\r
527                 {\r
528                         get { return this.Buffer; }\r
529                 }\r
530 \r
531                 /// <summary>\r
532                 /// <para>DirectSoundのセカンダリバッファ作成時のフラグ。</para>\r
533                 /// </summary>\r
534                 public BufferFlags DirectSoundBufferFlags\r
535                 {\r
536                         get;\r
537                         protected set;\r
538                 }\r
539 \r
540                 /// <summary>\r
541                 /// <para>全インスタンスリスト。</para>\r
542                 /// <para>~を作成する() で追加され、t解放する() or Dispose() で解放される。</para>\r
543                 /// </summary>\r
544                 public static List<CSound> listインスタンス = new List<CSound>();\r
545 \r
546                 public static void ShowAllCSoundFiles()\r
547                 {\r
548                         int i = 0;\r
549                         foreach ( CSound cs in listインスタンス )\r
550                         {\r
551                                 Debug.WriteLine( i++.ToString( "d3" ) + ": " + Path.GetFileName( cs.strファイル名 ) );\r
552                         }\r
553                 }\r
554 \r
555                 public CSound()\r
556                 {\r
557                         this.n音量 = 100;\r
558                         this.n位置 = 0;\r
559                         this._db周波数倍率 = 1.0;\r
560                         this._db再生速度 = 1.0;\r
561                         this.DirectSoundBufferFlags = CSoundDeviceDirectSound.DefaultFlags;\r
562 //                      this._cbRemoveMixerChannel = new WaitCallback( RemoveMixerChannelLater );\r
563                 }\r
564 \r
565                 public object Clone()\r
566                 {\r
567                         if ( !bDirectSoundである )\r
568                         {\r
569                                 throw new NotImplementedException();\r
570                         }\r
571                         CSound clone = (CSound) MemberwiseClone();      // これだけだとCY連打が途切れる&タイトルに戻る際にNullRef例外発生\r
572                         this.DirectSound.DuplicateSoundBuffer( this.Buffer, out clone.Buffer );\r
573 \r
574                         // CSound.listインスタンス.Add( this );                   // インスタンスリストに登録。\r
575                         // 本来これを加えるべきだが、Add後Removeできなくなっている。Clone()の仕方の問題であろう。\r
576 \r
577                         return clone;\r
578                 }\r
579                 public void tASIOサウンドを作成する( string strファイル名, int hMixer )\r
580                 {\r
581                         this.tBASSサウンドを作成する( strファイル名, hMixer, BASSFlag.BASS_STREAM_DECODE );\r
582                         this.eデバイス種別 = ESoundDeviceType.ASIO;               // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)\r
583                 }\r
584                 public void tASIOサウンドを作成する( byte[] byArrWAVファイルイメージ, int hMixer )\r
585                 {\r
586                         this.tBASSサウンドを作成する( byArrWAVファイルイメージ, hMixer, BASSFlag.BASS_STREAM_DECODE );\r
587                         this.eデバイス種別 = ESoundDeviceType.ASIO;               // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)\r
588                 }\r
589                 public void tWASAPIサウンドを作成する( string strファイル名, int hMixer, ESoundDeviceType eデバイス種別 )\r
590                 {\r
591                         this.tBASSサウンドを作成する( strファイル名, hMixer, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT );\r
592                         this.eデバイス種別 = eデバイス種別;         // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)\r
593                 }\r
594                 public void tWASAPIサウンドを作成する( byte[] byArrWAVファイルイメージ, int hMixer, ESoundDeviceType eデバイス種別 )\r
595                 {\r
596                         this.tBASSサウンドを作成する( byArrWAVファイルイメージ, hMixer, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT );\r
597                         this.eデバイス種別 = eデバイス種別;         // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)\r
598                 }\r
599                 public void tDirectSoundサウンドを作成する( string strファイル名, DirectSound DirectSound )\r
600                 {\r
601                         this.e作成方法 = E作成方法.ファイルから;\r
602                         this.strファイル名 = strファイル名;\r
603 \r
604                         if ( String.Compare( Path.GetExtension( strファイル名 ), ".xa", true ) == 0 ||\r
605                                  String.Compare( Path.GetExtension( strファイル名 ), ".mp3", true ) == 0 ||\r
606                                  String.Compare( Path.GetExtension( strファイル名 ), ".ogg", true ) == 0 ) // caselessで文字列比較\r
607                         {\r
608                                 tDirectSoundサウンドを作成するXaOggMp3( strファイル名, DirectSound );\r
609                                 return;\r
610                         }\r
611 \r
612                         // すべてのファイルを DirectShow でデコードすると時間がかかるので、ファイルが WAV かつ PCM フォーマットでない場合のみ DirectShow でデコードする。\r
613 \r
614                         byte[] byArrWAVファイルイメージ = null;\r
615                         bool bファイルがWAVかつPCMフォーマットである = true;\r
616 \r
617                         {\r
618                                 #region [ ファイルがWAVかつPCMフォーマットか否か調べる。]\r
619                                 //-----------------\r
620                                 try\r
621                                 {\r
622                                         using ( var ws = new WaveStream( strファイル名 ) )\r
623                                         {\r
624                                                 if ( ws.Format.FormatTag != WaveFormatTag.Pcm )\r
625                                                         bファイルがWAVかつPCMフォーマットである = false;\r
626                                         }\r
627                                 }\r
628                                 catch\r
629                                 {\r
630                                         bファイルがWAVかつPCMフォーマットである = false;\r
631                                 }\r
632                                 //-----------------\r
633                                 #endregion\r
634 \r
635                                 if ( bファイルがWAVかつPCMフォーマットである )\r
636                                 {\r
637                                         #region [ ファイルを読み込んで byArrWAVファイルイメージへ格納。]\r
638                                         //-----------------\r
639                                         var fs = File.Open( strファイル名, FileMode.Open, FileAccess.Read );\r
640                                         var br = new BinaryReader( fs );\r
641 \r
642                                         byArrWAVファイルイメージ = new byte[ fs.Length ];\r
643                                         br.Read( byArrWAVファイルイメージ, 0, (int) fs.Length );\r
644 \r
645                                         br.Close();\r
646                                         fs.Close();\r
647                                         //-----------------\r
648                                         #endregion\r
649                                 }\r
650                                 else\r
651                                 {\r
652                                         #region [ DirectShow でデコード変換し、 byArrWAVファイルイメージへ格納。]\r
653                                         //-----------------\r
654                                         CDStoWAVFileImage.t変換( strファイル名, out byArrWAVファイルイメージ );\r
655                                         //-----------------\r
656                                         #endregion\r
657                                 }\r
658                         }\r
659 \r
660                         // あとはあちらで。\r
661 \r
662                         this.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, DirectSound );\r
663                 }\r
664                 public void tDirectSoundサウンドを作成するXaOggMp3( string strファイル名, DirectSound DirectSound )\r
665                 {\r
666                         this.e作成方法 = E作成方法.ファイルから;\r
667                         this.strファイル名 = strファイル名;\r
668 \r
669 \r
670                         WaveFormat wfx = new WaveFormat();\r
671                         int nPCMデータの先頭インデックス = 0;\r
672 //                      int nPCMサイズbyte = (int) ( xa.xaheader.nSamples * xa.xaheader.nChannels * 2 );     // nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
673 \r
674                         int nPCMサイズbyte;\r
675                         CWin32.WAVEFORMATEX cw32wfx;\r
676                         tオンメモリ方式でデコードする( strファイル名, out this.byArrWAVファイルイメージ,\r
677                         out nPCMデータの先頭インデックス, out nPCMサイズbyte, out cw32wfx );\r
678 \r
679                         wfx.AverageBytesPerSecond = (int) cw32wfx.nAvgBytesPerSec;\r
680                         wfx.BitsPerSample = (short) cw32wfx.wBitsPerSample;\r
681                         wfx.BlockAlignment = (short) cw32wfx.nBlockAlign;\r
682                         wfx.Channels = (short) cw32wfx.nChannels;\r
683                         wfx.FormatTag = WaveFormatTag.Pcm;      // xa.waveformatex.wFormatTag;\r
684                         wfx.SamplesPerSecond = (int) cw32wfx.nSamplesPerSec;\r
685 \r
686                         // セカンダリバッファを作成し、PCMデータを書き込む。\r
687                         tDirectSoundサウンドを作成する・セカンダリバッファの作成とWAVデータ書き込み\r
688                                 ( ref this.byArrWAVファイルイメージ, DirectSound, CSoundDeviceDirectSound.DefaultFlags, wfx,\r
689                                   nPCMサイズbyte, nPCMデータの先頭インデックス );\r
690                 }\r
691 \r
692                 public void tDirectSoundサウンドを作成する( byte[] byArrWAVファイルイメージ, DirectSound DirectSound )\r
693                 {\r
694                         this.tDirectSoundサウンドを作成する(  byArrWAVファイルイメージ, DirectSound, CSoundDeviceDirectSound.DefaultFlags );\r
695                 }\r
696                 public void tDirectSoundサウンドを作成する( byte[] byArrWAVファイルイメージ, DirectSound DirectSound, BufferFlags flags )\r
697                 {\r
698                         if( this.e作成方法 == E作成方法.Unknown )\r
699                                 this.e作成方法 = E作成方法.WAVファイルイメージから;\r
700 \r
701                         WaveFormat wfx = null;\r
702                         int nPCMデータの先頭インデックス = -1;\r
703                         int nPCMサイズbyte = -1;\r
704         \r
705                         #region [ byArrWAVファイルイメージ[] から上記3つのデータを取得。]\r
706                         //-----------------\r
707                         var ms = new MemoryStream( byArrWAVファイルイメージ );\r
708                         var br = new BinaryReader( ms );\r
709 \r
710                         try\r
711                         {\r
712                                 // 'RIFF'+RIFFデータサイズ\r
713 \r
714                                 if( br.ReadUInt32() != 0x46464952 )\r
715                                         throw new InvalidDataException( "RIFFファイルではありません。" );\r
716                                 br.ReadInt32();\r
717 \r
718                                 // 'WAVE'\r
719                                 if( br.ReadUInt32() != 0x45564157 )\r
720                                         throw new InvalidDataException( "WAVEファイルではありません。" );\r
721 \r
722                                 // チャンク\r
723                                 while( ( ms.Position + 8 ) < ms.Length )        // +8 は、チャンク名+チャンクサイズ。残り8バイト未満ならループ終了。\r
724                                 {\r
725                                         uint chunkName = br.ReadUInt32();\r
726 \r
727                                         // 'fmt '\r
728                                         if( chunkName == 0x20746D66 )\r
729                                         {\r
730                                                 long chunkSize = (long) br.ReadUInt32();\r
731 \r
732                                                 var tag = (WaveFormatTag) br.ReadUInt16();\r
733 \r
734                                                 if( tag == WaveFormatTag.Pcm ) wfx = new WaveFormat();\r
735                                                 else if( tag == WaveFormatTag.Extensible ) wfx = new SlimDX.Multimedia.WaveFormatExtensible();  // このクラスは WaveFormat を継承している。\r
736                                                 else\r
737                                                         throw new InvalidDataException( string.Format( "未対応のWAVEフォーマットタグです。(Tag:{0})", tag.ToString() ) );\r
738 \r
739                                                 wfx.FormatTag = tag;\r
740                                                 wfx.Channels = br.ReadInt16();\r
741                                                 wfx.SamplesPerSecond = br.ReadInt32();\r
742                                                 wfx.AverageBytesPerSecond = br.ReadInt32();\r
743                                                 wfx.BlockAlignment = br.ReadInt16();\r
744                                                 wfx.BitsPerSample = br.ReadInt16();\r
745 \r
746                                                 long nフォーマットサイズbyte = 16;\r
747 \r
748                                                 if( wfx.FormatTag == WaveFormatTag.Extensible )\r
749                                                 {\r
750                                                         br.ReadUInt16();        // 拡張領域サイズbyte\r
751                                                         var wfxEx = (SlimDX.Multimedia.WaveFormatExtensible) wfx;\r
752                                                         wfxEx.ValidBitsPerSample = br.ReadInt16();\r
753                                                         wfxEx.ChannelMask = (Speakers) br.ReadInt32();\r
754                                                         wfxEx.SubFormat = new Guid( br.ReadBytes( 16 ) );       // GUID は 16byte (128bit)\r
755 \r
756                                                         nフォーマットサイズbyte += 24;\r
757                                                 }\r
758 \r
759                                                 ms.Seek( chunkSize - nフォーマットサイズbyte, SeekOrigin.Current );\r
760                                                 continue;\r
761                                         }\r
762 \r
763                                         // 'data'\r
764                                         else if( chunkName == 0x61746164 )\r
765                                         {\r
766                                                 nPCMサイズbyte = br.ReadInt32();\r
767                                                 nPCMデータの先頭インデックス = (int) ms.Position;\r
768 \r
769                                                 ms.Seek( nPCMサイズbyte, SeekOrigin.Current );\r
770                                                 continue;\r
771                                         }\r
772 \r
773                                         // その他\r
774                                         else\r
775                                         {\r
776                                                 long chunkSize = (long) br.ReadUInt32();\r
777                                                 ms.Seek( chunkSize, SeekOrigin.Current );\r
778                                                 continue;\r
779                                         }\r
780                                 }\r
781 \r
782                                 if( wfx == null )\r
783                                         throw new InvalidDataException( "fmt チャンクが存在しません。不正なサウンドデータです。" );\r
784                                 if( nPCMサイズbyte < 0 )\r
785                                         throw new InvalidDataException( "data チャンクが存在しません。不正なサウンドデータです。" );\r
786                         }\r
787                         finally\r
788                         {\r
789                                 ms.Close();\r
790                                 br.Close();\r
791                         }\r
792                         //-----------------\r
793                         #endregion\r
794 \r
795 \r
796                         // セカンダリバッファを作成し、PCMデータを書き込む。\r
797                         tDirectSoundサウンドを作成する・セカンダリバッファの作成とWAVデータ書き込み(\r
798                                 ref byArrWAVファイルイメージ, DirectSound, flags, wfx, nPCMサイズbyte, nPCMデータの先頭インデックス );\r
799                 }\r
800 \r
801                 private void tDirectSoundサウンドを作成する・セカンダリバッファの作成とWAVデータ書き込み\r
802                         ( ref byte[] byArrWAVファイルイメージ, DirectSound DirectSound, BufferFlags flags, WaveFormat wfx,\r
803                         int nPCMサイズbyte, int nPCMデータの先頭インデックス )\r
804                 {\r
805                         // セカンダリバッファを作成し、PCMデータを書き込む。\r
806 \r
807                         this.Buffer = new SecondarySoundBuffer( DirectSound, new SoundBufferDescription()\r
808                         {\r
809                                 Format = ( wfx.FormatTag == WaveFormatTag.Pcm ) ? wfx : (SlimDX.Multimedia.WaveFormatExtensible) wfx,\r
810                                 Flags = flags,\r
811                                 SizeInBytes = nPCMサイズbyte,\r
812                         } );\r
813                         this.Buffer.Write( byArrWAVファイルイメージ, nPCMデータの先頭インデックス, nPCMサイズbyte, 0, LockFlags.None );\r
814 \r
815                         // 作成完了。\r
816 \r
817                         this.eデバイス種別 = ESoundDeviceType.DirectSound;\r
818                         this.DirectSoundBufferFlags = flags;\r
819                         this.byArrWAVファイルイメージ = byArrWAVファイルイメージ;\r
820                         this.DirectSound = DirectSound;\r
821 \r
822                         // DTXMania用に追加\r
823                         this.nオリジナルの周波数 = wfx.SamplesPerSecond;\r
824                         n総演奏時間ms = (int) ( ( (double) nPCMサイズbyte ) / ( this.Buffer.Format.AverageBytesPerSecond * 0.001 ) );\r
825 \r
826 \r
827                         // インスタンスリストに登録。\r
828 \r
829                         CSound.listインスタンス.Add( this );\r
830                 }\r
831 \r
832                 #region [ DTXMania用の変換 ]\r
833 \r
834                 public void tサウンドを破棄する( CSound cs )\r
835                 {\r
836                         cs.t解放する();\r
837                 }\r
838                 public void t再生を開始する()\r
839                 {\r
840                         t再生位置を先頭に戻す();\r
841                         tサウンドを再生する();\r
842                 }\r
843                 public void t再生を開始する( bool bループする )\r
844                 {\r
845                         if ( bBASSサウンドである )\r
846                         {\r
847                                 if ( bループする )\r
848                                 {\r
849                                         Bass.BASS_ChannelFlags( this.hBassStream, BASSFlag.BASS_SAMPLE_LOOP, BASSFlag.BASS_SAMPLE_LOOP );\r
850                                 }\r
851                                 else\r
852                                 {\r
853                                         Bass.BASS_ChannelFlags( this.hBassStream, BASSFlag.BASS_DEFAULT, BASSFlag.BASS_DEFAULT );\r
854                                 }\r
855                         }\r
856                         t再生位置を先頭に戻す();\r
857                         tサウンドを再生する( bループする );\r
858                 }\r
859                 public void t再生を停止する()\r
860                 {\r
861                         tサウンドを停止する();\r
862                         t再生位置を先頭に戻す();\r
863                 }\r
864                 public void t再生を一時停止する()\r
865                 {\r
866                         tサウンドを停止する(true);\r
867                         this.n一時停止回数++;\r
868                 }\r
869                 public void t再生を再開する( long t )    // ★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
870                 {\r
871                         Debug.WriteLine( "t再生を再開する(long " + t + ")" );\r
872                         t再生位置を変更する( t );\r
873                         tサウンドを再生する();\r
874                         this.n一時停止回数--;\r
875                 }\r
876                 public bool b一時停止中\r
877                 {\r
878                         get\r
879                         {\r
880                                 if ( this.bBASSサウンドである )\r
881                                 {\r
882                                         bool ret = ( BassMix.BASS_Mixer_ChannelIsActive( this.hBassStream ) == BASSActive.BASS_ACTIVE_PAUSED ) &\r
883                                                                 ( BassMix.BASS_Mixer_ChannelGetPosition( this.hBassStream ) > 0 );\r
884                                         return ret;\r
885                                 }\r
886                                 else\r
887                                 {\r
888                                         return ( this.n一時停止回数 > 0 );\r
889                                 }\r
890                         }\r
891                 }\r
892                 public bool b再生中\r
893                 {\r
894                         get\r
895                         {\r
896                                 if ( this.eデバイス種別 == ESoundDeviceType.DirectSound )\r
897                                 {\r
898                                         return ( ( this.Buffer.Status & BufferStatus.Playing ) != BufferStatus.None );\r
899                                 }\r
900                                 else\r
901                                 {\r
902                                         // 基本的にはBASS_ACTIVE_PLAYINGなら再生中だが、最後まで再生しきったchannelも\r
903                                         // BASS_ACTIVE_PLAYINGのままになっているので、小細工が必要。\r
904                                         bool ret = ( BassMix.BASS_Mixer_ChannelIsActive( this.hBassStream ) == BASSActive.BASS_ACTIVE_PLAYING );\r
905                                         if ( BassMix.BASS_Mixer_ChannelGetPosition( this.hBassStream ) >= nBytes )\r
906                                         {\r
907                                                 ret = false;\r
908                                         }\r
909                                         return ret;\r
910                                 }\r
911                         }\r
912                 }\r
913                 //public lint t時刻から位置を返す( long t )\r
914                 //{\r
915                 //    double num = ( n時刻 * this.db再生速度 ) * this.db周波数倍率;\r
916                 //    return (int) ( ( num * 0.01 ) * this.nSamplesPerSecond );\r
917                 //}\r
918                 #endregion\r
919 \r
920 \r
921                 public void t解放する()\r
922                 {\r
923                         t解放する( false );\r
924                 }\r
925 \r
926                 public void t解放する( bool _bインスタンス削除 )\r
927                 {\r
928                         if ( this.bBASSサウンドである )          // stream数の削減用\r
929                         {\r
930                                 tBASSサウンドをミキサーから削除する();\r
931                                 //_cbEndofStream = null;\r
932                                 //_cbStreamXA = null;\r
933                                 CSound管理.nStreams--;\r
934                         }\r
935                         bool bManagedも解放する = true;\r
936                         bool bインスタンス削除 = _bインスタンス削除;    // CSoundの再初期化時は、インスタンスは存続する。\r
937                         this.Dispose( bManagedも解放する, bインスタンス削除 );\r
938 //Debug.WriteLine( "Disposed: " + _bインスタンス削除 + " : " + Path.GetFileName( this.strファイル名 ) );\r
939                 }\r
940                 public void tサウンドを再生する()\r
941                 {\r
942                         tサウンドを再生する( false );\r
943                 }\r
944                 public void tサウンドを再生する( bool bループする )\r
945                 {\r
946                         if ( this.bBASSサウンドである )                  // BASSサウンド時のループ処理は、t再生を開始する()側に実装。\r
947                         {\r
948 //Debug.WriteLine( "再生中?: " +  System.IO.Path.GetFileName(this.strファイル名) + " status=" + BassMix.BASS_Mixer_ChannelIsActive( this.hBassStream ) + " current=" + BassMix.BASS_Mixer_ChannelGetPosition( this.hBassStream ) + " nBytes=" + nBytes );\r
949                                 bool b = BassMix.BASS_Mixer_ChannelPlay( this.hBassStream );\r
950                                 if ( !b )\r
951                                 {\r
952 Debug.WriteLine( "再生しようとしたが、Mixerに登録されていなかった: " + Path.GetFileName( this.strファイル名 ) + ", " + hBassStream );\r
953 //Debug.WriteLine( "ErrCode= " +Bass.BASS_ErrorGetCode() );\r
954 \r
955                                         bool bb = tBASSサウンドをミキサーに追加する();\r
956                                         if ( !bb )\r
957                                         {\r
958 Debug.WriteLine( "Mixerへの登録に失敗: " + Path.GetFileName( this.strファイル名 ) + ": " + Bass.BASS_ErrorGetCode() );\r
959                                         }\r
960                                         else\r
961                                         {\r
962 //Debug.WriteLine( "Mixerへの登録に成功: " + Path.GetFileName( this.strファイル名 ) + ": " + Bass.BASS_ErrorGetCode() );\r
963                                         }\r
964                                         //this.t再生位置を先頭に戻す();\r
965 \r
966                                         bool bbb = BassMix.BASS_Mixer_ChannelPlay( this.hBassStream );\r
967                                         if (!bbb)\r
968                                         {\r
969                                                 Debug.WriteLine("更に再生に失敗                                 : " + Path.GetFileName(this.strファイル名));\r
970                                                 Debug.WriteLine("ErrCode= " + Bass.BASS_ErrorGetCode());\r
971                                         }\r
972                                         else\r
973                                         {\r
974 //                                              Debug.WriteLine("再生成功(ミキサー追加後)                       : " + Path.GetFileName(this.strファイル名));\r
975                                         }\r
976                                 }\r
977                                 else\r
978                                 {\r
979 //Debug.WriteLine( "再生成功: " + Path.GetFileName( this.strファイル名 ) + " (" + hBassStream + ")" );\r
980                                 }\r
981                         }\r
982                         else if( this.bDirectSoundである )\r
983                         {\r
984                                 PlayFlags pf = ( bループする ) ? PlayFlags.Looping : PlayFlags.None;\r
985                                 this.Buffer.Play( 0, pf );\r
986                         }\r
987                 }\r
988                 public void tサウンドを先頭から再生する()\r
989                 {\r
990                         this.t再生位置を先頭に戻す();\r
991                         this.tサウンドを再生する();\r
992                 }\r
993                 public void tサウンドを停止する()\r
994                 {\r
995                         tサウンドを停止する( false );\r
996                 }\r
997                 public void tサウンドを停止する( bool pause )\r
998                 {\r
999                         if( this.bBASSサウンドである )\r
1000                         {\r
1001 //Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル名 ) + " status=" + BassMix.BASS_Mixer_ChannelIsActive( this.hBassStream ) + " current=" + BassMix.BASS_Mixer_ChannelGetPosition( this.hBassStream ) + " nBytes=" + nBytes );\r
1002                                 BassMix.BASS_Mixer_ChannelPause( this.hBassStream );\r
1003                                 if ( !pause )\r
1004                                 {\r
1005                         //              tBASSサウンドをミキサーから削除する();           // PAUSEと再生停止を区別できるようにすること!!\r
1006                                 }\r
1007                         }\r
1008                         else if( this.bDirectSoundである )\r
1009                         {\r
1010                                 this.Buffer.Stop();\r
1011                         }\r
1012                 }\r
1013                 \r
1014                 public void t再生位置を先頭に戻す()\r
1015                 {\r
1016                         if( this.bBASSサウンドである )\r
1017                         {\r
1018                                 BassMix.BASS_Mixer_ChannelSetPosition( this.hBassStream, 0 );\r
1019                                 pos = 0;\r
1020                         }\r
1021                         else if( this.bDirectSoundである )\r
1022                         {\r
1023                                 this.Buffer.CurrentPlayPosition = 0;\r
1024                         }\r
1025                 }\r
1026                 public void t再生位置を変更する( long n位置ms )\r
1027                 {\r
1028                         if( this.bBASSサウンドである )\r
1029                         {\r
1030                                 BassMix.BASS_Mixer_ChannelSetPosition( this.hBassStream, Bass.BASS_ChannelSeconds2Bytes( this.hBassStream, n位置ms / 1000.0 ), BASSMode.BASS_POS_BYTES );\r
1031                         }\r
1032                         else if( this.bDirectSoundである )\r
1033                         {\r
1034                                 int n位置sample = (int) ( this.Buffer.Format.SamplesPerSecond * n位置ms * 0.001 );\r
1035                                 this.Buffer.CurrentPlayPosition = n位置sample * this.Buffer.Format.BlockAlignment;\r
1036                         }\r
1037                 }\r
1038 \r
1039                 public static void tすべてのサウンドを初期状態に戻す()\r
1040                 {\r
1041                         foreach ( var sound in CSound.listインスタンス )\r
1042                         {\r
1043                                 sound.t解放する( false );\r
1044                         }\r
1045                 }\r
1046                 public static void tすべてのサウンドを再構築する( ISoundDevice device )\r
1047                 {\r
1048                         if( CSound.listインスタンス.Count == 0 )\r
1049                                 return;\r
1050 \r
1051 \r
1052                         // サウンドを再生する際にインスタンスリストも更新されるので、配列にコピーを取っておき、リストはクリアする。\r
1053 \r
1054                         var sounds = CSound.listインスタンス.ToArray();\r
1055                         CSound.listインスタンス.Clear();\r
1056                         \r
1057 \r
1058                         // 配列に基づいて個々のサウンドを作成する。\r
1059 \r
1060                         for( int i = 0; i < sounds.Length; i++ )\r
1061                         {\r
1062                                 switch( sounds[ i ].e作成方法 )\r
1063                                 {\r
1064                                         #region [ ファイルから ]\r
1065                                         case E作成方法.ファイルから:\r
1066                                                 string strファイル名 = sounds[ i ].strファイル名;\r
1067                                                 sounds[ i ].Dispose( true, false );\r
1068                                                 device.tサウンドを作成する( strファイル名, ref sounds[ i ] );\r
1069                                                 break;\r
1070                                         #endregion\r
1071                                         #region [ WAVファイルイメージから ]\r
1072                                         case E作成方法.WAVファイルイメージから:\r
1073                                                 if( sounds[ i ].bBASSサウンドである )\r
1074                                                 {\r
1075                                                         byte[] byArrWaveファイルイメージ = sounds[ i ].byArrWAVファイルイメージ;\r
1076                                                         sounds[ i ].Dispose( true, false );\r
1077                                                         device.tサウンドを作成する( byArrWaveファイルイメージ, ref sounds[ i ] );\r
1078                                                 }\r
1079                                                 else if( sounds[ i ].bDirectSoundである )\r
1080                                                 {\r
1081                                                         byte[] byArrWaveファイルイメージ = sounds[ i ].byArrWAVファイルイメージ;\r
1082                                                         var flags = sounds[ i ].DirectSoundBufferFlags;\r
1083                                                         sounds[ i ].Dispose( true, false );\r
1084                                                         ( (CSoundDeviceDirectSound) device ).tサウンドを作成する( byArrWaveファイルイメージ, flags, ref sounds[ i ] );\r
1085                                                 }\r
1086                                                 break;\r
1087                                         #endregion\r
1088                                 }\r
1089                         }\r
1090                 }\r
1091 \r
1092                 #region [ Dispose-Finalizeパターン実装 ]\r
1093                 //-----------------\r
1094                 public void Dispose()\r
1095                 {\r
1096                         this.Dispose( true, true );\r
1097                         GC.SuppressFinalize( this );\r
1098                 }\r
1099                 protected void Dispose( bool bManagedも解放する, bool bインスタンス削除 )\r
1100                 {\r
1101                         if( this.bBASSサウンドである )\r
1102                         {\r
1103                                 #region [ ASIO, WASAPI の解放 ]\r
1104                                 //-----------------\r
1105                                 BassMix.BASS_Mixer_ChannelRemove( this.hBassStream );\r
1106                                 Bass.BASS_StreamFree( this.hBassStream );\r
1107                                 this.hBassStream = -1;\r
1108                                 //-----------------\r
1109                                 #endregion\r
1110                         }\r
1111 \r
1112                         if( bManagedも解放する )\r
1113                         {\r
1114                                 //int freeIndex = -1;\r
1115 \r
1116                                 //if ( CSound.listインスタンス != null )\r
1117                                 //{\r
1118                                 //    freeIndex = CSound.listインスタンス.IndexOf( this );\r
1119                                 //    if ( freeIndex == -1 )\r
1120                                 //    {\r
1121                                 //        Debug.WriteLine( "ERR: freeIndex==-1 : Count=" + CSound.listインスタンス.Count + ", filename=" + Path.GetFileName( this.strファイル名 ) );\r
1122                                 //    }\r
1123                                 //}\r
1124 \r
1125                                 if( this.eデバイス種別 == ESoundDeviceType.DirectSound )\r
1126                                 {\r
1127                                         #region [ DirectSound の解放 ]\r
1128                                         //-----------------\r
1129                                         if( this.Buffer != null )\r
1130                                         {\r
1131                                                 try\r
1132                                                 {\r
1133                                                         this.Buffer.Stop();\r
1134                                                 }\r
1135                                                 catch\r
1136                                                 {\r
1137                                                         // 演奏終了後、長時間解放しないでいると、たまに AccessViolationException が発生することがある。\r
1138                                                 }\r
1139                                                 C共通.tDisposeする( ref this.Buffer );\r
1140                                         }\r
1141                                         //-----------------\r
1142                                         #endregion\r
1143                                 }\r
1144 \r
1145                                 if( this.e作成方法 == E作成方法.WAVファイルイメージから &&\r
1146                                         this.eデバイス種別 != ESoundDeviceType.DirectSound )      // DirectSound は hGC 未使用。\r
1147                                 {\r
1148                                         if ( this.hGC != null && this.hGC.IsAllocated )\r
1149                                         {\r
1150                                                 this.hGC.Free();\r
1151                                                 this.hGC = default( GCHandle );\r
1152                                         }\r
1153                                 }\r
1154                                 if ( this.byArrWAVファイルイメージ != null )\r
1155                                 {\r
1156                                         this.byArrWAVファイルイメージ = null;\r
1157                                 }\r
1158 \r
1159                                 if ( bインスタンス削除 )\r
1160                                 {\r
1161                                         //try\r
1162                                         //{\r
1163                                         //    CSound.listインスタンス.RemoveAt( freeIndex );\r
1164                                         //}\r
1165                                         //catch\r
1166                                         //{\r
1167                                         //    Debug.WriteLine( "FAILED to remove CSound.listインスタンス: Count=" + CSound.listインスタンス.Count + ", filename=" + Path.GetFileName( this.strファイル名 ) );\r
1168                                         //}\r
1169                                         bool b = CSound.listインスタンス.Remove( this );  // これだと、Clone()したサウンドのremoveに失敗する\r
1170                                         if ( !b )\r
1171                                         {\r
1172                                                 Debug.WriteLine( "FAILED to remove CSound.listインスタンス: Count=" + CSound.listインスタンス.Count + ", filename=" + Path.GetFileName( this.strファイル名 ) );\r
1173                                         }\r
1174 \r
1175                                 }\r
1176                         }\r
1177                 }\r
1178                 ~CSound()\r
1179                 {\r
1180                         this.Dispose( false, true );\r
1181                 }\r
1182                 //-----------------\r
1183                 #endregion\r
1184 \r
1185                 #region [ protected ]\r
1186                 //-----------------\r
1187                 protected enum E作成方法 { ファイルから, WAVファイルイメージから, Unknown }\r
1188                 protected E作成方法 e作成方法 = E作成方法.Unknown;\r
1189                 protected ESoundDeviceType eデバイス種別 = ESoundDeviceType.Unknown;\r
1190                 public string strファイル名 = null;\r
1191                 protected byte[] byArrWAVファイルイメージ = null;       // WAVファイルイメージ、もしくはchunkのDATA部のみ\r
1192                 protected GCHandle hGC;\r
1193                 public int hBassStream = -1;                                    // ASIO, WASAPI 用\r
1194                 protected SoundBuffer Buffer = null;                    // DirectSound 用\r
1195                 protected DirectSound DirectSound;\r
1196                 public int hMixer = -1; // 設計壊してゴメン Mixerに後で登録するときに使う\r
1197                 //-----------------\r
1198                 #endregion\r
1199 \r
1200                 #region [ private ]\r
1201                 //-----------------\r
1202                 private bool bDirectSoundである\r
1203                 {\r
1204                         get { return ( this.eデバイス種別 == ESoundDeviceType.DirectSound ); }\r
1205                 }\r
1206                 private bool bBASSサウンドである\r
1207                 {\r
1208                         get\r
1209                         {\r
1210                                 return (\r
1211                                         this.eデバイス種別 == ESoundDeviceType.ASIO ||\r
1212                                         this.eデバイス種別 == ESoundDeviceType.ExclusiveWASAPI ||\r
1213                                         this.eデバイス種別 == ESoundDeviceType.SharedWASAPI );\r
1214                         }\r
1215                 }\r
1216                 private int _n位置 = 0;\r
1217                 private int _n位置db;\r
1218                 private int _n音量 = 100;\r
1219                 private int _n音量db;\r
1220                 private long nBytes = 0;\r
1221                 private int n一時停止回数 = 0;\r
1222                 private int nオリジナルの周波数 = 0;\r
1223                 private double _db周波数倍率 = 1.0;\r
1224                 private double _db再生速度 = 1.0;\r
1225 \r
1226                 private void tBASSサウンドを作成する( string strファイル名, int hMixer, BASSFlag flags )\r
1227                 {\r
1228                         if ( String.Compare( Path.GetExtension( strファイル名 ), ".xa", true ) == 0 )      // caselessで文字列比較\r
1229                         {\r
1230                                 tBASSサウンドを作成するXA( strファイル名, hMixer, flags );\r
1231                                 return;\r
1232                         }\r
1233 \r
1234                         this.e作成方法 = E作成方法.ファイルから;\r
1235                         this.strファイル名 = strファイル名;\r
1236 \r
1237 \r
1238                         // BASSファイルストリームを作成。\r
1239 \r
1240                         this.hBassStream = Bass.BASS_StreamCreateFile( strファイル名, 0, 0, flags );\r
1241                         if( this.hBassStream == 0 )\r
1242                                 throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_StreamCreateFile)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
1243                         \r
1244                         nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
1245                         \r
1246                         tBASSサウンドを作成する・ストリーム生成後の共通処理( hMixer );\r
1247                 }\r
1248                 private void tBASSサウンドを作成する( byte[] byArrWAVファイルイメージ, int hMixer, BASSFlag flags )\r
1249                 {\r
1250                         this.e作成方法 = E作成方法.WAVファイルイメージから;\r
1251                         this.byArrWAVファイルイメージ = byArrWAVファイルイメージ;\r
1252                         this.hGC = GCHandle.Alloc( byArrWAVファイルイメージ, GCHandleType.Pinned );             // byte[] をピン留め\r
1253 \r
1254 \r
1255                         // BASSファイルストリームを作成。\r
1256 \r
1257                         this.hBassStream = Bass.BASS_StreamCreateFile( hGC.AddrOfPinnedObject(), 0, byArrWAVファイルイメージ.Length, flags );\r
1258                         if ( this.hBassStream == 0 )\r
1259                                 throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_StreamCreateFile)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
1260 \r
1261                         nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
1262         \r
1263                         tBASSサウンドを作成する・ストリーム生成後の共通処理( hMixer );\r
1264                 }\r
1265                 private void tBASSサウンドを作成するXA( string strファイル名, int hMixer, BASSFlag flags )\r
1266                 {\r
1267                         int nPCMデータの先頭インデックス;\r
1268                         CWin32.WAVEFORMATEX wfx;\r
1269                         int totalPCMSize;\r
1270 \r
1271                         tオンメモリ方式でデコードする( strファイル名, out this.byArrWAVファイルイメージ,\r
1272                                 out nPCMデータの先頭インデックス, out totalPCMSize, out wfx );\r
1273 \r
1274                         nBytes = totalPCMSize;\r
1275 \r
1276                         this.e作成方法 = E作成方法.WAVファイルイメージから;           //.ファイルから;  // 再構築時はデコード後のイメージを流用する&Dispose時にhGCを解放する\r
1277                         this.strファイル名 = strファイル名;\r
1278                         this.hGC = GCHandle.Alloc( this.byArrWAVファイルイメージ, GCHandleType.Pinned );                // byte[] をピン留め\r
1279 \r
1280 \r
1281                         _cbStreamXA = new STREAMPROC( CallbackPlayingXA );\r
1282 \r
1283                         // BASSファイルストリームを作成。\r
1284 \r
1285                         //this.hBassStream = Bass.BASS_StreamCreate( xa.xaheader.nSamplesPerSec, xa.xaheader.nChannels, BASSFlag.BASS_STREAM_DECODE, _myStreamCreate, IntPtr.Zero );\r
1286                         this.hBassStream = Bass.BASS_StreamCreate( (int) wfx.nSamplesPerSec, (int) wfx.nChannels, BASSFlag.BASS_STREAM_DECODE, _cbStreamXA, IntPtr.Zero );\r
1287                         if ( this.hBassStream == 0 )\r
1288                         {\r
1289                                 hGC.Free();\r
1290                                 throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_SampleCreate)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
1291                         }\r
1292 \r
1293                         tBASSサウンドを作成する・ストリーム生成後の共通処理( hMixer );\r
1294                 }\r
1295 \r
1296 \r
1297                 private void tBASSサウンドを作成する・ストリーム生成後の共通処理( int hMixer )\r
1298                 {\r
1299                         CSound管理.nStreams++;\r
1300 \r
1301                         // ミキサーにBASSファイルストリームを追加。\r
1302 \r
1303                         //if ( !BassMix.BASS_Mixer_StreamAddChannel( hMixer, this.hBassStream, BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_PAUSE | BASSFlag.BASS_MIXER_NORAMPIN ) )\r
1304                         ////                    if ( !tBASSサウンドをミキサーに追加する() )\r
1305                         //{\r
1306                         //    hGC.Free();\r
1307                         //    throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_Mixer_StreamAddChannel)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
1308                         //}\r
1309                         //CSound管理.nStreams++;\r
1310 \r
1311                         //                      _cbEndofStream = new SYNCPROC( CallbackEndofStream );\r
1312                         //                      Bass.BASS_ChannelSetSync( hBassStream, BASSSync.BASS_SYNC_END |BASSSync.BASS_SYNC_MIXTIME, 0, _cbEndofStream, IntPtr.Zero );\r
1313 \r
1314 \r
1315                         // インスタンスリストに登録。\r
1316 \r
1317                         CSound.listインスタンス.Add( this );\r
1318 \r
1319                         // n総演奏時間の取得; DTXMania用に追加。\r
1320                         double seconds = Bass.BASS_ChannelBytes2Seconds( this.hBassStream, nBytes );\r
1321                         this.n総演奏時間ms = (int) ( seconds * 1000 );\r
1322                         this.pos = 0;\r
1323                         this.hMixer = hMixer;\r
1324                         float freq = 0.0f;\r
1325                         if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ref freq ) )\r
1326                         {\r
1327                                 hGC.Free();\r
1328                                 throw new Exception( string.Format( "サウンドストリームの周波数取得に失敗しました。(BASS_ChannelGetAttribute)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
1329                         }\r
1330                         this.nオリジナルの周波数 = (int) freq;\r
1331                 }\r
1332                 //-----------------\r
1333 \r
1334                 private int pos = 0;\r
1335                 private int CallbackPlayingXA( int handle, IntPtr buffer, int length, IntPtr user )\r
1336                 {\r
1337                         int bytesread = ( pos + length > Convert.ToInt32(nBytes) ) ? Convert.ToInt32(nBytes) - pos : length;\r
1338 \r
1339                         Marshal.Copy( byArrWAVファイルイメージ, pos, buffer, bytesread );\r
1340                         pos += bytesread;\r
1341 \r
1342                         if ( pos >= nBytes )\r
1343                         {\r
1344                                 // set indicator flag\r
1345                                 bytesread |= (int) BASSStreamProc.BASS_STREAMPROC_END;\r
1346                         }\r
1347                         return bytesread;\r
1348                 }\r
1349 \r
1350 \r
1351 // mixerからの削除\r
1352 \r
1353                 public bool tBASSサウンドをミキサーから削除する()\r
1354                 {\r
1355                         return tBASSサウンドをミキサーから削除する( this.hBassStream );\r
1356                 }\r
1357                 public bool tBASSサウンドをミキサーから削除する( int channel )\r
1358                 {\r
1359                         bool b = BassMix.BASS_Mixer_ChannelRemove( channel );\r
1360                         if ( b )\r
1361                         {\r
1362                                 Interlocked.Decrement( ref CSound管理.nMixing );\r
1363 //                              Debug.WriteLine( "Removed: " + Path.GetFileName( this.strファイル名 ) + " (" + channel + ")" + " MixedStreams=" + CSound管理.nMixing );\r
1364                         }\r
1365                         return b;\r
1366                 }\r
1367 \r
1368 \r
1369 // mixer への追加\r
1370                 \r
1371                 public bool tBASSサウンドをミキサーに追加する()\r
1372                 {\r
1373                         if ( BassMix.BASS_Mixer_ChannelGetMixer( hBassStream ) == 0 )\r
1374                         {\r
1375                                 BASSFlag bf = BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_NORAMPIN;       // | BASSFlag.BASS_MIXER_PAUSE;\r
1376                                 Interlocked.Increment( ref CSound管理.nMixing );\r
1377 \r
1378                                 // preloadされることを期待して、敢えてflagからはBASS_MIXER_PAUSEを外してAddChannelした上で、すぐにPAUSEする\r
1379                                 bool b1 = BassMix.BASS_Mixer_StreamAddChannel( this.hMixer, this.hBassStream, bf );\r
1380                                 bool b2 = BassMix.BASS_Mixer_ChannelPause( this.hBassStream );\r
1381                                 t再生位置を先頭に戻す();      // StreamAddChannelの後で再生位置を戻さないとダメ。逆だと再生位置が変わらない。\r
1382 //                              Debug.WriteLine( "Add Mixer: " + Path.GetFileName( this.strファイル名 ) + " (" + hBassStream + ")" + " MixedStreams=" + CSound管理.nMixing );\r
1383                                 return b1 & b2;\r
1384                         }\r
1385                         return true;\r
1386                 }\r
1387 \r
1388                 #region [ tオンメモリ方式でデコードする() ]\r
1389                 public void tオンメモリ方式でデコードする( string strファイル名, out byte[] buffer,\r
1390                         out int nPCMデータの先頭インデックス, out int totalPCMSize, out CWin32.WAVEFORMATEX wfx )\r
1391                 {\r
1392                         nPCMデータの先頭インデックス = 0;\r
1393                         //int nPCMサイズbyte = (int) ( xa.xaheader.nSamples * xa.xaheader.nChannels * 2 );   // nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
1394 \r
1395                         SoundDecoder sounddecoder;\r
1396 \r
1397                         if ( String.Compare( Path.GetExtension( strファイル名 ), ".xa", true ) == 0 )\r
1398                         {\r
1399                                 sounddecoder = new Cxa();\r
1400                         }\r
1401                         else if ( String.Compare( Path.GetExtension( strファイル名 ), ".ogg", true ) == 0 )\r
1402                         {\r
1403                                 sounddecoder = new Cogg();\r
1404                         }\r
1405                         else if ( String.Compare( Path.GetExtension( strファイル名 ), ".mp3", true ) == 0 )\r
1406                         {\r
1407                                 sounddecoder = new Cmp3();\r
1408                         }\r
1409                         else\r
1410                         {\r
1411                                 throw new NotImplementedException();\r
1412                         }\r
1413 \r
1414                         int nHandle = sounddecoder.Open( strファイル名 );\r
1415                         if ( nHandle < 0 )\r
1416                         {\r
1417                                 throw new Exception( string.Format( "Open() に失敗しました。({0})({1})", nHandle, strファイル名 ) );\r
1418                         }\r
1419                         wfx = new CWin32.WAVEFORMATEX();\r
1420                         if ( sounddecoder.GetFormat( nHandle, ref wfx ) < 0 )\r
1421                         {\r
1422                                 sounddecoder.Close( nHandle );\r
1423                                 throw new Exception( string.Format( "GetFormat() に失敗しました。({0})", strファイル名 ) );\r
1424                         }\r
1425                         //totalPCMSize = (int) sounddecoder.nTotalPCMSize;              //  tデコード後のサイズを調べる()で既に取得済みの値を流用する。ms単位の高速化だが、チップ音がたくさんあると塵積で結構効果がある\r
1426                         totalPCMSize = (int) sounddecoder.GetTotalPCMSize( nHandle );\r
1427                         if ( totalPCMSize == 0 )\r
1428                         {\r
1429                                 sounddecoder.Close( nHandle );\r
1430                                 throw new Exception( string.Format( "GetTotalPCMSize() に失敗しました。({0})", strファイル名 ) );\r
1431                         }\r
1432                         totalPCMSize += ( ( totalPCMSize % 2 ) != 0 ) ? 1 : 0;\r
1433                         buffer = new byte[ totalPCMSize ];\r
1434                         GCHandle handle = GCHandle.Alloc( buffer, GCHandleType.Pinned );\r
1435                         try\r
1436                         {\r
1437                                 if ( sounddecoder.Decode( nHandle, handle.AddrOfPinnedObject(), (uint) totalPCMSize, 0 ) < 0 )\r
1438                                 {\r
1439                                         buffer = null;\r
1440                                         throw new Exception( string.Format( "デコードに失敗しました。({0})", strファイル名 ) );\r
1441                                 }\r
1442                         }\r
1443                         finally\r
1444                         {\r
1445                                 handle.Free();\r
1446                                 sounddecoder.Close( nHandle );\r
1447                                 sounddecoder = null;\r
1448                         }\r
1449                 }\r
1450                 #endregion\r
1451                 #endregion\r
1452         }\r
1453 }\r