OSDN Git Service

#37271 TimeStretch=OFFの時に限り、ミキシング負荷を従来の半分にした。
[dtxmania/dtxmania.git] / FDK17プロジェクト / コード / 03.サウンド / CSound.cs
index 7d9d769..5ea1acc 100644 (file)
@@ -13,6 +13,7 @@ using Un4seen.Bass;
 using Un4seen.BassAsio;\r
 using Un4seen.BassWasapi;\r
 using Un4seen.Bass.AddOn.Mix;\r
+using Un4seen.Bass.AddOn.Fx;\r
 using DirectShowLib;\r
 \r
 namespace FDK\r
@@ -20,36 +21,47 @@ namespace FDK
        #region [ DTXMania用拡張 ]\r
        public class CSound管理       // : CSound\r
        {\r
-               //private static ISoundDevice _SoundDevice;\r
-               //private static ESoundDeviceType _SoundDeviceType = ESoundDeviceType.Unknown;\r
                public static ISoundDevice SoundDevice\r
                {\r
                        get; set;\r
-                       //get\r
-                       //{\r
-                       //    return _SoundDevice;\r
-                       //}\r
-                       //set\r
-                       //{\r
-                       //    _SoundDevice = value;\r
-                       //}\r
                }\r
                public static ESoundDeviceType SoundDeviceType\r
                {\r
                        get; set;\r
-                       //get\r
-                       //{\r
-                       //    return _SoundDeviceType;\r
-                       //}\r
-                       //set\r
-                       //{\r
-                       //    _SoundDeviceType = value;\r
-                       //}\r
                }\r
                public static CSoundTimer rc演奏用タイマ = null;\r
+               public static bool bUseOSTimer = false;         // OSのタイマーを使うか、CSoundTimerを使うか。DTXCではfalse, DTXManiaではtrue。\r
+                                                                                                       // DTXC(DirectSound)でCSoundTimerを使うと、内部で無音のループサウンドを再生するため\r
+                                                                                                       // サウンドデバイスを占有してしまい、Viewerとして呼び出されるDTXManiaで、ASIOが使えなくなる。\r
 \r
+                                                                                                       // DTXMania単体でこれをtrueにすると、WASAPI/ASIO時に演奏タイマーとしてFDKタイマーではなく\r
+                                                                                                       // システムのタイマーを使うようになる。こうするとスクロールは滑らかになるが、音ズレが出るかもしれない。\r
+               \r
                public static IntPtr WindowHandle;\r
 \r
+               public static bool bIsTimeStretch = false;\r
+\r
+               private static int _nMasterVolume;\r
+               public int nMasterVolume\r
+               {\r
+                       get\r
+                       {\r
+                               return _nMasterVolume;\r
+                       }\r
+                       set\r
+                       {\r
+                               SoundDevice.nMasterVolume = value;\r
+                               _nMasterVolume = value;\r
+                       }\r
+               }\r
+\r
+               ///// <summary>\r
+               ///// BASS時、mp3をストリーミング再生せずに、デコードしたraw wavをオンメモリ再生する場合はtrueにする。\r
+               ///// 特殊なmp3を使用時はシークが乱れるので、必要に応じてtrueにすること。(Config.iniのNoMP3Streamingで設定可能。)\r
+               ///// ただし、trueにすると、その分再生開始までの時間が長くなる。\r
+               ///// </summary>\r
+               //public static bool bIsMP3DecodeByWindowsCodec = false;\r
+\r
                public static int nMixing = 0;\r
                public int GetMixingStreams()\r
                {\r
@@ -63,15 +75,24 @@ namespace FDK
                #region [ WASAPI/ASIO/DirectSound設定値 ]\r
                /// <summary>\r
                /// <para>WASAPI 排他モード出力における再生遅延[ms](の希望値)。最終的にはこの数値を基にドライバが決定する)。</para>\r
+               /// <para>0以下の値を指定すると、この数値はWASAPI初期化時に自動設定する。正数を指定すると、その値を設定しようと試みる。</para>\r
                /// </summary>\r
                public static int SoundDelayExclusiveWASAPI = 0;                // SSTでは、50ms\r
+               public int GetSoundExclusiveWASAPI()\r
+               {\r
+                       return SoundDelayExclusiveWASAPI;\r
+               }\r
+               public void SetSoundDelayExclusiveWASAPI( int value )\r
+               {\r
+                       SoundDelayExclusiveWASAPI = value;\r
+               }\r
                /// <summary>\r
                /// <para>WASAPI 共有モード出力における再生遅延[ms]。ユーザが決定する。</para>\r
                /// </summary>\r
                public static int SoundDelaySharedWASAPI = 100;\r
                /// <summary>\r
                /// <para>排他WASAPIバッファの更新間隔。出力間隔ではないので注意。</para>\r
-               /// <para>SoundDelay よりも小さい値であること。(小さすぎる場合はBASSによって自動修正される。)</para>\r
+               /// <para>→ 自動設定されるのでSoundDelay よりも小さい値であること。(小さすぎる場合はBASSによって自動修正される。)</para>\r
                /// </summary>\r
                public static int SoundUpdatePeriodExclusiveWASAPI = 6;\r
                /// <summary>\r
@@ -86,52 +107,105 @@ namespace FDK
                /// <summary>\r
                /// <para>ASIO 出力におけるバッファサイズ。</para>\r
                /// </summary>\r
-               public static int SoundBufferSizeASIO = 0;                                              // 0にすると、デバイスの設定値をそのまま使う。\r
-               public int GetSoundBufferSizeASIO()\r
+               public static int SoundDelayASIO = 0;                                           // 0にすると、デバイスの設定値をそのまま使う。\r
+               public int GetSoundDelayASIO()\r
+               {\r
+                       return SoundDelayASIO;\r
+               }\r
+               public void SetSoundDelayASIO(int value)\r
+               {\r
+                       SoundDelayASIO = value;\r
+               }\r
+               public static int ASIODevice = 0;\r
+               public int GetASIODevice()\r
                {\r
-                       return SoundBufferSizeASIO;\r
+                       return ASIODevice;\r
                }\r
-               public void SetSoundBufferSizeASIO(int value)\r
+               public void SetASIODevice(int value)\r
                {\r
-                       SoundBufferSizeASIO = value;\r
+                       ASIODevice = value;\r
                }\r
                /// <summary>\r
                /// <para>DirectSound 出力における再生遅延[ms]。ユーザが決定する。</para>\r
                /// </summary>\r
                public static int SoundDelayDirectSound = 100;\r
 \r
-               \r
+               public long GetSoundDelay()\r
+               {\r
+                       if ( SoundDevice != null )\r
+                       {\r
+                               return SoundDevice.n実バッファサイズms;\r
+                       }\r
+                       else\r
+                       {\r
+                               return -1;\r
+                       }\r
+               }\r
+\r
                #endregion\r
 \r
 \r
-       /// <summary>\r
-       /// コンストラクタ\r
-       /// </summary>\r
-       /// <param name="handle"></param>\r
-               public CSound管理( IntPtr handle, ESoundDeviceType soundDeviceType )\r
+               /// <summary>\r
+               /// DTXC用コンストラクタ\r
+               /// </summary>\r
+               /// <param name="handle"></param>\r
+               public CSound管理( IntPtr handle )    // #30803 従来のコンストラクタ相当のI/Fを追加。(DTXC用)\r
                {\r
                        WindowHandle = handle;\r
-                       //cMixerManager = new CBassMixerManager();\r
-                       //thMixerManager = new Thread( new ThreadStart( cMixerManager.Start ) );\r
-\r
-                       t初期化( soundDeviceType );\r
+                       SoundDevice = null;\r
+                       bUseOSTimer = true;\r
+                       t初期化( ESoundDeviceType.DirectSound, 0, 0, 0 );\r
+               }\r
+               /// <summary>\r
+               /// DTXMania用コンストラクタ\r
+               /// </summary>\r
+               /// <param name="handle"></param>\r
+               /// <param name="soundDeviceType"></param>\r
+               /// <param name="nSoundDelayExclusiveWASAPI"></param>\r
+               /// <param name="nSoundDelayASIO"></param>\r
+               /// <param name="nASIODevice"></param>\r
+               public CSound管理( IntPtr handle, ESoundDeviceType soundDeviceType, int nSoundDelayExclusiveWASAPI, int nSoundDelayASIO, int nASIODevice, bool _bUseOSTimer )\r
+               {\r
+                       WindowHandle = handle;\r
+                       SoundDevice = null;\r
+                       //bUseOSTimer = false;\r
+                       t初期化( soundDeviceType, nSoundDelayExclusiveWASAPI, nSoundDelayASIO, nASIODevice, _bUseOSTimer );\r
                }\r
                public void Dispose()\r
                {\r
                        t終了();\r
                }\r
 \r
-               public static void t初期化()\r
+               //public static void t初期化()\r
+               //{\r
+               //    t初期化( ESoundDeviceType.DirectSound, 0, 0, 0 );\r
+               //}\r
+\r
+               public void t初期化( ESoundDeviceType soundDeviceType, int _nSoundDelayExclusiveWASAPI, int _nSoundDelayASIO, int _nASIODevice, IntPtr handle )\r
+               {\r
+                       //if ( !bInitialized )\r
+                       {\r
+                               WindowHandle = handle;\r
+                               t初期化( soundDeviceType, _nSoundDelayExclusiveWASAPI, _nSoundDelayASIO, _nASIODevice );\r
+                               //bInitialized = true;\r
+                       }\r
+               }\r
+               public void t初期化( ESoundDeviceType soundDeviceType, int _nSoundDelayExclusiveWASAPI, int _nSoundDelayASIO, int _nASIODevice )\r
                {\r
-                       t初期化( ESoundDeviceType.DirectSound );\r
+                       t初期化( soundDeviceType, _nSoundDelayExclusiveWASAPI, _nSoundDelayASIO, _nASIODevice, false );\r
                }\r
 \r
-               public static void t初期化( ESoundDeviceType soundDeviceType )\r
+               public void t初期化( ESoundDeviceType soundDeviceType, int _nSoundDelayExclusiveWASAPI, int _nSoundDelayASIO, int _nASIODevice, bool _bUseOSTimer )\r
                {\r
-                       SoundDevice = null;                                                     // ユーザ依存\r
+                       //SoundDevice = null;                                           // 後で再初期化することがあるので、null初期化はコンストラクタに回す\r
                        rc演奏用タイマ = null;                                            // Global.Bass 依存(つまりユーザ依存)\r
                        nMixing = 0;\r
 \r
+                       SoundDelayExclusiveWASAPI = _nSoundDelayExclusiveWASAPI;\r
+                       SoundDelayASIO = _nSoundDelayASIO;\r
+                       ASIODevice = _nASIODevice;\r
+                       bUseOSTimer = _bUseOSTimer;\r
+\r
                        ESoundDeviceType[] ESoundDeviceTypes = new ESoundDeviceType[ 4 ]\r
                        {\r
                                ESoundDeviceType.ExclusiveWASAPI,\r
@@ -168,11 +242,31 @@ namespace FDK
                                        Trace.TraceInformation( e.Message );\r
                                        if ( ESoundDeviceTypes[ n初期デバイス ] == ESoundDeviceType.Unknown )\r
                                        {\r
-                                               throw new Exception( string.Format( "サウンドデバイスの初期化に失敗しました。" ) );\r
+                                               Trace.TraceError( string.Format( "サウンドデバイスの初期化に失敗しました。" ) );\r
+                                               break;\r
                                        }\r
                                }\r
                        }\r
+                       if ( soundDeviceType == ESoundDeviceType.ExclusiveWASAPI || soundDeviceType == ESoundDeviceType.ASIO )\r
+                       {\r
+                               //Bass.BASS_SetConfig( BASSConfig.BASS_CONFIG_UPDATETHREADS, 4 );\r
+                               //Bass.BASS_SetConfig( BASSConfig.BASS_CONFIG_UPDATEPERIOD, 0 );\r
+\r
+                               Trace.TraceInformation( "BASS_CONFIG_UpdatePeriod=" + Bass.BASS_GetConfig( BASSConfig.BASS_CONFIG_UPDATEPERIOD ) );\r
+                               Trace.TraceInformation( "BASS_CONFIG_UpdateThreads=" + Bass.BASS_GetConfig( BASSConfig.BASS_CONFIG_UPDATETHREADS ) );\r
+                       }\r
+               }\r
+\r
+               public void tDisableUpdateBufferAutomatically()\r
+               {\r
+                       //Bass.BASS_SetConfig( BASSConfig.BASS_CONFIG_UPDATETHREADS, 0 );\r
+                       //Bass.BASS_SetConfig( BASSConfig.BASS_CONFIG_UPDATEPERIOD, 0 );\r
+\r
+                       //Trace.TraceInformation( "BASS_CONFIG_UpdatePeriod=" + Bass.BASS_GetConfig( BASSConfig.BASS_CONFIG_UPDATEPERIOD ) );\r
+                       //Trace.TraceInformation( "BASS_CONFIG_UpdateThreads=" + Bass.BASS_GetConfig( BASSConfig.BASS_CONFIG_UPDATETHREADS ) );\r
                }\r
+\r
+\r
                public static void t終了()\r
                {\r
                        C共通.tDisposeする( SoundDevice ); SoundDevice = null;\r
@@ -188,7 +282,7 @@ namespace FDK
                        {\r
                                // すでに生成済みのサウンドがあれば初期状態に戻す。\r
 \r
-                               CSound.tすべてのサウンドを初期状態に戻す();\r
+                               CSound.tすべてのサウンドを初期状態に戻す();             // リソースは解放するが、CSoundのインスタンスは残す。\r
 \r
 \r
                                // サウンドデバイスと演奏タイマを解放する。\r
@@ -212,11 +306,11 @@ namespace FDK
                                        break;\r
 \r
                                case ESoundDeviceType.ASIO:\r
-                                       SoundDevice = new CSoundDeviceASIO( SoundBufferSizeASIO );\r
+                                       SoundDevice = new CSoundDeviceASIO( SoundDelayASIO, ASIODevice );\r
                                        break;\r
 \r
                                case ESoundDeviceType.DirectSound:\r
-                                       SoundDevice = new CSoundDeviceDirectSound( WindowHandle, SoundDelayDirectSound );\r
+                                       SoundDevice = new CSoundDeviceDirectSound( WindowHandle, SoundDelayDirectSound, bUseOSTimer );\r
                                        break;\r
 \r
                                default:\r
@@ -230,11 +324,12 @@ namespace FDK
                        //-----------------\r
                        #endregion\r
 \r
+                       SoundDevice.nMasterVolume = _nMasterVolume;                                     // サウンドデバイスに対して、マスターボリュームを再設定する\r
+\r
                        CSound.tすべてのサウンドを再構築する( SoundDevice );              // すでに生成済みのサウンドがあれば作り直す。\r
                }\r
                public CSound tサウンドを生成する( string filename )\r
                {\r
-//Debug.WriteLine( "★★tサウンドを生成する()" + SoundDevice.e出力デバイス + " " + Path.GetFileName( filename ) );\r
                        if ( SoundDeviceType == ESoundDeviceType.Unknown )\r
                        {\r
                                throw new Exception( string.Format( "未対応の SoundDeviceType です。[{0}]", SoundDeviceType.ToString() ) );\r
@@ -242,21 +337,36 @@ namespace FDK
                        return SoundDevice.tサウンドを作成する( filename );\r
                }\r
 \r
+               private static DateTime lastUpdateTime = DateTime.MinValue;\r
+               public void t再生中の処理をする( object o )                    // #26122 2011.9.1 yyagi; delegate経由の呼び出し用\r
+               {\r
+                       t再生中の処理をする();\r
+               }\r
                public void t再生中の処理をする()\r
                {\r
 //★★★★★★★★★★★★★★★★★★★★★ダミー★★★★★★★★★★★★★★★★★★\r
 //                     Debug.Write( "再生中の処理をする()" );\r
+                       //DateTime now = DateTime.Now;\r
+                       //TimeSpan ts = now - lastUpdateTime;\r
+                       //if ( ts.Milliseconds > 5 )\r
+                       //{\r
+                       //    bool b = Bass.BASS_Update( 100 * 2 );\r
+                       //    lastUpdateTime = DateTime.Now;\r
+                       //    if ( !b )\r
+                       //    {\r
+                       //        Trace.TraceInformation( "BASS_UPdate() failed: " + Bass.BASS_ErrorGetCode().ToString() );\r
+                       //    }\r
+                       //}\r
                }\r
 \r
                public void tサウンドを破棄する( CSound csound )\r
                {\r
-                       csound.t解放する();\r
+                       csound.t解放する( true );                   // インスタンスは存続→破棄にする。\r
                        csound = null;\r
                }\r
 \r
                public float GetCPUusage()\r
                {\r
-                       //float f = Bass.BASS_GetCPU();\r
                        float f;\r
                        switch ( SoundDeviceType )\r
                        {\r
@@ -274,8 +384,6 @@ namespace FDK
                                        f = 0.0f;\r
                                        break;\r
                        }\r
-                       \r
-                       //Debug.WriteLine( "cpu=" + f );\r
                        return f;\r
                }\r
 \r
@@ -295,9 +403,17 @@ namespace FDK
                        }\r
                }\r
 \r
-               //private CBassMixerManager cMixerManager = null;\r
-               //private Thread thMixerManager = null;\r
-\r
+               public void AddMixer( CSound cs, double db再生速度, bool _b演奏終了後も再生が続くチップである )\r
+               {\r
+                       cs.b演奏終了後も再生が続くチップである = _b演奏終了後も再生が続くチップである;\r
+                       cs.db再生速度 = db再生速度;\r
+                       cs.tBASSサウンドをミキサーに追加する();\r
+               }\r
+               public void AddMixer( CSound cs, double db再生速度 )\r
+               {\r
+                       cs.db再生速度 = db再生速度;\r
+                       cs.tBASSサウンドをミキサーに追加する();\r
+               }\r
                public void AddMixer( CSound cs )\r
                {\r
                        cs.tBASSサウンドをミキサーに追加する();\r
@@ -306,13 +422,13 @@ namespace FDK
                {\r
                        cs.tBASSサウンドをミキサーから削除する();\r
                }\r
-}\r
+       }\r
        #endregion\r
 \r
        // CSound は、サウンドデバイスが変更されたときも、インスタンスを再作成することなく、新しいデバイスで作り直せる必要がある。\r
        // そのため、デバイスごとに別のクラスに分割するのではなく、1つのクラスに集約するものとする。\r
 \r
