2 using System.Collections.Generic;
\r
4 using System.Diagnostics;
\r
5 using System.Runtime.InteropServices;
\r
7 using System.Runtime.CompilerServices;
\r
8 using System.Threading;
\r
10 using SlimDX.DirectSound;
\r
11 using SlimDX.Multimedia;
\r
13 using Un4seen.BassAsio;
\r
14 using Un4seen.BassWasapi;
\r
15 using Un4seen.Bass.AddOn.Mix;
\r
16 using DirectShowLib;
\r
20 #region [ DTXMania用拡張 ]
\r
21 public class CSound管理 // : CSound
\r
23 //private static ISoundDevice _SoundDevice;
\r
24 //private static ESoundDeviceType _SoundDeviceType = ESoundDeviceType.Unknown;
\r
25 public static ISoundDevice SoundDevice
\r
30 // return _SoundDevice;
\r
34 // _SoundDevice = value;
\r
37 public static ESoundDeviceType SoundDeviceType
\r
42 // return _SoundDeviceType;
\r
46 // _SoundDeviceType = value;
\r
49 public static CSoundTimer rc演奏用タイマ = null;
\r
51 public static IntPtr WindowHandle;
\r
53 public static int nMixing = 0;
\r
54 public int GetMixingStreams()
\r
58 public static int nStreams = 0;
\r
59 public int GetStreams()
\r
63 #region [ WASAPI/ASIO/DirectSound設定値 ]
\r
65 /// <para>WASAPI 排他モード出力における再生遅延[ms](の希望値)。最終的にはこの数値を基にドライバが決定する)。</para>
\r
67 public static int SoundDelayExclusiveWASAPI = 0; // SSTでは、50ms
\r
69 /// <para>WASAPI 共有モード出力における再生遅延[ms]。ユーザが決定する。</para>
\r
71 public static int SoundDelaySharedWASAPI = 100;
\r
73 /// <para>排他WASAPIバッファの更新間隔。出力間隔ではないので注意。</para>
\r
74 /// <para>SoundDelay よりも小さい値であること。(小さすぎる場合はBASSによって自動修正される。)</para>
\r
76 public static int SoundUpdatePeriodExclusiveWASAPI = 6;
\r
78 /// <para>共有WASAPIバッファの更新間隔。出力間隔ではないので注意。</para>
\r
79 /// <para>SoundDelay よりも小さい値であること。(小さすぎる場合はBASSによって自動修正される。)</para>
\r
81 public static int SoundUpdatePeriodSharedWASAPI = 6;
\r
83 ///// <para>ASIO 出力における再生遅延[ms](の希望値)。最終的にはこの数値を基にドライバが決定する)。</para>
\r
85 //public static int SoundDelayASIO = 0; // SSTでは50ms。0にすると、デバイスの設定値をそのまま使う。
\r
87 /// <para>ASIO 出力におけるバッファサイズ。</para>
\r
89 public static int SoundBufferSizeASIO = 0; // 0にすると、デバイスの設定値をそのまま使う。
\r
90 public int GetSoundBufferSizeASIO()
\r
92 return SoundBufferSizeASIO;
\r
94 public void SetSoundBufferSizeASIO(int value)
\r
96 SoundBufferSizeASIO = value;
\r
99 /// <para>DirectSound 出力における再生遅延[ms]。ユーザが決定する。</para>
\r
101 public static int SoundDelayDirectSound = 100;
\r
110 /// <param name="handle"></param>
\r
111 public CSound管理( IntPtr handle, ESoundDeviceType soundDeviceType )
\r
113 WindowHandle = handle;
\r
114 //cMixerManager = new CBassMixerManager();
\r
115 //thMixerManager = new Thread( new ThreadStart( cMixerManager.Start ) );
\r
117 t初期化( soundDeviceType );
\r
119 public void Dispose()
\r
124 public static void t初期化()
\r
126 t初期化( ESoundDeviceType.DirectSound );
\r
129 public static void t初期化( ESoundDeviceType soundDeviceType )
\r
131 SoundDevice = null; // ユーザ依存
\r
132 rc演奏用タイマ = null; // Global.Bass 依存(つまりユーザ依存)
\r
135 ESoundDeviceType[] ESoundDeviceTypes = new ESoundDeviceType[ 4 ]
\r
137 ESoundDeviceType.ExclusiveWASAPI,
\r
138 ESoundDeviceType.ASIO,
\r
139 ESoundDeviceType.DirectSound,
\r
140 ESoundDeviceType.Unknown
\r
144 switch ( soundDeviceType )
\r
146 case ESoundDeviceType.ExclusiveWASAPI:
\r
149 case ESoundDeviceType.ASIO:
\r
152 case ESoundDeviceType.DirectSound:
\r
159 for ( SoundDeviceType = ESoundDeviceTypes[ n初期デバイス ]; ; SoundDeviceType = ESoundDeviceTypes[ ++n初期デバイス ] )
\r
163 t現在のユーザConfigに従ってサウンドデバイスとすべての既存サウンドを再構築する();
\r
166 catch ( Exception e )
\r
168 Trace.TraceInformation( e.Message );
\r
169 if ( ESoundDeviceTypes[ n初期デバイス ] == ESoundDeviceType.Unknown )
\r
171 throw new Exception( string.Format( "サウンドデバイスの初期化に失敗しました。" ) );
\r
176 public static void t終了()
\r
178 C共通.tDisposeする( SoundDevice ); SoundDevice = null;
\r
179 C共通.tDisposeする( ref rc演奏用タイマ ); // Global.Bass を解放した後に解放すること。(Global.Bass で参照されているため)
\r
183 public static void t現在のユーザConfigに従ってサウンドデバイスとすべての既存サウンドを再構築する()
\r
185 #region [ すでにサウンドデバイスと演奏タイマが構築されていれば解放する。]
\r
186 //-----------------
\r
187 if ( SoundDevice != null )
\r
189 // すでに生成済みのサウンドがあれば初期状態に戻す。
\r
191 CSound.tすべてのサウンドを初期状態に戻す();
\r
194 // サウンドデバイスと演奏タイマを解放する。
\r
196 C共通.tDisposeする( SoundDevice ); SoundDevice = null;
\r
197 C共通.tDisposeする( ref rc演奏用タイマ ); // Global.SoundDevice を解放した後に解放すること。(Global.SoundDevice で参照されているため)
\r
199 //-----------------
\r
202 #region [ 新しいサウンドデバイスを構築する。]
\r
203 //-----------------
\r
204 switch ( SoundDeviceType )
\r
206 case ESoundDeviceType.ExclusiveWASAPI:
\r
207 SoundDevice = new CSoundDeviceWASAPI( CSoundDeviceWASAPI.Eデバイスモード.排他, SoundDelayExclusiveWASAPI, SoundUpdatePeriodExclusiveWASAPI );
\r
210 case ESoundDeviceType.SharedWASAPI:
\r
211 SoundDevice = new CSoundDeviceWASAPI( CSoundDeviceWASAPI.Eデバイスモード.共有, SoundDelaySharedWASAPI, SoundUpdatePeriodSharedWASAPI );
\r
214 case ESoundDeviceType.ASIO:
\r
215 SoundDevice = new CSoundDeviceASIO( SoundBufferSizeASIO );
\r
218 case ESoundDeviceType.DirectSound:
\r
219 SoundDevice = new CSoundDeviceDirectSound( WindowHandle, SoundDelayDirectSound );
\r
223 throw new Exception( string.Format( "未対応の SoundDeviceType です。[{0}]", SoundDeviceType.ToString() ) );
\r
225 //-----------------
\r
227 #region [ 新しい演奏タイマを構築する。]
\r
228 //-----------------
\r
229 rc演奏用タイマ = new CSoundTimer( SoundDevice );
\r
230 //-----------------
\r
233 CSound.tすべてのサウンドを再構築する( SoundDevice ); // すでに生成済みのサウンドがあれば作り直す。
\r
235 public CSound tサウンドを生成する( string filename )
\r
237 //Debug.WriteLine( "★★tサウンドを生成する()" + SoundDevice.e出力デバイス + " " + Path.GetFileName( filename ) );
\r
238 if ( SoundDeviceType == ESoundDeviceType.Unknown )
\r
240 throw new Exception( string.Format( "未対応の SoundDeviceType です。[{0}]", SoundDeviceType.ToString() ) );
\r
242 return SoundDevice.tサウンドを作成する( filename );
\r
245 public void t再生中の処理をする()
\r
247 //★★★★★★★★★★★★★★★★★★★★★ダミー★★★★★★★★★★★★★★★★★★
\r
248 // Debug.Write( "再生中の処理をする()" );
\r
251 public void tサウンドを破棄する( CSound csound )
\r
257 public float GetCPUusage()
\r
259 //float f = Bass.BASS_GetCPU();
\r
261 switch ( SoundDeviceType )
\r
263 case ESoundDeviceType.ExclusiveWASAPI:
\r
264 case ESoundDeviceType.SharedWASAPI:
\r
265 f = BassWasapi.BASS_WASAPI_GetCPU();
\r
267 case ESoundDeviceType.ASIO:
\r
268 f = BassAsio.BASS_ASIO_GetCPU();
\r
270 case ESoundDeviceType.DirectSound:
\r
278 //Debug.WriteLine( "cpu=" + f );
\r
282 public string GetCurrentSoundDeviceType()
\r
284 switch ( SoundDeviceType )
\r
286 case ESoundDeviceType.ExclusiveWASAPI:
\r
287 case ESoundDeviceType.SharedWASAPI:
\r
289 case ESoundDeviceType.ASIO:
\r
291 case ESoundDeviceType.DirectSound:
\r
292 return "DirectSound";
\r
298 //private CBassMixerManager cMixerManager = null;
\r
299 //private Thread thMixerManager = null;
\r
301 public void AddMixer( CSound cs )
\r
303 cs.tBASSサウンドをミキサーに追加する();
\r
308 // CSound は、サウンドデバイスが変更されたときも、インスタンスを再作成することなく、新しいデバイスで作り直せる必要がある。
\r
309 // そのため、デバイスごとに別のクラスに分割するのではなく、1つのクラスに集約するものとする。
\r
311 public class CSound : IDisposable
\r
313 #region [ DTXMania用拡張 ]
\r
314 public int n総演奏時間ms
\r
319 public int nサウンドバッファサイズ // 取りあえず0固定★★★★★★★★★★★★★★★★★★★★
\r
323 public bool bストリーム再生する // 取りあえずfalse固定★★★★★★★★★★★★★★★★★★★★
\r
324 // trueにすると同一チップ音の多重再生で問題が出る(4POLY音源として動かない)
\r
326 get { return false; }
\r
328 public double db周波数倍率
\r
336 if ( _db周波数倍率 != value )
\r
339 if ( bBASSサウンドである )
\r
341 Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ( float ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 ) );
\r
347 this.Buffer.Frequency = ( int ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 );
\r
353 public double db再生速度
\r
361 if ( _db再生速度 != value )
\r
364 if ( bBASSサウンドである )
\r
366 Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ( float ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 ) );
\r
372 this.Buffer.Frequency = ( int ) ( _db周波数倍率 * _db再生速度 * nオリジナルの周波数 );
\r
381 private STREAMPROC _cbStreamXA; // make it global, so that the GC can not remove it
\r
382 private SYNCPROC _cbEndofStream; // ストリームの終端まで再生されたときに呼び出されるコールバック
\r
383 private WaitCallback _cbRemoveMixerChannel;
\r
386 /// <para>0:最小~100:原音</para>
\r
392 if( this.bBASSサウンドである )
\r
395 if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_VOL, ref f音量 ) )
\r
396 //if ( BassMix.BASS_Mixer_ChannelGetEnvelopePos( this.hBassStream, BASSMIXEnvelope.BASS_MIXER_ENV_VOL, ref f音量 ) == -1 )
\r
398 return (int) ( f音量 * 100 );
\r
400 else if( this.bDirectSoundである )
\r
408 if( this.bBASSサウンドである )
\r
410 float f音量 = Math.Min( Math.Max( value, 0 ), 100 ) / 100.0f; // 0~100 → 0.0~1.0
\r
411 //var nodes = new BASS_MIXER_NODE[ 1 ] { new BASS_MIXER_NODE( 0, f音量 ) };
\r
412 //BassMix.BASS_Mixer_ChannelSetEnvelope( this.hBassStream, BASSMIXEnvelope.BASS_MIXER_ENV_VOL, nodes );
\r
413 Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_VOL, f音量 );
\r
416 else if( this.bDirectSoundである )
\r
420 if( this._n音量 == 0 )
\r
422 this._n音量db = -10000;
\r
426 this._n音量db = (int) ( ( 20.0 * Math.Log10( ( (double) this._n音量 ) / 100.0 ) ) * 100.0 );
\r
429 this.Buffer.Volume = this._n音量db;
\r
435 /// <para>左:-100~中央:0~100:右。set のみ。</para>
\r
441 if( this.bBASSサウンドである )
\r
444 if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_PAN, ref f位置 ) )
\r
445 //if( BassMix.BASS_Mixer_ChannelGetEnvelopePos( this.hBassStream, BASSMIXEnvelope.BASS_MIXER_ENV_PAN, ref f位置 ) == -1 )
\r
447 return (int) ( f位置 * 100 );
\r
449 else if( this.bDirectSoundである )
\r
457 if( this.bBASSサウンドである )
\r
459 float f位置 = Math.Min( Math.Max( value, -100 ), 100 ) / 100.0f; // -100~100 → -1.0~1.0
\r
460 //var nodes = new BASS_MIXER_NODE[ 1 ] { new BASS_MIXER_NODE( 0, f位置 ) };
\r
461 //BassMix.BASS_Mixer_ChannelSetEnvelope( this.hBassStream, BASSMIXEnvelope.BASS_MIXER_ENV_PAN, nodes );
\r
462 Bass.BASS_ChannelSetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_PAN, f位置 );
\r
464 else if( this.bDirectSoundである )
\r
466 this._n位置 = Math.Min( Math.Max( -100, value ), 100 ); // -100~100
\r
468 if( this._n位置 == 0 )
\r
472 else if( this._n位置 == -100 )
\r
474 this._n位置db = -10000;
\r
476 else if( this._n位置 == 100 )
\r
478 this._n位置db = 100000;
\r
480 else if( this._n位置 < 0 )
\r
482 this._n位置db = (int) ( ( 20.0 * Math.Log10( ( (double) ( this._n位置 + 100 ) ) / 100.0 ) ) * 100.0 );
\r
486 this._n位置db = (int) ( ( -20.0 * Math.Log10( ( (double) ( 100 - this._n位置 ) ) / 100.0 ) ) * 100.0 );
\r
489 this.Buffer.Pan = this._n位置db;
\r
495 /// <para>DirectSoundのセカンダリバッファ。</para>
\r
497 public SecondarySoundBuffer DirectSoundBuffer
\r
499 get { return this.Buffer; }
\r
503 /// <para>DirectSoundのセカンダリバッファ作成時のフラグ。</para>
\r
505 public BufferFlags DirectSoundBufferFlags
\r
512 /// <para>全インスタンスリスト。</para>
\r
513 /// <para>~を作成する() で追加され、t解放する() or Dispose() で解放される。</para>
\r
515 public static List<CSound> listインスタンス = new List<CSound>();
\r
521 this._db周波数倍率 = 1.0;
\r
522 this._db再生速度 = 1.0;
\r
523 this.DirectSoundBufferFlags = CSoundDeviceDirectSound.DefaultFlags;
\r
524 this._cbRemoveMixerChannel = new WaitCallback( RemoveMixerChannelLater );
\r
527 public void tASIOサウンドを作成する( string strファイル名, int hMixer )
\r
529 this.tBASSサウンドを作成する( strファイル名, hMixer, BASSFlag.BASS_STREAM_DECODE );
\r
530 this.eデバイス種別 = ESoundDeviceType.ASIO; // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)
\r
532 public void tASIOサウンドを作成する( byte[] byArrWAVファイルイメージ, int hMixer )
\r
534 this.tBASSサウンドを作成する( byArrWAVファイルイメージ, hMixer, BASSFlag.BASS_STREAM_DECODE );
\r
535 this.eデバイス種別 = ESoundDeviceType.ASIO; // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)
\r
537 public void tWASAPIサウンドを作成する( string strファイル名, int hMixer, ESoundDeviceType eデバイス種別 )
\r
539 this.tBASSサウンドを作成する( strファイル名, hMixer, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT );
\r
540 this.eデバイス種別 = eデバイス種別; // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)
\r
542 public void tWASAPIサウンドを作成する( byte[] byArrWAVファイルイメージ, int hMixer, ESoundDeviceType eデバイス種別 )
\r
544 this.tBASSサウンドを作成する( byArrWAVファイルイメージ, hMixer, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT );
\r
545 this.eデバイス種別 = eデバイス種別; // 作成後に設定する。(作成に失敗してると例外発出されてここは実行されない)
\r
547 public void tDirectSoundサウンドを作成する( string strファイル名, DirectSound DirectSound )
\r
549 this.e作成方法 = E作成方法.ファイルから;
\r
550 this.strファイル名 = strファイル名;
\r
552 if ( String.Compare( Path.GetExtension( strファイル名 ), ".xa", true ) == 0 ||
\r
553 String.Compare( Path.GetExtension( strファイル名 ), ".ogg", true ) == 0 ) // caselessで文字列比較
\r
555 tDirectSoundサウンドを作成するXAOGG( strファイル名, DirectSound );
\r
559 // すべてのファイルを DirectShow でデコードすると時間がかかるので、ファイルが WAV かつ PCM フォーマットでない場合のみ DirectShow でデコードする。
\r
561 byte[] byArrWAVファイルイメージ = null;
\r
562 bool bファイルがWAVかつPCMフォーマットである = true;
\r
565 #region [ ファイルがWAVかつPCMフォーマットか否か調べる。]
\r
566 //-----------------
\r
569 using ( var ws = new WaveStream( strファイル名 ) )
\r
571 if ( ws.Format.FormatTag != WaveFormatTag.Pcm )
\r
572 bファイルがWAVかつPCMフォーマットである = false;
\r
577 bファイルがWAVかつPCMフォーマットである = false;
\r
579 //-----------------
\r
582 if ( bファイルがWAVかつPCMフォーマットである )
\r
584 #region [ ファイルを読み込んで byArrWAVファイルイメージへ格納。]
\r
585 //-----------------
\r
586 var fs = File.Open( strファイル名, FileMode.Open, FileAccess.Read );
\r
587 var br = new BinaryReader( fs );
\r
589 byArrWAVファイルイメージ = new byte[ fs.Length ];
\r
590 br.Read( byArrWAVファイルイメージ, 0, (int) fs.Length );
\r
594 //-----------------
\r
599 #region [ DirectShow でデコード変換し、 byArrWAVファイルイメージへ格納。]
\r
600 //-----------------
\r
601 CDStoWAVFileImage.t変換( strファイル名, out byArrWAVファイルイメージ );
\r
602 //-----------------
\r
609 this.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, DirectSound );
\r
611 public void tDirectSoundサウンドを作成するXAOGG( string strファイル名, DirectSound DirectSound )
\r
613 this.e作成方法 = E作成方法.ファイルから;
\r
614 this.strファイル名 = strファイル名;
\r
619 // Cxa xa = new Cxa();
\r
620 // xa.Decode( strファイル名, out this.byArrWAVファイルイメージ );
\r
622 WaveFormat wfx = new WaveFormat();
\r
623 int nPCMデータの先頭インデックス = 0;
\r
624 // int nPCMサイズbyte = (int) ( xa.xaheader.nSamples * xa.xaheader.nChannels * 2 ); // nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );
\r
627 CWin32.WAVEFORMATEX cw32wfx;
\r
628 tオンメモリ方式でデコードする( strファイル名, out this.byArrWAVファイルイメージ,
\r
629 out nPCMデータの先頭インデックス, out nPCMサイズbyte, out cw32wfx );
\r
631 wfx.AverageBytesPerSecond = (int) cw32wfx.nAvgBytesPerSec;
\r
632 wfx.BitsPerSample = (short) cw32wfx.wBitsPerSample;
\r
633 wfx.BlockAlignment = (short) cw32wfx.nBlockAlign;
\r
634 wfx.Channels = (short) cw32wfx.nChannels;
\r
635 wfx.FormatTag = WaveFormatTag.Pcm; // xa.waveformatex.wFormatTag;
\r
636 wfx.SamplesPerSecond = (int) cw32wfx.nSamplesPerSec;
\r
638 // セカンダリバッファを作成し、PCMデータを書き込む。
\r
640 this.Buffer = new SecondarySoundBuffer( DirectSound, new SoundBufferDescription()
\r
643 Flags = CSoundDeviceDirectSound.DefaultFlags,
\r
644 SizeInBytes = nPCMサイズbyte,
\r
646 this.Buffer.Write( byArrWAVファイルイメージ, nPCMデータの先頭インデックス, nPCMサイズbyte, 0, LockFlags.None );
\r
650 this.eデバイス種別 = ESoundDeviceType.DirectSound;
\r
651 this.DirectSoundBufferFlags = CSoundDeviceDirectSound.DefaultFlags;
\r
654 this.nオリジナルの周波数 = wfx.SamplesPerSecond;
\r
656 n総演奏時間ms = ( int ) ( ( ( double ) nPCMサイズbyte ) / ( this.Buffer.Format.AverageBytesPerSecond * 0.001 ) );
\r
657 nBytes = nPCMサイズbyte;
\r
661 CSound.listインスタンス.Add( this );
\r
665 public void tDirectSoundサウンドを作成する( byte[] byArrWAVファイルイメージ, DirectSound DirectSound )
\r
667 this.tDirectSoundサウンドを作成する( byArrWAVファイルイメージ, DirectSound, CSoundDeviceDirectSound.DefaultFlags );
\r
669 public void tDirectSoundサウンドを作成する( byte[] byArrWAVファイルイメージ, DirectSound DirectSound, BufferFlags flags )
\r
671 if( this.e作成方法 == E作成方法.Unknown )
\r
672 this.e作成方法 = E作成方法.WAVファイルイメージから;
\r
674 WaveFormat wfx = null;
\r
675 int nPCMデータの先頭インデックス = -1;
\r
676 int nPCMサイズbyte = -1;
\r
678 #region [ byArrWAVファイルイメージ[] から上記3つのデータを取得。]
\r
679 //-----------------
\r
680 var ms = new MemoryStream( byArrWAVファイルイメージ );
\r
681 var br = new BinaryReader( ms );
\r
685 // 'RIFF'+RIFFデータサイズ
\r
687 if( br.ReadUInt32() != 0x46464952 )
\r
688 throw new InvalidDataException( "RIFFファイルではありません。" );
\r
692 if( br.ReadUInt32() != 0x45564157 )
\r
693 throw new InvalidDataException( "WAVEファイルではありません。" );
\r
696 while( ( ms.Position + 8 ) < ms.Length ) // +8 は、チャンク名+チャンクサイズ。残り8バイト未満ならループ終了。
\r
698 uint chunkName = br.ReadUInt32();
\r
701 if( chunkName == 0x20746D66 )
\r
703 long chunkSize = (long) br.ReadUInt32();
\r
705 var tag = (WaveFormatTag) br.ReadUInt16();
\r
707 if( tag == WaveFormatTag.Pcm ) wfx = new WaveFormat();
\r
708 else if( tag == WaveFormatTag.Extensible ) wfx = new SlimDX.Multimedia.WaveFormatExtensible(); // このクラスは WaveFormat を継承している。
\r
710 throw new InvalidDataException( string.Format( "未対応のWAVEフォーマットタグです。(Tag:{0})", tag.ToString() ) );
\r
712 wfx.FormatTag = tag;
\r
713 wfx.Channels = br.ReadInt16();
\r
714 wfx.SamplesPerSecond = br.ReadInt32();
\r
715 wfx.AverageBytesPerSecond = br.ReadInt32();
\r
716 wfx.BlockAlignment = br.ReadInt16();
\r
717 wfx.BitsPerSample = br.ReadInt16();
\r
719 long nフォーマットサイズbyte = 16;
\r
721 if( wfx.FormatTag == WaveFormatTag.Extensible )
\r
723 br.ReadUInt16(); // 拡張領域サイズbyte
\r
724 var wfxEx = (SlimDX.Multimedia.WaveFormatExtensible) wfx;
\r
725 wfxEx.ValidBitsPerSample = br.ReadInt16();
\r
726 wfxEx.ChannelMask = (Speakers) br.ReadInt32();
\r
727 wfxEx.SubFormat = new Guid( br.ReadBytes( 16 ) ); // GUID は 16byte (128bit)
\r
729 nフォーマットサイズbyte += 24;
\r
732 ms.Seek( chunkSize - nフォーマットサイズbyte, SeekOrigin.Current );
\r
737 else if( chunkName == 0x61746164 )
\r
739 nPCMサイズbyte = br.ReadInt32();
\r
740 nPCMデータの先頭インデックス = (int) ms.Position;
\r
742 ms.Seek( nPCMサイズbyte, SeekOrigin.Current );
\r
749 long chunkSize = (long) br.ReadUInt32();
\r
750 ms.Seek( chunkSize, SeekOrigin.Current );
\r
756 throw new InvalidDataException( "fmt チャンクが存在しません。不正なサウンドデータです。" );
\r
757 if( nPCMサイズbyte < 0 )
\r
758 throw new InvalidDataException( "data チャンクが存在しません。不正なサウンドデータです。" );
\r
765 //-----------------
\r
769 // セカンダリバッファを作成し、PCMデータを書き込む。
\r
771 this.Buffer = new SecondarySoundBuffer( DirectSound, new SoundBufferDescription() {
\r
772 Format = ( wfx.FormatTag == WaveFormatTag.Pcm) ? wfx : (SlimDX.Multimedia.WaveFormatExtensible) wfx,
\r
774 SizeInBytes = nPCMサイズbyte,
\r
776 this.Buffer.Write( byArrWAVファイルイメージ, nPCMデータの先頭インデックス, nPCMサイズbyte, 0, LockFlags.None );
\r
780 this.eデバイス種別 = ESoundDeviceType.DirectSound;
\r
781 this.DirectSoundBufferFlags = flags;
\r
782 this.byArrWAVファイルイメージ = byArrWAVファイルイメージ;
\r
785 this.nオリジナルの周波数 = wfx.SamplesPerSecond;
\r
786 n総演奏時間ms = ( int ) ( ( ( double ) nPCMサイズbyte ) / ( this.Buffer.Format.AverageBytesPerSecond * 0.001 ) );
\r
791 CSound.listインスタンス.Add( this );
\r
794 #region [ DTXMania用の変換 ]
\r
796 public void tサウンドを破棄する( CSound cs )
\r
800 public void t再生を開始する()
\r
805 public void t再生を開始する( bool bループする )
\r
807 if ( bBASSサウンドである )
\r
811 Bass.BASS_ChannelFlags( this.hBassStream, BASSFlag.BASS_SAMPLE_LOOP, BASSFlag.BASS_SAMPLE_LOOP );
\r
815 Bass.BASS_ChannelFlags( this.hBassStream, BASSFlag.BASS_DEFAULT, BASSFlag.BASS_DEFAULT );
\r
821 public void t再生を停止する()
\r
826 public void t再生を一時停止する()
\r
831 public void t再生を再開する( long t ) // ★★★★★★★★★★★★★★★★★★★★★★★★★★★★
\r
833 Debug.WriteLine( "t再生を再開する(long " + t + ")" );
\r
842 if ( this.bBASSサウンドである )
\r
844 bool ret = ( BassMix.BASS_Mixer_ChannelIsActive( this.hBassStream ) == BASSActive.BASS_ACTIVE_PAUSED ) &
\r
845 ( BassMix.BASS_Mixer_ChannelGetPosition( this.hBassStream ) > 0 );
\r
850 return ( this.n一時停止回数 > 0 );
\r
858 if ( this.eデバイス種別 == ESoundDeviceType.DirectSound )
\r
860 return ( ( this.Buffer.Status & BufferStatus.Playing ) != BufferStatus.None );
\r
864 // 基本的にはBASS_ACTIVE_PLAYINGなら再生中だが、最後まで再生しきったchannelも
\r
865 // BASS_ACTIVE_PLAYINGのままになっているので、小細工が必要。
\r
866 bool ret = ( BassMix.BASS_Mixer_ChannelIsActive( this.hBassStream ) == BASSActive.BASS_ACTIVE_PLAYING );
\r
867 if ( BassMix.BASS_Mixer_ChannelGetPosition( this.hBassStream ) >= nBytes )
\r
875 //public lint t時刻から位置を返す( long t )
\r
877 // double num = ( n時刻 * this.db再生速度 ) * this.db周波数倍率;
\r
878 // return (int) ( ( num * 0.01 ) * this.nSamplesPerSecond );
\r
883 public void t解放する()
\r
885 if ( this.bBASSサウンドである ) // stream数の削減用
\r
887 tBASSサウンドをミキサーから削除する();
\r
888 _cbEndofStream = null;
\r
889 _cbStreamXA = null;
\r
890 CSound管理.nStreams--;
\r
892 bool bManagedも解放する = true;
\r
893 bool bインスタンス削除 = false; // インスタンスは存続する。
\r
894 this.Dispose( bManagedも解放する, bインスタンス削除 );
\r
896 public void tサウンドを再生する()
\r
898 //Debug.WriteLine( "tサウンドを再生する(): " + this.strファイル名 );
\r
899 if( this.bBASSサウンドである )
\r
901 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
902 bool b = BassMix.BASS_Mixer_ChannelPlay( this.hBassStream );
\r
905 Debug.WriteLine( "再生しようとしたが、Mixerに登録されていなかった: " + Path.GetFileName( this.strファイル名 ) + ", " + hBassStream );
\r
906 //Debug.WriteLine( "ErrCode= " +Bass.BASS_ErrorGetCode() );
\r
908 bool bb = tBASSサウンドをミキサーに追加する();
\r
911 Debug.WriteLine( "Mixerへの登録に失敗: " + Path.GetFileName( this.strファイル名 ) + ": " + Bass.BASS_ErrorGetCode() );
\r
915 Debug.WriteLine( "Mixerへの登録に成功: " + Path.GetFileName( this.strファイル名 ) + ": " + Bass.BASS_ErrorGetCode() );
\r
917 //this.t再生位置を先頭に戻す();
\r
919 bool bbb = BassMix.BASS_Mixer_ChannelPlay( this.hBassStream );
\r
922 Debug.WriteLine("更に再生に失敗 : " + Path.GetFileName(this.strファイル名));
\r
923 Debug.WriteLine("ErrCode= " + Bass.BASS_ErrorGetCode());
\r
927 Debug.WriteLine("再生成功(ミキサー追加後) : " + Path.GetFileName(this.strファイル名));
\r
932 Debug.WriteLine( "再生成功 : " + Path.GetFileName( this.strファイル名 ) + ", " + hBassStream );
\r
935 else if( this.bDirectSoundである )
\r
937 this.Buffer.Play( 0, PlayFlags.None );
\r
940 public void tサウンドを先頭から再生する()
\r
942 this.t再生位置を先頭に戻す();
\r
945 public void tサウンドを停止する()
\r
947 tサウンドを停止する( false );
\r
949 public void tサウンドを停止する( bool pause )
\r
951 if( this.bBASSサウンドである )
\r
953 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
954 BassMix.BASS_Mixer_ChannelPause( this.hBassStream );
\r
957 tBASSサウンドをミキサーから削除する(); // PAUSEと再生停止を区別できるようにすること!!
\r
960 else if( this.bDirectSoundである )
\r
962 this.Buffer.Stop();
\r
966 public void t再生位置を先頭に戻す()
\r
968 if( this.bBASSサウンドである )
\r
970 BassMix.BASS_Mixer_ChannelSetPosition( this.hBassStream, 0 );
\r
973 else if( this.bDirectSoundである )
\r
975 this.Buffer.CurrentPlayPosition = 0;
\r
978 public void t再生位置を変更する( long n位置ms )
\r
980 if( this.bBASSサウンドである )
\r
982 BassMix.BASS_Mixer_ChannelSetPosition( this.hBassStream, Bass.BASS_ChannelSeconds2Bytes( this.hBassStream, n位置ms / 1000.0 ), BASSMode.BASS_POS_BYTES );
\r
984 else if( this.bDirectSoundである )
\r
986 int n位置sample = (int) ( this.Buffer.Format.SamplesPerSecond * n位置ms * 0.001 );
\r
987 this.Buffer.CurrentPlayPosition = n位置sample * this.Buffer.Format.BlockAlignment;
\r
991 public static void tすべてのサウンドを初期状態に戻す()
\r
993 foreach( var sound in CSound.listインスタンス )
\r
996 public static void tすべてのサウンドを再構築する( ISoundDevice device )
\r
998 if( CSound.listインスタンス.Count == 0 )
\r
1002 // サウンドを再生する際にインスタンスリストも更新されるので、配列にコピーを取っておき、リストはクリアする。
\r
1004 var sounds = CSound.listインスタンス.ToArray();
\r
1005 CSound.listインスタンス.Clear();
\r
1008 // 配列に基づいて個々のサウンドを作成する。
\r
1010 for( int i = 0; i < sounds.Length; i++ )
\r
1012 switch( sounds[ i ].e作成方法 )
\r
1014 #region [ ファイルから ]
\r
1015 case E作成方法.ファイルから:
\r
1016 string strファイル名 = sounds[ i ].strファイル名;
\r
1017 sounds[ i ].Dispose( true, false );
\r
1018 device.tサウンドを作成する( strファイル名, ref sounds[ i ] );
\r
1021 #region [ WAVファイルイメージから ]
\r
1022 case E作成方法.WAVファイルイメージから:
\r
1023 if( sounds[ i ].bBASSサウンドである )
\r
1025 byte[] byArrWaveファイルイメージ = sounds[ i ].byArrWAVファイルイメージ;
\r
1026 sounds[ i ].Dispose( true, false );
\r
1027 device.tサウンドを作成する( byArrWaveファイルイメージ, ref sounds[ i ] );
\r
1029 else if( sounds[ i ].bDirectSoundである )
\r
1031 byte[] byArrWaveファイルイメージ = sounds[ i ].byArrWAVファイルイメージ;
\r
1032 var flags = sounds[ i ].DirectSoundBufferFlags;
\r
1033 sounds[ i ].Dispose( true, false );
\r
1034 ( (CSoundDeviceDirectSound) device ).tサウンドを作成する( byArrWaveファイルイメージ, flags, ref sounds[ i ] );
\r
1042 #region [ Dispose-Finalizeパターン実装 ]
\r
1043 //-----------------
\r
1044 public void Dispose()
\r
1046 this.Dispose( true, true );
\r
1047 GC.SuppressFinalize( this );
\r
1049 protected void Dispose( bool bManagedも解放する, bool bインスタンス削除 )
\r
1051 if( this.bBASSサウンドである )
\r
1053 #region [ ASIO, WASAPI の解放 ]
\r
1054 //-----------------
\r
1055 BassMix.BASS_Mixer_ChannelRemove( this.hBassStream );
\r
1056 Bass.BASS_StreamFree( this.hBassStream );
\r
1057 this.hBassStream = -1;
\r
1058 //-----------------
\r
1062 if( bManagedも解放する )
\r
1064 if( this.eデバイス種別 == ESoundDeviceType.DirectSound )
\r
1066 #region [ DirectSound の解放 ]
\r
1067 //-----------------
\r
1068 if( this.Buffer != null )
\r
1072 this.Buffer.Stop();
\r
1076 // 演奏終了後、長時間解放しないでいると、たまに AccessViolationException が発生することがある。
\r
1078 C共通.tDisposeする( ref this.Buffer );
\r
1080 //-----------------
\r
1084 if( this.e作成方法 == E作成方法.WAVファイルイメージから &&
\r
1085 this.eデバイス種別 != ESoundDeviceType.DirectSound ) // DirectSound は hGC 未使用。
\r
1088 this.hGC = default( GCHandle );
\r
1092 CSound.listインスタンス.Remove( this );
\r
1097 this.Dispose( false, true );
\r
1099 //-----------------
\r
1102 #region [ protected ]
\r
1103 //-----------------
\r
1104 protected enum E作成方法 { ファイルから, WAVファイルイメージから, Unknown }
\r
1105 protected E作成方法 e作成方法 = E作成方法.Unknown;
\r
1106 protected ESoundDeviceType eデバイス種別 = ESoundDeviceType.Unknown;
\r
1107 protected string strファイル名 = null;
\r
1108 protected byte[] byArrWAVファイルイメージ = null; // WAVファイルイメージ、もしくはchunkのDATA部のみ
\r
1109 protected GCHandle hGC;
\r
1110 protected int hBassStream = -1; // ASIO, WASAPI 用
\r
1111 protected SecondarySoundBuffer Buffer = null; // DirectSound 用
\r
1112 protected int hMixer = -1; // 設計壊してゴメン Mixerに後で登録するときに使う
\r
1113 //-----------------
\r
1116 #region [ private ]
\r
1117 //-----------------
\r
1118 private bool bDirectSoundである
\r
1120 get { return ( this.eデバイス種別 == ESoundDeviceType.DirectSound ); }
\r
1122 private bool bBASSサウンドである
\r
1127 this.eデバイス種別 == ESoundDeviceType.ASIO ||
\r
1128 this.eデバイス種別 == ESoundDeviceType.ExclusiveWASAPI ||
\r
1129 this.eデバイス種別 == ESoundDeviceType.SharedWASAPI );
\r
1132 private int _n位置 = 0;
\r
1133 private int _n位置db;
\r
1134 private int _n音量 = 100;
\r
1135 private int _n音量db;
\r
1136 private long nBytes = 0;
\r
1137 private int n一時停止回数 = 0;
\r
1138 private int nオリジナルの周波数 = 0;
\r
1139 private double _db周波数倍率 = 1.0;
\r
1140 private double _db再生速度 = 1.0;
\r
1142 private void tBASSサウンドを作成する( string strファイル名, int hMixer, BASSFlag flags )
\r
1144 if ( String.Compare( Path.GetExtension( strファイル名 ), ".xa", true ) == 0 ) // caselessで文字列比較
\r
1146 tBASSサウンドを作成するXA( strファイル名, hMixer, flags );
\r
1150 this.e作成方法 = E作成方法.ファイルから;
\r
1151 this.strファイル名 = strファイル名;
\r
1154 // BASSファイルストリームを作成。
\r
1156 this.hBassStream = Bass.BASS_StreamCreateFile( strファイル名, 0, 0, flags );
\r
1157 if( this.hBassStream == 0 )
\r
1158 throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_StreamCreateFile)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );
\r
1159 CSound管理.nStreams++;
\r
1161 // ミキサーにBASSファイルストリームを追加。
\r
1163 //if ( !BassMix.BASS_Mixer_StreamAddChannel( hMixer, this.hBassStream, BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_PAUSE | BASSFlag.BASS_MIXER_NORAMPIN ) )
\r
1164 //// if ( !tBASSサウンドをミキサーに追加する() )
\r
1167 // throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_Mixer_StreamAddChannel)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );
\r
1169 //CSound管理.nStreams++;
\r
1171 _cbEndofStream = new SYNCPROC( CallbackEndofStream );
\r
1172 Bass.BASS_ChannelSetSync( hBassStream, BASSSync.BASS_SYNC_END |BASSSync.BASS_SYNC_MIXTIME, 0, _cbEndofStream, IntPtr.Zero );
\r
1177 CSound.listインスタンス.Add( this );
\r
1179 // nBytesとn総演奏時間の取得; DTXMania用に追加。
\r
1180 nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );
\r
1181 double seconds = Bass.BASS_ChannelBytes2Seconds( this.hBassStream, nBytes );
\r
1182 this.n総演奏時間ms = (int) ( seconds * 1000 );
\r
1184 this.hMixer = hMixer;
\r
1185 float freq = 0.0f;
\r
1186 if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ref freq ) )
\r
1189 throw new Exception( string.Format( "サウンドストリームの周波数取得に失敗しました。(BASS_ChannelGetAttribute)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );
\r
1191 this.nオリジナルの周波数 = (int) freq;
\r
1193 private void tBASSサウンドを作成する( byte[] byArrWAVファイルイメージ, int hMixer, BASSFlag flags )
\r
1195 this.e作成方法 = E作成方法.WAVファイルイメージから;
\r
1196 this.byArrWAVファイルイメージ = byArrWAVファイルイメージ;
\r
1197 this.hGC = GCHandle.Alloc( byArrWAVファイルイメージ, GCHandleType.Pinned ); // byte[] をピン留め
\r
1200 // BASSファイルストリームを作成。
\r
1202 this.hBassStream = Bass.BASS_StreamCreateFile( hGC.AddrOfPinnedObject(), 0, byArrWAVファイルイメージ.Length, flags );
\r
1203 if( this.hBassStream == 0 )
\r
1204 throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_StreamCreateFile)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );
\r
1205 CSound管理.nStreams++;
\r
1208 // ミキサーにBASSファイルストリームを追加。
\r
1210 //if ( !BassMix.BASS_Mixer_StreamAddChannel( hMixer, this.hBassStream, BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_PAUSE | BASSFlag.BASS_MIXER_NORAMPIN ) )
\r
1211 //// if ( !tBASSサウンドをミキサーに追加する() )
\r
1214 // throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_Mixer_StreamAddChannel)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );
\r
1216 //CSound管理.nStreams++;
\r
1218 _cbEndofStream = new SYNCPROC( CallbackEndofStream );
\r
1219 Bass.BASS_ChannelSetSync( hBassStream, BASSSync.BASS_SYNC_END | BASSSync.BASS_SYNC_MIXTIME, 0, _cbEndofStream, IntPtr.Zero );
\r
1223 CSound.listインスタンス.Add( this );
\r
1225 // nBytesとn総演奏時間の取得; DTXMania用に追加。
\r
1226 nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );
\r
1227 double seconds = Bass.BASS_ChannelBytes2Seconds( this.hBassStream, nBytes );
\r
1228 this.n総演奏時間ms = (int) ( seconds * 1000 );
\r
1230 this.hMixer = hMixer;
\r
1231 float freq = 0.0f;
\r
1232 if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ref freq ) )
\r
1235 throw new Exception( string.Format( "サウンドストリームの周波数取得に失敗しました。(BASS_ChannelGetAttribute)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );
\r
1237 this.nオリジナルの周波数 = ( int ) freq;
\r
1239 private void tBASSサウンドを作成するXA( string strファイル名, int hMixer, BASSFlag flags )
\r
1241 int nPCMデータの先頭インデックス;
\r
1242 CWin32.WAVEFORMATEX wfx;
\r
1245 tオンメモリ方式でデコードする( strファイル名, out this.byArrWAVファイルイメージ,
\r
1246 out nPCMデータの先頭インデックス, out totalPCMSize, out wfx );
\r
1248 nBytes = totalPCMSize;
\r
1250 this.e作成方法 = E作成方法.WAVファイルイメージから;
\r
1251 this.strファイル名 = strファイル名;
\r
1252 this.hGC = GCHandle.Alloc( this.byArrWAVファイルイメージ, GCHandleType.Pinned ); // byte[] をピン留め
\r
1255 _cbStreamXA = new STREAMPROC( CallbackPlayingXA );
\r
1257 // BASSファイルストリームを作成。
\r
1259 //this.hBassStream = Bass.BASS_StreamCreate( xa.xaheader.nSamplesPerSec, xa.xaheader.nChannels, BASSFlag.BASS_STREAM_DECODE, _myStreamCreate, IntPtr.Zero );
\r
1260 this.hBassStream = Bass.BASS_StreamCreate( (int)wfx.nSamplesPerSec, (int)wfx.nChannels, BASSFlag.BASS_STREAM_DECODE, _cbStreamXA, IntPtr.Zero );
\r
1261 if ( this.hBassStream == 0 )
\r
1264 throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_SampleCreate)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );
\r
1266 CSound管理.nStreams++;
\r
1268 // ミキサーにBASSファイルストリームを追加。
\r
1270 //if ( !BassMix.BASS_Mixer_StreamAddChannel( hMixer, this.hBassStream, BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_PAUSE | BASSFlag.BASS_MIXER_NORAMPIN ) )
\r
1271 //// if ( !tBASSサウンドをミキサーに追加する() )
\r
1274 // throw new Exception( string.Format( "サウンドストリームの生成に失敗しました。(BASS_Mixer_StreamAddChannel)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );
\r
1276 //CSound管理.nStreams++;
\r
1278 _cbEndofStream = new SYNCPROC( CallbackEndofStream );
\r
1279 Bass.BASS_ChannelSetSync( hBassStream, BASSSync.BASS_SYNC_END | BASSSync.BASS_SYNC_MIXTIME, 0, _cbEndofStream, IntPtr.Zero );
\r
1283 CSound.listインスタンス.Add( this );
\r
1285 // nBytesとn総演奏時間の取得; DTXMania用に追加。
\r
1286 //nBytes = (int) ( xa.xaheader.nSamples * xa.xaheader.nChannels * 2 ); // nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );
\r
1287 //nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );
\r
1288 //if ( nBytes < 0 )
\r
1291 // BASSError err = Bass.BASS_ErrorGetCode();
\r
1292 // throw new Exception( "サウンドストリームの生成に失敗しました。(BASS_ChannelGetLength: " + err + ")" );
\r
1294 //nBytes = (int) this.byArrWAVファイルイメージ.Length;
\r
1296 double seconds = Bass.BASS_ChannelBytes2Seconds( this.hBassStream, nBytes );
\r
1297 this.n総演奏時間ms = (int) ( seconds * 1000 );
\r
1299 this.hMixer = hMixer;
\r
1300 float freq = 0.0f;
\r
1301 if ( !Bass.BASS_ChannelGetAttribute( this.hBassStream, BASSAttribute.BASS_ATTRIB_FREQ, ref freq ) )
\r
1304 throw new Exception( string.Format( "サウンドストリームの周波数取得に失敗しました。(BASS_ChannelGetAttribute)[{0}]", Bass.BASS_ErrorGetCode().ToString() ) );
\r
1306 this.nオリジナルの周波数 = ( int ) freq;
\r
1308 //-----------------
\r
1310 private int pos = 0;
\r
1311 private int CallbackPlayingXA( int handle, IntPtr buffer, int length, IntPtr user )
\r
1313 int bytesread = ( pos + length > Convert.ToInt32(nBytes) ) ? Convert.ToInt32(nBytes) - pos : length;
\r
1315 Marshal.Copy( byArrWAVファイルイメージ, pos, buffer, bytesread );
\r
1318 if ( pos >= nBytes )
\r
1320 // set indicator flag
\r
1321 bytesread |= (int) BASSStreamProc.BASS_STREAMPROC_END;
\r
1330 /// ストリームの終端まで再生したときに呼び出されるコールバック
\r
1332 /// <param name="handle"></param>
\r
1333 /// <param name="channel"></param>
\r
1334 /// <param name="data"></param>
\r
1335 /// <param name="user"></param>
\r
1336 private void CallbackEndofStream( int handle, int channel, int data, IntPtr user )
\r
1338 Debug.WriteLine( "Callback!(remove 3sec later)" );
\r
1339 ThreadPool.QueueUserWorkItem( RemoveMixerChannelLater, channel);
\r
1340 //tBASSサウンドをミキサーから削除する( channel );
\r
1342 private void RemoveMixerChannelLater( object o )
\r
1344 int channel = (int) o;
\r
1345 Thread.Sleep( 3000 );
\r
1346 tBASSサウンドをミキサーから削除する( channel );
\r
1348 public bool tBASSサウンドをミキサーから削除する()
\r
1350 return tBASSサウンドをミキサーから削除する( this.hBassStream );
\r
1352 public bool tBASSサウンドをミキサーから削除する( int channel )
\r
1354 bool b = BassMix.BASS_Mixer_ChannelRemove( channel );
\r
1357 CSound管理.nMixing--;
\r
1358 Debug.WriteLine( "Removed: " + channel );
\r
1366 public bool tBASSサウンドをミキサーに追加する()
\r
1368 if ( BassMix.BASS_Mixer_ChannelGetMixer( hBassStream ) == 0 )
\r
1370 BASSFlag bf = BASSFlag.BASS_SPEAKER_FRONT | BASSFlag.BASS_MIXER_NORAMPIN | BASSFlag.BASS_MIXER_PAUSE;
\r
1371 CSound管理.nMixing++;
\r
1373 bool b = BassMix.BASS_Mixer_StreamAddChannel( this.hMixer, this.hBassStream, bf );
\r
1374 t再生位置を先頭に戻す(); // StreamAddChannelの後で再生位置を戻さないとダメ。逆だと再生位置が変わらない。
\r
1375 Debug.WriteLine( "Add Mixer: " + hBassStream );
\r
1382 public void tオンメモリ方式でデコードする( string strファイル名, out byte[] buffer,
\r
1383 out int nPCMデータの先頭インデックス, out int totalPCMSize, out CWin32.WAVEFORMATEX wfx )
\r
1385 nPCMデータの先頭インデックス = 0;
\r
1386 //int nPCMサイズbyte = (int) ( xa.xaheader.nSamples * xa.xaheader.nChannels * 2 ); // nBytes = Bass.BASS_ChannelGetLength( this.hBassStream );
\r
1388 SoundDecoder sounddecoder;
\r
1390 if ( String.Compare( Path.GetExtension( strファイル名 ), ".xa", true ) == 0 )
\r
1392 sounddecoder = new Cxa();
\r
1394 else if ( String.Compare( Path.GetExtension( strファイル名 ), ".ogg", true ) == 0 )
\r
1396 sounddecoder = new Cogg();
\r
1400 throw new NotImplementedException();
\r
1403 int nHandle = sounddecoder.Open( strファイル名 );
\r
1404 if ( nHandle < 0 )
\r
1406 throw new Exception( string.Format( "Open() に失敗しました。({0})({1})", nHandle, strファイル名 ) );
\r
1408 wfx = new CWin32.WAVEFORMATEX();
\r
1409 if ( sounddecoder.GetFormat( nHandle, ref wfx ) < 0 )
\r
1411 sounddecoder.Close( nHandle );
\r
1412 throw new Exception( string.Format( "GetFormat() に失敗しました。({0})", strファイル名 ) );
\r
1414 //totalPCMSize = (int) sounddecoder.nTotalPCMSize; // tデコード後のサイズを調べる()で既に取得済みの値を流用する。ms単位の高速化だが、チップ音がたくさんあると塵積で結構効果がある
\r
1415 totalPCMSize = (int) sounddecoder.GetTotalPCMSize( nHandle );
\r
1416 if ( totalPCMSize == 0 )
\r
1418 sounddecoder.Close( nHandle );
\r
1419 throw new Exception( string.Format( "GetTotalPCMSize() に失敗しました。({0})", strファイル名 ) );
\r
1421 totalPCMSize += ( ( totalPCMSize % 2 ) != 0 ) ? 1 : 0;
\r
1422 buffer = new byte[ totalPCMSize ];
\r
1423 GCHandle handle = GCHandle.Alloc( buffer, GCHandleType.Pinned );
\r
1426 if ( sounddecoder.Decode( nHandle, handle.AddrOfPinnedObject(), (uint) totalPCMSize, 0 ) < 0 )
\r
1429 throw new Exception( string.Format( "デコードに失敗しました。({0})", strファイル名 ) );
\r
1435 sounddecoder.Close( nHandle );
\r
1437 sounddecoder = null;
\r