OSDN Git Service

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