-       public class CSound : IDisposable\r
+       public class CSound : IDisposable, ICloneable\r
        {\r
                #region [ DTXMania用拡張 ]\r
                public int n総演奏時間ms\r
@@ -346,10 +462,10 @@ namespace FDK
                                        }\r
                                        else\r
                                        {\r
-                                               if ( b再生中 )\r
-                                               {\r
+//                                             if ( b再生中 )       // #30838 2012.2.24 yyagi (delete b再生中)\r
+//                                             {\r
                                                        this.Buffer.Frequency = ( int ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 );\r
-                                               }\r
+//                                             }\r
                                        }\r
                                }\r
                        }\r
@@ -365,25 +481,45 @@ namespace FDK
                                if ( _db再生速度 != value )\r
                                {\r
                                        _db再生速度 = value;\r
+                                       bIs1倍速再生 = ( _db再生速度 == 1.000f );\r
                                        if ( bBASSサウンドである )\r
                                        {\r
-                                               Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ( float ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 ) );\r
+                                               if ( _hTempoStream != 0 && !this.bIs1倍速再生 )     // 再生速度がx1.000のときは、TempoStreamを用いないようにして高速化する\r
+                                       {\r
+                                                       this.hBassStream = _hTempoStream;\r
+                                       }\r
+                                       else\r
+                                               {\r
+                                                       this.hBassStream = _hBassStream;\r
+                                       }\r
+\r
+                                               if ( CSound管理.bIsTimeStretch )\r
+                                               {\r
+                                                       Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_TEMPO, (float) ( db再生速度 * 100 - 100 ) );\r
+                                                       //double seconds = Bass.BASS_ChannelBytes2Seconds( this.hTempoStream, nBytes );\r
+                                                       //this.n総演奏時間ms = (int) ( seconds * 1000 );\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ( float ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 ) );\r
+                                               }\r
                                        }\r
                                        else\r
                                        {\r
-                                               if ( b再生中 )\r
-                                               {\r
+//                                             if ( b再生中 )       // #30838 2012.2.24 yyagi (delete b再生中)\r
+//                                             {\r
                                                        this.Buffer.Frequency = ( int ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 );\r
-                                               }\r
+//                                             }\r
                                        }\r
                                }\r
                        }\r
                }\r
                #endregion\r
 \r
