namespace FDK\r
{\r
#region [ DTXMania用拡張 ]\r
- public sealed class CSound管理 // : CSound\r
+ public class CSound管理 // : CSound\r
{\r
- private static int refCounter = 0;\r
- private bool bInitialized = false;\r
-\r
- private static CSound管理 m_Instance = new CSound管理();\r
- /// <summary>\r
- /// コンストラクタ(singletone化するため、外部には見せない)\r
- /// </summary>\r
- private CSound管理()\r
- {\r
- SoundDevice = null;\r
- }\r
-\r
- public static CSound管理 Instance\r
- {\r
- get\r
- {\r
- refCounter++;\r
- return m_Instance;\r
- }\r
- }\r
-\r
-\r
-\r
public static ISoundDevice SoundDevice\r
{\r
get; set;\r
get; set;\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
#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
#endregion\r
\r
\r
- /// <summary>\r
- /// コンストラクタ\r
- /// </summary>\r
- /// <param name="handle"></param>\r
- //public CSound管理( IntPtr handle ) // #30803 従来のコンストラクタ相当のI/Fを追加。(DTXC用)\r
- //{\r
- // WindowHandle = handle;\r
- // SoundDevice = null;\r
- // t初期化( ESoundDeviceType.DirectSound, 0, 0, 0 );\r
- //}\r
- //public CSound管理( IntPtr handle, ESoundDeviceType soundDeviceType, int nSoundDelayExclusiveWASAPI, int nSoundDelayASIO, int nASIODevice )\r
- //{\r
- // WindowHandle = handle;\r
- // SoundDevice = null;\r
- // t初期化( soundDeviceType, nSoundDelayExclusiveWASAPI, nSoundDelayASIO, nASIODevice );\r
- //}\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
+ 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
- if ( --refCounter <= 0 )\r
- {\r
- t終了();\r
- refCounter = 0;\r
- bInitialized = false;\r
- }\r
+ t終了();\r
}\r
\r
//public static void t初期化()\r
\r
public void t初期化( ESoundDeviceType soundDeviceType, int _nSoundDelayExclusiveWASAPI, int _nSoundDelayASIO, int _nASIODevice, IntPtr handle )\r
{\r
- if ( !bInitialized )\r
+ //if ( !bInitialized )\r
{\r
WindowHandle = handle;\r
t初期化( soundDeviceType, _nSoundDelayExclusiveWASAPI, _nSoundDelayASIO, _nASIODevice );\r
- bInitialized = true;\r
+ //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
SoundDelayExclusiveWASAPI = _nSoundDelayExclusiveWASAPI;\r
SoundDelayASIO = _nSoundDelayASIO;\r
ASIODevice = _nASIODevice;\r
+ bUseOSTimer = _bUseOSTimer;\r
\r
ESoundDeviceType[] ESoundDeviceTypes = new ESoundDeviceType[ 4 ]\r
{\r
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
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
//-----------------\r
#endregion\r
\r
+ SoundDevice.nMasterVolume = _nMasterVolume; // サウンドデバイスに対して、マスターボリュームを再設定する\r
+\r
CSound.tすべてのサウンドを再構築する( SoundDevice ); // すでに生成済みのサウンドがあれば作り直す。\r
}\r
public CSound tサウンドを生成する( string filename )\r
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
//★★★★★★★★★★★★★★★★★★★★★ダミー★★★★★★★★★★★★★★★★★★\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
public bool b演奏終了後も再生が続くチップである = false; // これがtrueなら、本サウンドの再生終了のコールバック時に自動でミキサーから削除する\r
\r
- private STREAMPROC _cbStreamXA; // make it global, so that the GC can not remove it\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
}\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
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
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
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
// DTXCからDTXManiaを呼び出すと、DTXC終了時にこの現象が発生する。\r
}\r
}\r
+ this.n一時停止回数 = 0;\r
}\r
\r
public void t再生位置を先頭に戻す()\r
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
}\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 * this.db周波数倍率 * this.db再生速度 / 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 * _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
\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
\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
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.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
+ //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
// 個々のストリームの出力をテンポ変更のストリームに入力する。テンポ変更ストリームの出力を、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
// 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
}\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
+ //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
- if ( pos >= nBytes )\r
- {\r
- // set indicator flag\r
- bytesread |= (int) BASSStreamProc.BASS_STREAMPROC_END;\r
- }\r
- return bytesread;\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="user"></param>\r
private void CallbackEndofStream( int handle, int channel, int data, IntPtr user ) // #32248 2013.10.14 yyagi\r
{\r
- // Debug.WriteLine( "Callback!(remove): " + Path.GetFileName( this.strファイル名 ) );\r
+// Trace.TraceInformation( "Callback!(remove): " + Path.GetFileName( this.strファイル名 ) );\r
if ( b演奏終了後も再生が続くチップである ) // 演奏終了後に再生終了するチップ音のミキサー削除は、再生終了のコールバックに引っ掛けて、自前で行う。\r
{ // そうでないものは、ミキサー削除予定時刻に削除する。\r
tBASSサウンドをミキサーから削除する( channel );\r
{\r
if ( BassMix.BASS_Mixer_ChannelGetMixer( hBassStream ) == 0 )\r
{\r
- BASSFlag bf = BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_NORAMPIN; // | BASSFlag.BASS_MIXER_PAUSE;\r
+ BASSFlag bf = BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_NORAMPIN | BASSFlag.BASS_MIXER_PAUSE;\r
Interlocked.Increment( ref CSound管理.nMixing );\r
\r
// preloadされることを期待して、敢えてflagからはBASS_MIXER_PAUSEを外してAddChannelした上で、すぐにPAUSEする\r
+ // -> ChannelUpdateでprebufferできることが分かったため、BASS_MIXER_PAUSEを使用することにした\r
+\r
bool b1 = BassMix.BASS_Mixer_StreamAddChannel( this.hMixer, this.hBassStream, bf );\r
- bool b2 = BassMix.BASS_Mixer_ChannelPause( this.hBassStream );\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 b1 & b2;\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
#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
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
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
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