OSDN Git Service

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