+               public bool b演奏終了後も再生が続くチップである = false;       // これがtrueなら、本サウンドの再生終了のコールバック時に自動でミキサーから削除する\r
 \r
-               private STREAMPROC _cbStreamXA;         // make it global, so that the GC can not remove it\r
-//             private SYNCPROC _cbEndofStream;        // ストリームの終端まで再生されたときに呼び出されるコールバック\r
+               //private STREAMPROC _cbStreamXA;               // make it global, so that the GC can not remove it\r
+               private SYNCPROC _cbEndofStream;        // ストリームの終端まで再生されたときに呼び出されるコールバック\r
 //             private WaitCallback _cbRemoveMixerChannel;\r
 \r
                /// <summary>\r
@@ -479,7 +615,7 @@ namespace FDK
                                        }\r
                                        else if( this._n位置 == 100 )\r
                                        {\r
-                                               this._n位置db = 100000;\r
+                                               this._n位置db = 10000;\r
                                        }\r
                                        else if( this._n位置 < 0 )\r
                                        {\r
@@ -498,7 +634,8 @@ namespace FDK
                /// <summary>\r
                /// <para>DirectSoundのセカンダリバッファ。</para>\r
                /// </summary>\r
-               public SecondarySoundBuffer DirectSoundBuffer\r
+               //public SecondarySoundBuffer DirectSoundBuffer\r
+               public SoundBuffer DirectSoundBuffer\r
                {\r
                        get { return this.Buffer; }\r
                }\r
@@ -518,6 +655,15 @@ namespace FDK
                /// </summary>\r
                public static List<CSound> listインスタンス = new List<CSound>();\r
 \r
+               public static void ShowAllCSoundFiles()\r
+               {\r
+                       int i = 0;\r
+                       foreach ( CSound cs in listインスタンス )\r
+                       {\r
+                               Debug.WriteLine( i++.ToString( "d3" ) + ": " + Path.GetFileName( cs.strファイル名 ) );\r
+                       }\r
+               }\r
+\r
                public CSound()\r
                {\r
                        this.n音量 = 100;\r
@@ -526,8 +672,23 @@ namespace FDK
                        this._db再生速度 = 1.0;\r
                        this.DirectSoundBufferFlags = CSoundDeviceDirectSound.DefaultFlags;\r
 //                     this._cbRemoveMixerChannel = new WaitCallback( RemoveMixerChannelLater );\r
+                       this._hBassStream = -1;\r
+                       this._hTempoStream = 0;\r
                }\r
 \r
+               public object Clone()\r
+               {\r
+                       if ( !bDirectSoundである )\r
+                       {\r
+                               throw new NotImplementedException();\r
+                       }\r
+                       CSound clone = (CSound) MemberwiseClone();      // これだけだとCY連打が途切れる&タイトルに戻る際にNullRef例外発生\r
+                       this.DirectSound.DuplicateSoundBuffer( this.Buffer, out clone.Buffer );\r
+\r
+                       CSound.listインスタンス.Add( clone );                     // インスタンスリストに登録。\r
+\r
+                       return clone;\r
+               }\r
                public void tASIOサウンドを作成する( string strファイル名, int hMixer )\r
                {\r
                        this.tBASSサウンドを作成する( strファイル名, hMixer, BASSFlag.BASS_STREAM_DECODE );\r
@@ -554,9 +715,10 @@ namespace FDK
                        this.strファイル名 = strファイル名;\r
 \r
                        if ( String.Compare( Path.GetExtension( strファイル名 ), ".xa", true ) == 0 ||\r
+                                String.Compare( Path.GetExtension( strファイル名 ), ".mp3", true ) == 0 ||\r
                                 String.Compare( Path.GetExtension( strファイル名 ), ".ogg", true ) == 0 ) // caselessで文字列比較\r
                        {\r
-                               tDirectSoundサウンドを作成するXAOGG( strファイル名, DirectSound );\r
+                               tDirectSoundサウンドを作成するXaOggMp3( strファイル名, DirectSound );\r
                                return;\r
                        }\r
 \r
@@ -612,17 +774,12 @@ namespace FDK
 \r
                        this.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, DirectSound );\r
                }\r
-               public void tDirectSoundサウンドを作成するXAOGG( string strファイル名, DirectSound DirectSound )\r
+               public void tDirectSoundサウンドを作成するXaOggMp3( string strファイル名, DirectSound DirectSound )\r
                {\r
                        this.e作成方法 = E作成方法.ファイルから;\r
                        this.strファイル名 = strファイル名;\r
 \r
 \r
-\r
-                       \r
-//                     Cxa xa = new Cxa();\r
-//                     xa.Decode( strファイル名, out this.byArrWAVファイルイメージ );\r
-\r
                        WaveFormat wfx = new WaveFormat();\r
                        int nPCMデータの先頭インデックス = 0;\r
 //                     int nPCMサイズbyte = (int) ( xa.xaheader.nSamples * xa.xaheader.nChannels * 2 );     // nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
@@ -630,7 +787,7 @@ namespace FDK
                        int nPCMサイズbyte;\r
                        CWin32.WAVEFORMATEX cw32wfx;\r
                        tオンメモリ方式でデコードする( strファイル名, out this.byArrWAVファイルイメージ,\r
-                       out nPCMデータの先頭インデックス, out nPCMサイズbyte, out cw32wfx );\r
+                       out nPCMデータの先頭インデックス, out nPCMサイズbyte, out cw32wfx, false );\r
 \r
                        wfx.AverageBytesPerSecond = (int) cw32wfx.nAvgBytesPerSec;\r
                        wfx.BitsPerSample = (short) cw32wfx.wBitsPerSample;\r
@@ -640,35 +797,14 @@ namespace FDK
                        wfx.SamplesPerSecond = (int) cw32wfx.nSamplesPerSec;\r
 \r
                        // セカンダリバッファを作成し、PCMデータを書き込む。\r
-\r
-                       this.Buffer = new SecondarySoundBuffer( DirectSound, new SoundBufferDescription()\r
-                       {\r
-                               Format = wfx,\r
-                               Flags = CSoundDeviceDirectSound.DefaultFlags,\r
-                               SizeInBytes = nPCMサイズbyte,\r
-                       } );\r
-                       this.Buffer.Write( byArrWAVファイルイメージ, nPCMデータの先頭インデックス, nPCMサイズbyte, 0, LockFlags.None );\r
-\r
-                       // 作成完了。\r
-\r
-                       this.eデバイス種別 = ESoundDeviceType.DirectSound;\r
-                       this.DirectSoundBufferFlags = CSoundDeviceDirectSound.DefaultFlags;\r
-\r
-                       // DTXMania用に追加\r
-                       this.nオリジナルの周波数 = wfx.SamplesPerSecond;\r
-\r
-                       n総演奏時間ms = ( int ) ( ( ( double ) nPCMサイズbyte ) / ( this.Buffer.Format.AverageBytesPerSecond * 0.001 ) );\r
-                       nBytes = nPCMサイズbyte;\r
-\r
-                       // インスタンスリストに登録。\r
-\r
-                       CSound.listインスタンス.Add( this );\r
-\r
+                       tDirectSoundサウンドを作成する・セカンダリバッファの作成とWAVデータ書き込み\r
+                               ( ref this.byArrWAVファイルイメージ, DirectSound, CSoundDeviceDirectSound.DefaultFlags, wfx,\r
+                                 nPCMサイズbyte, nPCMデータの先頭インデックス );\r
                }\r
 \r
                public void tDirectSoundサウンドを作成する( byte[] byArrWAVファイルイメージ, DirectSound DirectSound )\r
                {\r
-                       this.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, DirectSound, CSoundDeviceDirectSound.DefaultFlags );\r
+                       this.tDirectSoundサウンドを作成する(  byArrWAVファイルイメージ, DirectSound, CSoundDeviceDirectSound.DefaultFlags );\r
                }\r
                public void tDirectSoundサウンドを作成する( byte[] byArrWAVファイルイメージ, DirectSound DirectSound, BufferFlags flags )\r
                {\r
@@ -771,9 +907,19 @@ namespace FDK
 \r
 \r
                        // セカンダリバッファを作成し、PCMデータを書き込む。\r
+                       tDirectSoundサウンドを作成する・セカンダリバッファの作成とWAVデータ書き込み(\r
+                               ref byArrWAVファイルイメージ, DirectSound, flags, wfx, nPCMサイズbyte, nPCMデータの先頭インデックス );\r
+               }\r
 \r
