public static IntPtr WindowHandle;\r
\r
public static bool bIsTimeStretch = false;\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
\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
public void tASIOサウンドを作成する( string strファイル名, int hMixer )\r
{\r
- this.tBASSサウンドを作成する( strファイル名, hMixer, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_LOOP ); // BASSFlag.BASS_SAMPLE_LOOP は、koh-heyさん指摘のmp3低速再生時に再生停止する問題のworkaround。\r
+ this.tBASSサウンドを作成する( strファイル名, hMixer, BASSFlag.BASS_STREAM_DECODE );\r
this.eデバイス種別 = ESoundDeviceType.ASIO; // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)\r
}\r
public void tASIOサウンドを作成する( byte[] byArrWAVファイルイメージ, int hMixer )\r
{\r
- this.tBASSサウンドを作成する( byArrWAVファイルイメージ, hMixer, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_LOOP ); // BASSFlag.BASS_SAMPLE_LOOP は、koh-heyさん指摘のmp3低速再生時に再生停止する問題のworkaround。\r
+ this.tBASSサウンドを作成する( byArrWAVファイルイメージ, hMixer, BASSFlag.BASS_STREAM_DECODE );\r
this.eデバイス種別 = ESoundDeviceType.ASIO; // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)\r
}\r
public void tWASAPIサウンドを作成する( string strファイル名, int hMixer, ESoundDeviceType eデバイス種別 )\r
{\r
- this.tBASSサウンドを作成する( strファイル名, hMixer, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_SAMPLE_LOOP ); // BASSFlag.BASS_SAMPLE_LOOP は、koh-heyさん指摘のmp3低速再生時に再生停止する問題のworkaround。\r
+ this.tBASSサウンドを作成する( strファイル名, hMixer, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT );\r
this.eデバイス種別 = eデバイス種別; // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)\r
}\r
public void tWASAPIサウンドを作成する( byte[] byArrWAVファイルイメージ, int hMixer, ESoundDeviceType eデバイス種別 )\r
{\r
- this.tBASSサウンドを作成する( byArrWAVファイルイメージ, hMixer, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_SAMPLE_LOOP ); // BASSFlag.BASS_SAMPLE_LOOP は、koh-heyさん指摘のmp3低速再生時に再生停止する問題のworkaround。\r
+ this.tBASSサウンドを作成する( byArrWAVファイルイメージ, hMixer, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT );\r
this.eデバイス種別 = eデバイス種別; // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)\r
}\r
public void tDirectSoundサウンドを作成する( string strファイル名, DirectSound DirectSound )\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
{\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 = 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
+ {\r
+ BASSError be = Bass.BASS_ErrorGetCode();\r
+ Trace.TraceInformation( Path.GetFileName( this.strファイル名 ) + ": Seek error: " + be.ToString() );\r
+ }\r
}\r
else if( this.bDirectSoundである )\r
{\r
{\r
if ( String.Compare( Path.GetExtension( strファイル名 ), ".xa", true ) == 0 ) // caselessで文字列比較\r
{\r
- tBASSサウンドを作成するXA( strファイル名, hMixer, flags );\r
+ tBASSサウンドを作成するXAmp3( strファイル名, hMixer, flags );\r
+ return;\r
+ }\r
+ else if ( String.Compare( Path.GetExtension( strファイル名 ), ".mp3", true ) == 0 && CSound管理.bIsMP3DecodeByWindowsCodec )\r
+ {\r
+ // 特定mp3を特定低速度で再生させたときに音が途中で切れる問題(koh-heyさん問題)のworkaround。\r
+ // mp3をオンメモリでwavにデコードして、再生してしまう。\r
+ tBASSサウンドを作成するXAmp3( strファイル名, hMixer, flags );\r
return;\r
}\r
\r
\r
tBASSサウンドを作成する・ストリーム生成後の共通処理( hMixer );\r
}\r
- private void tBASSサウンドを作成するXA( string strファイル名, int hMixer, BASSFlag flags )\r
+ private void tBASSサウンドを作成するXAmp3( string strファイル名, int hMixer, BASSFlag flags )\r
{\r
int nPCMデータの先頭インデックス;\r
CWin32.WAVEFORMATEX wfx;\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.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
+ this._hBassStream = Bass.BASS_StreamCreateFile( this.hGC.AddrOfPinnedObject(), 0L, nBytes, flags );\r
if ( this._hBassStream == 0 )\r
{\r
hGC.Free();\r
CSound.listインスタンス.Add( this );\r
\r
// n総演奏時間の取得; DTXMania用に追加。\r
- double seconds = Bass.BASS_ChannelBytes2Seconds( this._hBassStream, nBytes );\r
+ long length = Bass.BASS_ChannelGetLength( _hBassStream ); //\r
+ double seconds = Bass.BASS_ChannelBytes2Seconds( this._hBassStream, length ); //\r
+ //double seconds = Bass.BASS_ChannelBytes2Seconds( this._hBassStream, nBytes ); // nBytesを使うと、mp3の低速再生時に途中で切れる場合がある。\r
this.n総演奏時間ms = (int) ( seconds * 1000 );\r
this.pos = 0;\r
this.hMixer = hMixer;\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 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