OSDN Git Service

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