-                       this.Buffer = new SecondarySoundBuffer( DirectSound, new SoundBufferDescription() {\r
-                               Format = ( wfx.FormatTag == WaveFormatTag.Pcm) ? wfx : (SlimDX.Multimedia.WaveFormatExtensible) wfx,\r
+               private void tDirectSoundサウンドを作成する・セカンダリバッファの作成とWAVデータ書き込み\r
+                       ( ref byte[] byArrWAVファイルイメージ, DirectSound DirectSound, BufferFlags flags, WaveFormat wfx,\r
+                       int nPCMサイズbyte, int nPCMデータの先頭インデックス )\r
+               {\r
+                       // セカンダリバッファを作成し、PCMデータを書き込む。\r
+\r
+                       this.Buffer = new SecondarySoundBuffer( DirectSound, new SoundBufferDescription()\r
+                       {\r
+                               Format = ( wfx.FormatTag == WaveFormatTag.Pcm ) ? wfx : (SlimDX.Multimedia.WaveFormatExtensible) wfx,\r
                                Flags = flags,\r
                                SizeInBytes = nPCMサイズbyte,\r
                        } );\r
@@ -784,11 +930,12 @@ namespace FDK
                        this.eデバイス種別 = ESoundDeviceType.DirectSound;\r
                        this.DirectSoundBufferFlags = flags;\r
                        this.byArrWAVファイルイメージ = byArrWAVファイルイメージ;\r
+                       this.DirectSound = DirectSound;\r
 \r
                        // DTXMania用に追加\r
                        this.nオリジナルの周波数 = wfx.SamplesPerSecond;\r
-                       n総演奏時間ms = ( int ) ( ( ( double ) nPCMサイズbyte ) / ( this.Buffer.Format.AverageBytesPerSecond * 0.001 ) );\r
-                       \r
+                       n総演奏時間ms = (int) ( ( (double) nPCMサイズbyte ) / ( this.Buffer.Format.AverageBytesPerSecond * 0.001 ) );\r
+\r
 \r
                        // インスタンスリストに登録。\r
 \r
@@ -820,7 +967,7 @@ namespace FDK
                                }\r
                        }\r
                        t再生位置を先頭に戻す();\r
-                       tサウンドを再生する();\r
+                       tサウンドを再生する( bループする );\r
                }\r
                public void t再生を停止する()\r
                {\r
@@ -886,59 +1033,67 @@ namespace FDK
 \r
                public void t解放する()\r
                {\r
+                       t解放する( false );\r
+               }\r
+\r
+               public void t解放する( bool _bインスタンス削除 )\r
+               {\r
                        if ( this.bBASSサウンドである )          // stream数の削減用\r
                        {\r
                                tBASSサウンドをミキサーから削除する();\r
-                               //_cbEndofStream = null;\r
+                               _cbEndofStream = null;\r
                                //_cbStreamXA = null;\r
                                CSound管理.nStreams--;\r
                        }\r
                        bool bManagedも解放する = true;\r
-                       bool bインスタンス削除 = false;                                                         // インスタンスは存続する。\r
+                       bool bインスタンス削除 = _bインスタンス削除;    // CSoundの再初期化時は、インスタンスは存続する。\r
                        this.Dispose( bManagedも解放する, bインスタンス削除 );\r
+//Debug.WriteLine( "Disposed: " + _bインスタンス削除 + " : " + Path.GetFileName( this.strファイル名 ) );\r
                }\r
                public void tサウンドを再生する()\r
                {\r
-//Debug.WriteLine( "tサウンドを再生する(): " + this.strファイル名 );\r
-                       if( this.bBASSサウンドである )\r
+                       tサウンドを再生する( false );\r
+               }\r
+               public void tサウンドを再生する( bool bループする )\r
+               {\r
+                       if ( this.bBASSサウンドである )                  // BASSサウンド時のループ処理は、t再生を開始する()側に実装。ここでは「bループする」は未使用。\r
                        {\r
 //Debug.WriteLine( "再生中?: " +  System.IO.Path.GetFileName(this.strファイル名) + " status=" + BassMix.BASS_Mixer_ChannelIsActive( this.hBassStream ) + " current=" + BassMix.BASS_Mixer_ChannelGetPosition( this.hBassStream ) + " nBytes=" + nBytes );\r
                                bool b = BassMix.BASS_Mixer_ChannelPlay( this.hBassStream );\r
                                if ( !b )\r
                                {\r
-Debug.WriteLine( "再生しようとしたが、Mixerに登録されていなかった: " + Path.GetFileName( this.strファイル名 ) + ", " + hBassStream );\r
-//Debug.WriteLine( "ErrCode= " +Bass.BASS_ErrorGetCode() );\r
+//Debug.WriteLine( "再生しようとしたが、Mixerに登録されていなかった: " + Path.GetFileName( this.strファイル名 ) + ", stream#=" + this.hBassStream + ", ErrCode=" + Bass.BASS_ErrorGetCode() );\r
 \r
                                        bool bb = tBASSサウンドをミキサーに追加する();\r
                                        if ( !bb )\r
                                        {\r
-Debug.WriteLine( "Mixerへの登録に失敗: " + Path.GetFileName( this.strファイル名 ) + "" + Bass.BASS_ErrorGetCode() );\r
+Debug.WriteLine( "Mixerへの登録に失敗: " + Path.GetFileName( this.strファイル名 ) + ", ErrCode=" + Bass.BASS_ErrorGetCode() );\r
                                        }\r
                                        else\r
                                        {\r
-Debug.WriteLine( "Mixerへの登録に成功: " + Path.GetFileName( this.strファイル名 ) + ": " + Bass.BASS_ErrorGetCode() );\r
+//Debug.WriteLine( "Mixerへの登録に成功: " + Path.GetFileName( this.strファイル名 ) + ": " + Bass.BASS_ErrorGetCode() );\r
                                        }\r
                                        //this.t再生位置を先頭に戻す();\r
 \r
                                        bool bbb = BassMix.BASS_Mixer_ChannelPlay( this.hBassStream );\r
                                        if (!bbb)\r
                                        {\r
-                                               Debug.WriteLine("更に再生に失敗                                 : " + Path.GetFileName(this.strファイル名));\r
-                                               Debug.WriteLine("ErrCode= " + Bass.BASS_ErrorGetCode());\r
+Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイル名) + ", ErrCode=" + Bass.BASS_ErrorGetCode() );\r
                                        }\r
                                        else\r
                                        {\r
-                                               Debug.WriteLine("再生成功(ミキサー追加後)                       : " + Path.GetFileName(this.strファイル名));\r
+//                                             Debug.WriteLine("再生成功(ミキサー追加後)                       : " + Path.GetFileName(this.strファイル名));\r
                                        }\r
                                }\r
                                else\r
                                {\r
-Debug.WriteLine( "再生成功: " + Path.GetFileName( this.strファイル名 ) + " (" + hBassStream + ")" );\r
+//Debug.WriteLine( "再生成功: " + Path.GetFileName( this.strファイル名 ) + " (" + hBassStream + ")" );\r
                                }\r
                        }\r
                        else if( this.bDirectSoundである )\r
                        {\r
-                               this.Buffer.Play( 0, PlayFlags.None );\r
+                               PlayFlags pf = ( bループする ) ? PlayFlags.Looping : PlayFlags.None;\r
+                               this.Buffer.Play( 0, pf );\r
                        }\r
                }\r
                public void tサウンドを先頭から再生する()\r
@@ -946,6 +1101,14 @@ Debug.WriteLine( "再生成功: " + Path.GetFileName( this.strファイル名 )
                        this.t再生位置を先頭に戻す();\r
                        this.tサウンドを再生する();\r
                }\r
