OSDN Git Service

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