OSDN Git Service

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