OSDN Git Service

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