+               public void tサウンドを停止してMixerからも削除する()\r
+               {\r
+                       tサウンドを停止する( false );\r
+                       if ( bBASSサウンドである )\r
+                       {\r
+                               tBASSサウンドをミキサーから削除する();\r
+                       }\r
+               }\r
                public void tサウンドを停止する()\r
                {\r
                        tサウンドを停止する( false );\r
@@ -954,7 +1117,7 @@ Debug.WriteLine( "再生成功: " + Path.GetFileName( this.strファイル名 )
                {\r
                        if( this.bBASSサウンドである )\r
                        {\r
-Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル名 ) + " status=" + BassMix.BASS_Mixer_ChannelIsActive( this.hBassStream ) + " current=" + BassMix.BASS_Mixer_ChannelGetPosition( this.hBassStream ) + " nBytes=" + nBytes );\r
+//Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル名 ) + " status=" + BassMix.BASS_Mixer_ChannelIsActive( this.hBassStream ) + " current=" + BassMix.BASS_Mixer_ChannelGetPosition( this.hBassStream ) + " nBytes=" + nBytes );\r
                                BassMix.BASS_Mixer_ChannelPause( this.hBassStream );\r
                                if ( !pause )\r
                                {\r
@@ -963,8 +1126,17 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                        }\r
                        else if( this.bDirectSoundである )\r
                        {\r
-                               this.Buffer.Stop();\r
+                               try\r
+                               {\r
+                                       this.Buffer.Stop();\r
+                               }\r
+                               catch ( Exception )\r
+                               {\r
+                                       // WASAPI/ASIOとDirectSoundを同時使用すると、Bufferがlostしてここで例外発生する。→ catchして無視する。\r
+                                       // DTXCからDTXManiaを呼び出すと、DTXC終了時にこの現象が発生する。\r
+                               }\r
                        }\r
+                       this.n一時停止回数 = 0;\r
                }\r
                \r
                public void t再生位置を先頭に戻す()\r
@@ -972,7 +1144,7 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                        if( this.bBASSサウンドである )\r
                        {\r
                                BassMix.BASS_Mixer_ChannelSetPosition( this.hBassStream, 0 );\r
-                               pos = 0;\r
+                               //pos = 0;\r
                        }\r
                        else if( this.bDirectSoundである )\r
                        {\r
@@ -981,21 +1153,46 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                }\r
                public void t再生位置を変更する( long n位置ms )\r
                {\r
-                       if( this.bBASSサウンドである )\r
+                       if ( this.bBASSサウンドである )\r
                        {\r
-                               BassMix.BASS_Mixer_ChannelSetPosition( this.hBassStream, Bass.BASS_ChannelSeconds2Bytes( this.hBassStream, n位置ms / 1000.0 ), BASSMode.BASS_POS_BYTES );\r
+                               bool b = true;\r
+                               try\r
+                               {\r
+                                       b = BassMix.BASS_Mixer_ChannelSetPosition( this.hBassStream, Bass.BASS_ChannelSeconds2Bytes( this.hBassStream, n位置ms * this.db周波数倍率 * this.db再生速度 / 1000.0 ), BASSMode.BASS_POS_BYTES );\r
+                               }\r
+                               catch ( Exception e )\r
+                               {\r
+                                       Trace.TraceInformation( Path.GetFileName( this.strファイル名 ) + ": Seek error: " + e.ToString() );\r
+                               }\r
+                               finally\r
+                               {\r
+                                       if ( !b )\r
+                                       {\r
+                                               BASSError be = Bass.BASS_ErrorGetCode();\r
+                                               Trace.TraceInformation( Path.GetFileName( this.strファイル名 ) + ": Seek error: " + be.ToString() );\r
+                                       }\r
+                               }\r
                        }\r
                        else if( this.bDirectSoundである )\r
                        {\r
-                               int n位置sample = (int) ( this.Buffer.Format.SamplesPerSecond * n位置ms * 0.001 );\r
-                               this.Buffer.CurrentPlayPosition = n位置sample * this.Buffer.Format.BlockAlignment;\r
+                               int n位置sample = (int) ( this.Buffer.Format.SamplesPerSecond * n位置ms * 0.001 * _db周波数倍率 * _db再生速度 );   // #30839 2013.2.24 yyagi; add _db周波数倍率 and _db再生速度\r
+                               try\r
+                               {\r
+                                       this.Buffer.CurrentPlayPosition = n位置sample * this.Buffer.Format.BlockAlignment;\r
+                               }\r
+                               catch ( DirectSoundException e )\r
+                               {\r
+                                       Trace.TraceError( "{0}: Seek error: {1}", Path.GetFileName( this.strファイル名 ), n位置ms, e.Message );\r
+                               }\r
                        }\r
                }\r
 \r
                public static void tすべてのサウンドを初期状態に戻す()\r
                {\r
-                       foreach( var sound in CSound.listインスタンス )\r
-                               sound.t解放する();\r
+                       foreach ( var sound in CSound.listインスタンス )\r
+                       {\r
+                               sound.t解放する( false );\r
+                       }\r
                }\r
                public static void tすべてのサウンドを再構築する( ISoundDevice device )\r
                {\r
@@ -1056,15 +1253,33 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                        {\r
                                #region [ ASIO, WASAPI の解放 ]\r
                                //-----------------\r
-                               BassMix.BASS_Mixer_ChannelRemove( this.hBassStream );\r
-                               Bass.BASS_StreamFree( this.hBassStream );\r
+                               if ( _hTempoStream != 0 )\r
+                               {\r
+                                       BassMix.BASS_Mixer_ChannelRemove( this._hTempoStream );\r
+                                       Bass.BASS_StreamFree( this._hTempoStream );\r
+                               }\r
+                               BassMix.BASS_Mixer_ChannelRemove( this._hBassStream );\r
+                               Bass.BASS_StreamFree( this._hBassStream );\r
                                this.hBassStream = -1;\r
+                               this._hBassStream = -1;\r
+                               this._hTempoStream = 0;\r
                                //-----------------\r
                                #endregion\r
                        }\r
 \r
                        if( bManagedも解放する )\r
                        {\r
+                               //int freeIndex = -1;\r
+\r
+                               //if ( CSound.listインスタンス != null )\r
+                               //{\r
+                               //    freeIndex = CSound.listインスタンス.IndexOf( this );\r
+                               //    if ( freeIndex == -1 )\r
+                               //    {\r
+                               //        Debug.WriteLine( "ERR: freeIndex==-1 : Count=" + CSound.listインスタンス.Count + ", filename=" + Path.GetFileName( this.strファイル名 ) );\r
+                               //    }\r
+                               //}\r
+\r
                                if( this.eデバイス種別 == ESoundDeviceType.DirectSound )\r
                                {\r
                                        #region [ DirectSound の解放 ]\r
@@ -1088,12 +1303,34 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                                if( this.e作成方法 == E作成方法.WAVファイルイメージから &&\r
                                        this.eデバイス種別 != ESoundDeviceType.DirectSound )      // DirectSound は hGC 未使用。\r
                                {\r
-                                       this.hGC.Free();\r
-                                       this.hGC = default( GCHandle );\r
+                                       if ( this.hGC != null && this.hGC.IsAllocated )\r
+                                       {\r
+                                               this.hGC.Free();\r
+                                               this.hGC = default( GCHandle );\r
+                                       }\r
+                               }\r
+                               if ( this.byArrWAVファイルイメージ != null )\r
+                               {\r
+                                       this.byArrWAVファイルイメージ = null;\r
                                }\r
 \r
-                               if( bインスタンス削除 )\r
-                                       CSound.listインスタンス.Remove( this );\r
+                               if ( bインスタンス削除 )\r
+                               {\r
+                                       //try\r
+                                       //{\r
+                                       //    CSound.listインスタンス.RemoveAt( freeIndex );\r
+                                       //}\r
+                                       //catch\r
+                                       //{\r
+                                       //    Debug.WriteLine( "FAILED to remove CSound.listインスタンス: Count=" + CSound.listインスタンス.Count + ", filename=" + Path.GetFileName( this.strファイル名 ) );\r
+                                       //}\r
+                                       bool b = CSound.listインスタンス.Remove( this );  // これだと、Clone()したサウンドのremoveに失敗する\r
+                                       if ( !b )\r
+                                       {\r
+                                               Debug.WriteLine( "FAILED to remove CSound.listインスタンス: Count=" + CSound.listインスタンス.Count + ", filename=" + Path.GetFileName( this.strファイル名 ) );\r
+                                       }\r
+\r
+                               }\r
                        }\r
                }\r
                ~CSound()\r
@@ -1108,11 +1345,34 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                protected enum E作成方法 { ファイルから, WAVファイルイメージから, Unknown }\r
                protected E作成方法 e作成方法 = E作成方法.Unknown;\r
                protected ESoundDeviceType eデバイス種別 = ESoundDeviceType.Unknown;\r
-               protected string strファイル名 = null;\r
+               public string strファイル名 = null;\r
                protected byte[] byArrWAVファイルイメージ = null;       // WAVファイルイメージ、もしくはchunkのDATA部のみ\r
                protected GCHandle hGC;\r
-               protected int hBassStream = -1;                                 // ASIO, WASAPI 用\r
-               protected SecondarySoundBuffer Buffer = null;   // DirectSound 用\r
+               protected int _hTempoStream = 0;\r
+               protected int _hBassStream = -1;                                        // ASIO, WASAPI 用\r
+               protected int hBassStream = 0;                                          // #31076 2013.4.1 yyagi; プロパティとして実装すると動作が低速になったため、\r
+                                                                                                                       // tBASSサウンドを作成する・ストリーム生成後の共通処理()のタイミングと、\r
+                                                                                                                       // 再生速度を変更したタイミングでのみ、\r
+                                                                                                                       // hBassStreamを更新するようにした。\r
+               //{\r
+               //    get\r
+               //    {\r
+               //        if ( _hTempoStream != 0 && !this.bIs1倍速再生 )   // 再生速度がx1.000のときは、TempoStreamを用いないようにして高速化する\r
+               //        {\r
+               //            return _hTempoStream;\r
+               //        }\r
+               //        else\r
+               //        {\r
+               //            return _hBassStream;\r
+               //        }\r
+               //    }\r
+               //    set\r
+               //    {\r
+               //        _hBassStream = value;\r
+               //    }\r
+               //}\r
+               protected SoundBuffer Buffer = null;                    // DirectSound 用\r
+               protected DirectSound DirectSound;\r
                protected int hMixer = -1;      // 設計壊してゴメン Mixerに後で登録するときに使う\r
                //-----------------\r
                #endregion\r
@@ -1142,14 +1402,29 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                private int nオリジナルの周波数 = 0;\r
                private double _db周波数倍率 = 1.0;\r
                private double _db再生速度 = 1.0;\r
+               private bool bIs1倍速再生 = true;\r
 \r
                private void tBASSサウンドを作成する( string strファイル名, int hMixer, BASSFlag flags )\r
                {\r
-                       if ( String.Compare( Path.GetExtension( strファイル名 ), ".xa", true ) == 0 )      // caselessで文字列比較\r
+                       #region [ xaとwav(RIFF chunked vorbis)に対しては専用の処理をする ]\r
+                       switch ( Path.GetExtension( strファイル名 ).ToLower() )\r
                        {\r
-                               tBASSサウンドを作成するXA( strファイル名, hMixer, flags );\r
-                               return;\r
-                       }\r
+                               case ".xa":\r
+                                       tBASSサウンドを作成するXA( strファイル名, hMixer, flags );\r
+                                       return;\r
+\r
+                               case ".wav":\r
+                                       if ( tRIFFchunkedVorbisならDirectShowでDecodeする( strファイル名, ref byArrWAVファイルイメージ ) )\r
+                                       {\r
+                                               tBASSサウンドを作成する( byArrWAVファイルイメージ, hMixer, flags );\r
+                                               return;\r
+                                       }\r
+                                       break;\r
+\r
+                               default:\r
+                                       break;\r
+                               }\r
+                       #endregion\r
 \r
                        this.e作成方法 = E作成方法.ファイルから;\r
                        this.strファイル名 = strファイル名;\r
@@ -1157,42 +1432,13 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
 \r
                        // BASSファイルストリームを作成。\r
 \r
-                       this.hBassStream = Bass.BASS_StreamCreateFile( strファイル名, 0, 0, flags );\r
-                       if( this.hBassStream == 0 )\r
+                       this._hBassStream = Bass.BASS_StreamCreateFile( strファイル名, 0, 0, flags );\r
+                       if( this._hBassStream == 0 )\r
                                throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_StreamCreateFile)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
-                       CSound管理.nStreams++;\r
-\r
-                       // ミキサーにBASSファイルストリームを追加。\r
-\r
-                       //if ( !BassMix.BASS_Mixer_StreamAddChannel( hMixer, this.hBassStream, BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_PAUSE | BASSFlag.BASS_MIXER_NORAMPIN ) )\r
-                       ////                    if ( !tBASSサウンドをミキサーに追加する() )\r
-                       //{\r
-                       //    hGC.Free();\r
-                       //    throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_Mixer_StreamAddChannel)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
-                       //}\r
-                       //CSound管理.nStreams++;\r
-\r
-//                     _cbEndofStream = new SYNCPROC( CallbackEndofStream );\r
-//                     Bass.BASS_ChannelSetSync( hBassStream, BASSSync.BASS_SYNC_END |BASSSync.BASS_SYNC_MIXTIME, 0, _cbEndofStream, IntPtr.Zero );\r
-\r
-\r
-                       // インスタンスリストに登録。\r
-\r
-                       CSound.listインスタンス.Add( this );\r
-\r
-                       // nBytesとn総演奏時間の取得; DTXMania用に追加。\r
-                       nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
-                       double seconds = Bass.BASS_ChannelBytes2Seconds( this.hBassStream, nBytes );\r
-                       this.n総演奏時間ms = (int) ( seconds * 1000 );\r
-                       this.pos = 0;\r
-                       this.hMixer = hMixer;\r
-                       float freq = 0.0f;\r
-                       if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ref freq ) )\r
-                       {\r
-                               hGC.Free();\r
-                               throw new Exception( string.Format( "サウンドストリームの周波数取得に失敗しました。(BASS_ChannelGetAttribute)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
-                       }\r
-                       this.nオリジナルの周波数 = (int) freq;\r
+                       \r
+                       nBytes = Bass.BASS_ChannelGetLength( this._hBassStream );\r
+                       \r
+                       tBASSサウンドを作成する・ストリーム生成後の共通処理( hMixer );\r
                }\r
                private void tBASSサウンドを作成する( byte[] byArrWAVファイルイメージ, int hMixer, BASSFlag flags )\r
                {\r
@@ -1203,42 +1449,60 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
 \r
                        // BASSファイルストリームを作成。\r
 \r
-                       this.hBassStream = Bass.BASS_StreamCreateFile( hGC.AddrOfPinnedObject(), 0, byArrWAVファイルイメージ.Length, flags );\r
-                       if( this.hBassStream == 0 )\r
+                       this._hBassStream = Bass.BASS_StreamCreateFile( hGC.AddrOfPinnedObject(), 0, byArrWAVファイルイメージ.Length, flags );\r
+                       if ( this._hBassStream == 0 )\r
                                throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_StreamCreateFile)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
-                       CSound管理.nStreams++;\r
-\r
-\r
-                       // ミキサーにBASSファイルストリームを追加。\r
-\r
-                       //if ( !BassMix.BASS_Mixer_StreamAddChannel( hMixer, this.hBassStream, BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_PAUSE | BASSFlag.BASS_MIXER_NORAMPIN ) )\r
-                       ////                    if ( !tBASSサウンドをミキサーに追加する() )\r
-                       //{\r
-                       //    hGC.Free();\r
-                       //    throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_Mixer_StreamAddChannel)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
-                       //}\r
-                       //CSound管理.nStreams++;\r
-\r
-//                     _cbEndofStream = new SYNCPROC( CallbackEndofStream );\r
-//                     Bass.BASS_ChannelSetSync( hBassStream, BASSSync.BASS_SYNC_END | BASSSync.BASS_SYNC_MIXTIME, 0, _cbEndofStream, IntPtr.Zero );\r
 \r
-                       // インスタンスリストに登録。\r
-\r
-                       CSound.listインスタンス.Add( this );\r
+                       nBytes = Bass.BASS_ChannelGetLength( this._hBassStream );\r
+       \r
+                       tBASSサウンドを作成する・ストリーム生成後の共通処理( hMixer );\r
+               }\r
+               /// <summary>\r
+               /// Decode "RIFF chunked Vorbis" to "raw wave"\r
+               /// because BASE.DLL has two problems for RIFF chunked Vorbis;\r
+               /// 1. time seek is not fine  2. delay occurs (about 10ms)\r
+               /// </summary>\r
+               /// <param name="strファイル名">wave filename</param>\r
+               /// <param name="byArrWAVファイルイメージ">wav file image</param>\r
+               /// <returns></returns>\r
+               private bool tRIFFchunkedVorbisならDirectShowでDecodeする( string strファイル名, ref byte[] byArrWAVファイルイメージ )\r
+               {\r
+                       bool bファイルにVorbisコンテナが含まれている = false;\r
 \r
-                       // nBytesとn総演奏時間の取得; DTXMania用に追加。\r
-                       nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
-                       double seconds = Bass.BASS_ChannelBytes2Seconds( this.hBassStream, nBytes );\r
-                       this.n総演奏時間ms = (int) ( seconds * 1000 );\r
-                       this.pos = 0;\r
-                       this.hMixer = hMixer;\r
-                       float freq = 0.0f;\r
-                       if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ref freq ) )\r
+                       #region [ ファイルがWAVかつ、Vorbisコンテナが含まれているかを調べ、それに該当するなら、DirectShowでデコードする。]\r
+                       //-----------------\r
+                       try\r
                        {\r
-                               hGC.Free();\r
-                               throw new Exception( string.Format( "サウンドストリームの周波数取得に失敗しました。(BASS_ChannelGetAttribute)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
+                               using ( var ws = new WaveStream( strファイル名 ) )\r
+                               {\r
+                                       if ( ws.Format.FormatTag == ( WaveFormatTag ) 0x6770 || // Ogg Vorbis Mode 2+\r
+                                                ws.Format.FormatTag == ( WaveFormatTag ) 0x6771 )      // Ogg Vorbis Mode 3+\r
+                                       {\r
+                                               Trace.TraceInformation( Path.GetFileName( strファイル名 ) + ": RIFF chunked Vorbis. Decode to raw Wave first, to avoid BASS.DLL troubles" );\r
+                                               try\r
+                                               {\r
+                                                       CDStoWAVFileImage.t変換( strファイル名, out byArrWAVファイルイメージ );\r
+                                                       bファイルにVorbisコンテナが含まれている = true;\r
+                                               }\r
+                                               catch\r
+                                               {\r
+                                                       Trace.TraceWarning( "Warning: " + Path.GetFileName( strファイル名 ) + " : RIFF chunked Vorbisのデコードに失敗しました。" );\r
+                                               }\r
+                                       }\r
+                               }\r
                        }\r
-                       this.nオリジナルの周波数 = ( int ) freq;\r
+                       catch ( InvalidDataException )\r
+                       {\r
+                               // DirectShowのデコードに失敗したら、次はACMでのデコードを試すことになるため、ここではエラーログを出さない。\r
+                               // Trace.TraceWarning( "Warning: " + Path.GetFileName( strファイル名 ) + " : デコードに失敗しました。" );\r
+                       }\r
+                       catch ( Exception e )\r
+                       {\r
+                               Trace.TraceWarning( "Warning: " + Path.GetFileName( strファイル名 ) + " : 読み込みに失敗しました。" );\r
+                       }\r
+                       #endregion\r
+\r
+                       return bファイルにVorbisコンテナが含まれている;\r
                }\r
                private void tBASSサウンドを作成するXA( string strファイル名, int hMixer, BASSFlag flags )\r
                {\r
@@ -1247,108 +1511,121 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                        int totalPCMSize;\r
 \r
                        tオンメモリ方式でデコードする( strファイル名, out this.byArrWAVファイルイメージ,\r
-                           out nPCMデータの先頭インデックス, out totalPCMSize, out wfx );\r
+                               out nPCMデータの先頭インデックス, out totalPCMSize, out wfx, true );\r
 \r
                        nBytes = totalPCMSize;\r
 \r
-                       this.e作成方法 = E作成方法.WAVファイルイメージから;\r
-                       this.strファイル名 = strファイル名; \r
+                       this.e作成方法 = E作成方法.WAVファイルイメージから;           //.ファイルから;  // 再構築時はデコード後のイメージを流用する&Dispose時にhGCを解放する\r
+                       this.strファイル名 = strファイル名;\r
                        this.hGC = GCHandle.Alloc( this.byArrWAVファイルイメージ, GCHandleType.Pinned );                // byte[] をピン留め\r
 \r
-\r
-                       _cbStreamXA = new STREAMPROC( CallbackPlayingXA );\r
+                       //_cbStreamXA = new STREAMPROC( CallbackPlayingXA );\r
 \r
                        // BASSファイルストリームを作成。\r
 \r
                        //this.hBassStream = Bass.BASS_StreamCreate( xa.xaheader.nSamplesPerSec, xa.xaheader.nChannels, BASSFlag.BASS_STREAM_DECODE, _myStreamCreate, IntPtr.Zero );\r
-                       this.hBassStream = Bass.BASS_StreamCreate( (int)wfx.nSamplesPerSec, (int)wfx.nChannels, BASSFlag.BASS_STREAM_DECODE, _cbStreamXA, IntPtr.Zero );\r
-                       if ( this.hBassStream == 0 )\r
+                       //this._hBassStream = Bass.BASS_StreamCreate( (int) wfx.nSamplesPerSec, (int) wfx.nChannels, BASSFlag.BASS_STREAM_DECODE, _cbStreamXA, IntPtr.Zero );\r
+\r
+                       // StreamCreate()で作成したstreamはseek不可のため、StreamCreateFile()を使う。\r
+                       this._hBassStream = Bass.BASS_StreamCreateFile( this.hGC.AddrOfPinnedObject(), 0L, totalPCMSize, flags );\r
+                       if ( this._hBassStream == 0 )\r
                        {\r
                                hGC.Free();\r
                                throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_SampleCreate)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
                        }\r
