OSDN Git Service

#37271 TimeStretch=OFFの時に限り、ミキシング負荷を従来の半分にした。
[dtxmania/dtxmania.git] / FDK17プロジェクト / コード / 03.サウンド / CSound.cs
index 25c2f88..5ea1acc 100644 (file)
@@ -34,6 +34,9 @@ namespace FDK
                                                                                                        // 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
@@ -45,49 +48,6 @@ namespace FDK
                        {\r
                                return _nMasterVolume;\r
                        }\r
-                       //get\r
-                       //{\r
-                       //    if ( SoundDeviceType == ESoundDeviceType.ExclusiveWASAPI || SoundDeviceType == ESoundDeviceType.ASIO )\r
-                       //    {\r
-                       //        return Bass.BASS_GetConfig(BASSConfig.BASS_CONFIG_GVOL_STREAM ) / 100;\r
-                       //    }\r
-                       //    else\r
-                       //    {\r
-                       //        return 100;\r
-                       //    }\r
-                       //}\r
-                       //set\r
-                       //{\r
-                       //    if ( SoundDeviceType == ESoundDeviceType.ExclusiveWASAPI )\r
-                       //    {\r
-                       //        bool b = BassWasapi.BASS_WASAPI_SetVolume( BASSWASAPIVolume.BASS_WASAPI_CURVE_LINEAR, value / 100.0f );\r
-                       //        if ( !b )\r
-                       //        {\r
-                       //            BASSError be = Bass.BASS_ErrorGetCode();\r
-                       //            Trace.TraceInformation( "WASAPI Master Volume Set Error: " + be.ToString() );\r
-                       //        }\r
-                       //    }\r
-                       //}\r
-                       //set\r
-                       //{\r
-                       //    if ( SoundDeviceType == ESoundDeviceType.ExclusiveWASAPI || SoundDeviceType == ESoundDeviceType.ASIO )\r
-                       //    {\r
-                       //        bool b = Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_GVOL_STREAM, value * 100 );\r
-                       //        if ( !b )\r
-                       //        {\r
-                       //            BASSError be = Bass.BASS_ErrorGetCode();\r
-                       //            Trace.TraceInformation( "Master Volume Set Error: " + be.ToString() );\r
-                       //        }\r
-                       //    }\r
-                       //}\r
-                       //set\r
-                       //{\r
-                       //    if ( SoundDeviceType == ESoundDeviceType.ExclusiveWASAPI || SoundDeviceType == ESoundDeviceType.ASIO )\r
-                       //    {\r
-                       //        var nodes = new BASS_MIXER_NODE[ 1 ] { new BASS_MIXER_NODE( 0, (float) value ) };\r
-                       //        BassMix.BASS_Mixer_ChannelSetEnvelope( SoundDevice.hMixer, BASSMIXEnvelope.BASS_MIXER_ENV_VOL, nodes );\r
-                       //    }\r
-                       //}\r
                        set\r
                        {\r
                                SoundDevice.nMasterVolume = value;\r
@@ -115,7 +75,7 @@ namespace FDK
                #region [ WASAPI/ASIO/DirectSound設定値 ]\r
                /// <summary>\r
                /// <para>WASAPI 排他モード出力における再生遅延[ms](の希望値)。最終的にはこの数値を基にドライバが決定する)。</para>\r
-               /// <para>→ WASAPI初期化時に自動設定するようにしたため、ここで設定した値は使用しないようになった。</para>\r
+               /// <para>0以下の値を指定すると、この数値はWASAPI初期化時に自動設定する。正数を指定すると、その値を設定しようと試みる。</para>\r
                /// </summary>\r
                public static int SoundDelayExclusiveWASAPI = 0;                // SSTでは、50ms\r
                public int GetSoundExclusiveWASAPI()\r
@@ -204,12 +164,12 @@ namespace FDK
                /// <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 )\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 );\r
+                       //bUseOSTimer = false;\r
+                       t初期化( soundDeviceType, nSoundDelayExclusiveWASAPI, nSoundDelayASIO, nASIODevice, _bUseOSTimer );\r
                }\r
                public void Dispose()\r
                {\r
@@ -230,9 +190,13 @@ namespace FDK
                                //bInitialized = true;\r
                        }\r
                }\r
