OSDN Git Service

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