+\r
+                       nBytes = Bass.BASS_ChannelGetLength( this._hBassStream );\r
+\r
+\r
+                       tBASSサウンドを作成する・ストリーム生成後の共通処理( hMixer );\r
+               }\r
+\r
+\r
+               private void tBASSサウンドを作成する・ストリーム生成後の共通処理( int hMixer )\r
+               {\r
                        CSound管理.nStreams++;\r
 \r
-                       // ミキサーにBASSファイルストリームを追加\r
+                       // 個々のストリームの出力をテンポ変更のストリームに入力する。テンポ変更ストリームの出力を、Mixerに出力する\r
 \r
-                       //if ( !BassMix.BASS_Mixer_StreamAddChannel( hMixer, this.hBassStream, BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_PAUSE | BASSFlag.BASS_MIXER_NORAMPIN ) )\r
-                       ////                    if ( !tBASSサウンドをミキサーに追加する() )\r
-                       //{\r
-                       //    hGC.Free();\r
-                       //    throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_Mixer_StreamAddChannel)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
-                       //}\r
-                       //CSound管理.nStreams++;\r
+                       _hTempoStream = 0;\r
+                       if ( CSound管理.bIsTimeStretch )      // TimeStretchのON/OFFに関わりなく、テンポ変更のストリームを生成する。後からON/OFF切り替え可能とするため。\r
+                       {\r
+                               this._hTempoStream = BassFx.BASS_FX_TempoCreate( this._hBassStream, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_FX_FREESOURCE );\r
+                               if ( this._hTempoStream == 0 )\r
+                               {\r
+                                       hGC.Free();\r
+                                       throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_FX_TempoCreate)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
+                               }\r
+                               else\r
+                               {\r
+                                       Bass.BASS_ChannelSetAttribute( this._hTempoStream, BASSAttribute.BASS_ATTRIB_TEMPO_OPTION_USE_QUICKALGO, 1f );  // 高速化(音の品質は少し落ちる)\r
+                               }\r
+                       }\r
+\r
+                       if ( _hTempoStream != 0 && !this.bIs1倍速再生 )     // 再生速度がx1.000のときは、TempoStreamを用いないようにして高速化する\r
+                       {\r
+                               this.hBassStream = _hTempoStream;\r
+                       }\r
+                       else\r
+                       {\r
+                               this.hBassStream = _hBassStream;\r
+                       }\r
 \r