-\r
                public void t初期化( ESoundDeviceType soundDeviceType, int _nSoundDelayExclusiveWASAPI, int _nSoundDelayASIO, int _nASIODevice )\r
                {\r
+                       t初期化( soundDeviceType, _nSoundDelayExclusiveWASAPI, _nSoundDelayASIO, _nASIODevice, false );\r
+               }\r
+\r
+               public void t初期化( ESoundDeviceType soundDeviceType, int _nSoundDelayExclusiveWASAPI, int _nSoundDelayASIO, int _nASIODevice, bool _bUseOSTimer )\r
+               {\r
                        //SoundDevice = null;                                           // 後で再初期化することがあるので、null初期化はコンストラクタに回す\r
                        rc演奏用タイマ = null;                                            // Global.Bass 依存(つまりユーザ依存)\r
                        nMixing = 0;\r
@@ -240,6 +204,7 @@ namespace FDK
                        SoundDelayExclusiveWASAPI = _nSoundDelayExclusiveWASAPI;\r
                        SoundDelayASIO = _nSoundDelayASIO;\r
                        ASIODevice = _nASIODevice;\r
+                       bUseOSTimer = _bUseOSTimer;\r
 \r
                        ESoundDeviceType[] ESoundDeviceTypes = new ESoundDeviceType[ 4 ]\r
                        {\r
@@ -720,8 +685,7 @@ namespace FDK
                        CSound clone = (CSound) MemberwiseClone();      // これだけだとCY連打が途切れる&タイトルに戻る際にNullRef例外発生\r
                        this.DirectSound.DuplicateSoundBuffer( this.Buffer, out clone.Buffer );\r
 \r
-                       // CSound.listインスタンス.Add( this );                   // インスタンスリストに登録。\r
-                       // 本来これを加えるべきだが、Add後Removeできなくなっている。Clone()の仕方の問題であろう。\r
+                       CSound.listインスタンス.Add( clone );                     // インスタンスリストに登録。\r
 \r
                        return clone;\r
                }\r
@@ -1189,19 +1153,37 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
                }\r
                public void t再生位置を変更する( long n位置ms )\r
                {\r
-                       if( this.bBASSサウンドである )\r
+                       if ( this.bBASSサウンドである )\r
                        {\r
-                               bool 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
-                               if ( !b )\r
+                               bool b = true;\r
+                               try\r
                                {\r
-                                       BASSError be = Bass.BASS_ErrorGetCode();\r
-                                       Trace.TraceInformation( Path.GetFileName( this.strファイル名 ) + ": Seek error: " + be.ToString() );\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 * _db周波数倍率 * _db再生速度 );   // #30839 2013.2.24 yyagi; add _db周波数倍率 and _db再生速度\r
-                               this.Buffer.CurrentPlayPosition = n位置sample * this.Buffer.Format.BlockAlignment;\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
@@ -1424,11 +1406,25 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
 \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
@@ -1461,6 +1457,53 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
        \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
+                       #region [ ファイルがWAVかつ、Vorbisコンテナが含まれているかを調べ、それに該当するなら、DirectShowでデコードする。]\r
+                       //-----------------\r
+                       try\r
+                       {\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
+                       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
                        int nPCMデータの先頭インデックス;\r
@@ -1504,7 +1547,8 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
 \r
                        // 個々のストリームの出力をテンポ変更のストリームに入力する。テンポ変更ストリームの出力を、Mixerに出力する。\r
 \r
-//                     if ( CSound管理.bIsTimeStretch )      // TimeStretchのON/OFFに関わりなく、テンポ変更のストリームを生成する。後からON/OFF切り替え可能とするため。\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
@@ -1647,6 +1691,10 @@ Debug.WriteLine("更に再生に失敗: " + Path.GetFileName(this.strファイ
                                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