OSDN Git Service

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