-//                     _cbEndofStream = new SYNCPROC( CallbackEndofStream );\r
-//                     Bass.BASS_ChannelSetSync( hBassStream, BASSSync.BASS_SYNC_END | BASSSync.BASS_SYNC_MIXTIME, 0, _cbEndofStream, IntPtr.Zero );\r
+                       // #32248 再生終了時に発火するcallbackを登録する (演奏終了後に再生終了するチップを非同期的にミキサーから削除するため。)\r
+                       _cbEndofStream = new SYNCPROC( CallbackEndofStream );\r
+                       Bass.BASS_ChannelSetSync( hBassStream, BASSSync.BASS_SYNC_END | BASSSync.BASS_SYNC_MIXTIME, 0, _cbEndofStream, IntPtr.Zero );\r
 \r
                        // インスタンスリストに登録。\r
 \r
                        CSound.listインスタンス.Add( this );\r
 \r
-                       // nBytesとn総演奏時間の取得; DTXMania用に追加。\r
-                       //nBytes = (int) ( xa.xaheader.nSamples * xa.xaheader.nChannels * 2 );  // nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
-                       //nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
-                       //if ( nBytes < 0 )\r
-                       //{\r
-                       //    hGC.Free();\r
-                       //    BASSError err = Bass.BASS_ErrorGetCode();\r
-                       //    throw new Exception( "サウンドストリームの生成に失敗しました。(BASS_ChannelGetLength: " + err + ")" );\r
-                       //}\r
-                       //nBytes = (int) this.byArrWAVファイルイメージ.Length;\r
-\r
-                       double seconds = Bass.BASS_ChannelBytes2Seconds( this.hBassStream, nBytes );\r
+                       // n総演奏時間の取得; DTXMania用に追加。\r
+                       double seconds = Bass.BASS_ChannelBytes2Seconds( this._hBassStream, nBytes );\r
                        this.n総演奏時間ms = (int) ( seconds * 1000 );\r
-                       this.pos = 0;\r
+                       //this.pos = 0;\r
                        this.hMixer = hMixer;\r
                        float freq = 0.0f;\r
-                       if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ref freq ) )\r
+                       if ( !Bass.BASS_ChannelGetAttribute( this._hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ref freq ) )\r
                        {\r
                                hGC.Free();\r
                                throw new Exception( string.Format( "サウンドストリームの周波数取得に失敗しました。(BASS_ChannelGetAttribute)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );\r
                        }\r
-                       this.nオリジナルの周波数 = ( int ) freq;\r
+                       this.nオリジナルの周波数 = (int) freq;\r
                }\r
                //-----------------\r
 \r
-               private int pos = 0;\r
-               private int CallbackPlayingXA( int handle, IntPtr buffer, int length, IntPtr user )\r
-               {\r
-                       int bytesread = ( pos + length > Convert.ToInt32(nBytes) ) ? Convert.ToInt32(nBytes) - pos : length;\r
-\r
-                       Marshal.Copy( byArrWAVファイルイメージ, pos, buffer, bytesread );\r
-                       pos += bytesread;\r
-\r
-                       if ( pos >= nBytes )\r
-                       {\r
-                               // set indicator flag\r
-                               bytesread |= (int) BASSStreamProc.BASS_STREAMPROC_END;\r
+               //private int pos = 0;\r
+               //private int CallbackPlayingXA( int handle, IntPtr buffer, int length, IntPtr user )\r
+               //{\r
+               //    int bytesread = ( pos + length > Convert.ToInt32( nBytes ) ) ? Convert.ToInt32( nBytes ) - pos : length;\r
+\r
+               //    Marshal.Copy( byArrWAVファイルイメージ, pos, buffer, bytesread );\r
+               //    pos += bytesread;\r
+               //    if ( pos >= nBytes )\r
+               //    {\r
+               //        // set indicator flag\r
+               //        bytesread |= (int) BASSStreamProc.BASS_STREAMPROC_END;\r
+               //    }\r
+               //    return bytesread;\r
+               //}\r
+               /// <summary>\r
+               /// ストリームの終端まで再生したときに呼び出されるコールバック\r
+               /// </summary>\r
+               /// <param name="handle"></param>\r
+               /// <param name="channel"></param>\r
+               /// <param name="data"></param>\r
+               /// <param name="user"></param>\r
+               private void CallbackEndofStream( int handle, int channel, int data, IntPtr user )      // #32248 2013.10.14 yyagi\r
+               {\r
+// Trace.TraceInformation( "Callback!(remove): " + Path.GetFileName( this.strファイル名 ) );\r
+                       if ( b演奏終了後も再生が続くチップである )                     // 演奏終了後に再生終了するチップ音のミキサー削除は、再生終了のコールバックに引っ掛けて、自前で行う。\r
+                       {                                                                                                       // そうでないものは、ミキサー削除予定時刻に削除する。\r
+                               tBASSサウンドをミキサーから削除する( channel );\r
                        }\r
-                       return bytesread;\r
                }\r
 \r
-\r
 // mixerからの削除\r
 \r
-               ///// <summary>\r
-               ///// ストリームの終端まで再生したときに呼び出されるコールバック\r
-               ///// </summary>\r
-               ///// <param name="handle"></param>\r
-               ///// <param name="channel"></param>\r
-               ///// <param name="data"></param>\r
-               ///// <param name="user"></param>\r
-               //private void CallbackEndofStream( int handle, int channel, int data, IntPtr user )\r
-               //{\r
-               //    //Debug.WriteLine( "Callback!(remove 3sec later)" );\r
-               //    //ThreadPool.QueueUserWorkItem( RemoveMixerChannelLater, channel); \r
-               //    //tBASSサウンドをミキサーから削除する( channel );\r
-               //}\r
-               //private void RemoveMixerChannelLater( object o )\r
-               //{\r
-               //    int channel = (int) o;\r
-               //    Thread.Sleep( 3000 );\r
-               //    tBASSサウンドをミキサーから削除する( channel );\r
-               //}\r
                public bool tBASSサウンドをミキサーから削除する()\r
                {\r
                        return tBASSサウンドをミキサーから削除する( this.hBassStream );\r
@@ -1358,8 +1635,8 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                        bool b = BassMix.BASS_Mixer_ChannelRemove( channel );\r
                        if ( b )\r
                        {\r
-                               CSound管理.nMixing--;\r
-                               Debug.WriteLine( "Removed: " + Path.GetFileName( this.strファイル名 ) + " (" + channel + ")" + " MixedStreams=" + CSound管理.nMixing );\r
+                               Interlocked.Decrement( ref CSound管理.nMixing );\r
+//                             Debug.WriteLine( "Removed: " + Path.GetFileName( this.strファイル名 ) + " (" + channel + ")" + " MixedStreams=" + CSound管理.nMixing );\r
                        }\r
                        return b;\r
                }\r
@@ -1372,19 +1649,25 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                        if ( BassMix.BASS_Mixer_ChannelGetMixer( hBassStream ) == 0 )\r
                        {\r
                                BASSFlag bf = BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_NORAMPIN | BASSFlag.BASS_MIXER_PAUSE;\r
-                               CSound管理.nMixing++;\r
+                               Interlocked.Increment( ref CSound管理.nMixing );\r
+\r
+                               // preloadされることを期待して、敢えてflagからはBASS_MIXER_PAUSEを外してAddChannelした上で、すぐにPAUSEする\r
+                               // -> ChannelUpdateでprebufferできることが分かったため、BASS_MIXER_PAUSEを使用することにした\r
 \r
-                               bool b = BassMix.BASS_Mixer_StreamAddChannel( this.hMixer, this.hBassStream, bf );\r
+                               bool b1 = BassMix.BASS_Mixer_StreamAddChannel( this.hMixer, this.hBassStream, bf );\r
+                               //bool b2 = BassMix.BASS_Mixer_ChannelPause( this.hBassStream );\r
                                t再生位置を先頭に戻す();      // StreamAddChannelの後で再生位置を戻さないとダメ。逆だと再生位置が変わらない。\r
-                               Debug.WriteLine( "Add Mixer: " + Path.GetFileName( this.strファイル名 ) + " (" + hBassStream + ")" + " MixedStreams=" + CSound管理.nMixing );\r
-                               return b;\r
+//Trace.TraceInformation( "Add Mixer: " + Path.GetFileName( this.strファイル名 ) + " (" + hBassStream + ")" + " MixedStreams=" + CSound管理.nMixing );\r
+                               Bass.BASS_ChannelUpdate( this.hBassStream, 0 ); // pre-buffer\r
+                               return b1;      // &b2;\r
                        }\r
                        return true;\r
                }\r
 \r
-\r
+               #region [ tオンメモリ方式でデコードする() ]\r
                public void tオンメモリ方式でデコードする( string strファイル名, out byte[] buffer,\r
-                       out int nPCMデータの先頭インデックス, out int totalPCMSize, out CWin32.WAVEFORMATEX wfx )\r
+                       out int nPCMデータの先頭インデックス, out int totalPCMSize, out CWin32.WAVEFORMATEX wfx,\r
+                       bool bIntegrateWaveHeader )\r
                {\r
                        nPCMデータの先頭インデックス = 0;\r
                        //int nPCMサイズbyte = (int) ( xa.xaheader.nSamples * xa.xaheader.nChannels * 2 );   // nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );\r
@@ -1399,11 +1682,19 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                        {\r
                                sounddecoder = new Cogg();\r
                        }\r
+                       else if ( String.Compare( Path.GetExtension( strファイル名 ), ".mp3", true ) == 0 )\r
+                       {\r
+                               sounddecoder = new Cmp3();\r
+                       }\r
                        else\r
                        {\r
                                throw new NotImplementedException();\r
                        }\r
 \r
+                       if ( !File.Exists( strファイル名 ) )\r
+                       {\r
+                               throw new Exception( string.Format( "ファイルが見つかりませんでした。({0})", strファイル名 ) );\r
+                       }\r
                        int nHandle = sounddecoder.Open( strファイル名 );\r
                        if ( nHandle < 0 )\r
                        {\r
@@ -1423,8 +1714,10 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                                throw new Exception( string.Format( "GetTotalPCMSize() に失敗しました。({0})", strファイル名 ) );\r
                        }\r
                        totalPCMSize += ( ( totalPCMSize % 2 ) != 0 ) ? 1 : 0;\r
-                       buffer = new byte[ totalPCMSize ];\r
-                       GCHandle handle = GCHandle.Alloc( buffer, GCHandleType.Pinned );\r
+                       int wavheadersize = ( bIntegrateWaveHeader ) ? 44 : 0;\r
+                       byte[] buffer_rawdata = new byte[ totalPCMSize ];\r
+                       buffer = new byte[ wavheadersize + totalPCMSize ];\r
+                       GCHandle handle = GCHandle.Alloc( buffer_rawdata, GCHandleType.Pinned );\r
                        try\r
                        {\r
                                if ( sounddecoder.Decode( nHandle, handle.AddrOfPinnedObject(), (uint) totalPCMSize, 0 ) < 0 )\r
@@ -1432,15 +1725,59 @@ Debug.WriteLine( "停止: " + System.IO.Path.GetFileName( this.strファイル
                                        buffer = null;\r
                                        throw new Exception( string.Format( "デコードに失敗しました。({0})", strファイル名 ) );\r
                                }\r
+                               if ( bIntegrateWaveHeader )\r
+                               {\r
+                                       // wave headerを書き込む\r
+\r
+                                       int wfx拡張領域_Length = 0;\r
+                                       var ms = new MemoryStream();\r
+                                       var bw = new BinaryWriter( ms );\r
+                                       bw.Write( new byte[] { 0x52, 0x49, 0x46, 0x46 } );              // 'RIFF'\r
+                                       bw.Write( (UInt32) totalPCMSize + 44 - 8 );                             // ファイルサイズ - 8 [byte];今は不明なので後で上書きする。\r
+                                       bw.Write( new byte[] { 0x57, 0x41, 0x56, 0x45 } );              // 'WAVE'\r
+                                       bw.Write( new byte[] { 0x66, 0x6D, 0x74, 0x20 } );              // 'fmt '\r
+                                       bw.Write( (UInt32) ( 16 + ( ( wfx拡張領域_Length > 0 ) ? ( 2/*sizeof(WAVEFORMATEX.cbSize)*/ + wfx拡張領域_Length ) : 0 ) ) );   // fmtチャンクのサイズ[byte]\r
+                                       bw.Write( (UInt16) wfx.wFormatTag );                                    // フォーマットID(リニアPCMなら1)\r
+                                       bw.Write( (UInt16) wfx.nChannels );                                             // チャンネル数\r
+                                       bw.Write( (UInt32) wfx.nSamplesPerSec );                                // サンプリングレート\r
+                                       bw.Write( (UInt32) wfx.nAvgBytesPerSec );                               // データ速度\r
+                                       bw.Write( (UInt16) wfx.nBlockAlign );                                   // ブロックサイズ\r
+                                       bw.Write( (UInt16) wfx.wBitsPerSample );                                // サンプルあたりのビット数\r
+                                       //if ( wfx拡張領域_Length > 0 )\r
+                                       //{\r
+                                       //    bw.Write( (UInt16) wfx拡張領域.Length );                      // 拡張領域のサイズ[byte]\r
+                                       //    bw.Write( wfx拡張領域 );                                                      // 拡張データ\r
+                                       //}\r
+                                       bw.Write( new byte[] { 0x64, 0x61, 0x74, 0x61 } );              // 'data'\r
+                                       //int nDATAチャンクサイズ位置 = (int) ms.Position;\r
+                                       bw.Write( (UInt32) totalPCMSize );                                              // dataチャンクのサイズ[byte]\r
+\r
+                                       byte[] bs = ms.ToArray();\r
+\r
+                                       bw.Close();\r
+                                       ms.Close();\r
+\r
+                                       for ( int i = 0; i < bs.Length; i++ )\r
+                                       {\r
+                                               buffer[ i ] = bs[ i ];\r
+                                       }\r
+                               }\r
+                               int s = ( bIntegrateWaveHeader ) ? 44 : 0;\r
+                               for ( int i = 0; i < totalPCMSize; i++ )\r
+                               {\r
+                                       buffer[ i + s ] = buffer_rawdata[ i ];\r
+                               }\r
+                               totalPCMSize += wavheadersize;\r
+                               nPCMデータの先頭インデックス = wavheadersize;\r
                        }\r
                        finally\r
                        {\r
                                handle.Free();\r
                                sounddecoder.Close( nHandle );\r
+                               sounddecoder = null;\r
                        }\r
-                       sounddecoder = null;\r
                }\r
-\r
+               #endregion\r
                #endregion\r
        }\r
 }\r